Skip to content

Instantly share code, notes, and snippets.

@FransBouma
Created June 10, 2020 10:01
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save FransBouma/1378210503a13e4efeb872df1f630066 to your computer and use it in GitHub Desktop.
Save FransBouma/1378210503a13e4efeb872df1f630066 to your computer and use it in GitHub Desktop.
ASP.NET MVC global.asax Session_Start migration to ASP.NET Core 3 middleware

ASP.NET MVC Session_Start migration to ASP.NET Core 3 middleware

When you migrate your site from ASP.NET MVC on netfx to ASP.NET Core on .net core you might run into the problem what to do with the Session_Start method in global.asax: what's the equivalent in ASP.NET Core ?

Searching on the internet will likely make you end up on forum posts / stackoverflow posts telling you to 'write some middleware', but what does that mean? How to do that? If you feel as lost as I was, read on as this post is for you.

The request pipeline

In ASP.NET Core there are no events that are called at a given moment: there's a request pipeline and to get code executed when a request comes in, it has to be part of that pipeline. In Startup.Configure, the request pipeline is configured. This is an essential concept that I was overlooking and if you're still reading, you might have as well: it defines the steps that are executed at each request. So all calls there, define middelware elements on the request handler pipeline, in the order in which they're called.

To have Session_Start behavior in ASP.NET Core, we therefore have to make sure what we have to write is part of that request pipeline.

The Session_Start middleware

'Middleware' sounds really heavy but really, it can be just a lambda. Session_Start is called at the start of a user session, so at the first request of a user: we want to initialize the session object with data that'll be needed for the user's subsequential requests. To have equivalent behavior in ASP.NET Core, we therefore would need 'middleware' that is part of the request pipeline and checks if the session has been initialized. If so, continue as normal, if not, do the initialization part.

Middleware that's part of the ASP.NET Core request pipeline will be executed on every request, so we have to make sure the code does the initialization step only once.

Add a Use call to the Startup.Configure method

We start by adding a lambda using the Use() extension method to the Startup.Configure method:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
	//... 
	app.UseHttpsRedirection();
	app.UseStaticFiles();
	app.UseRouting();
	app.UseAuthentication();
	app.UseAuthorization();
	app.UseSession();
	// own middleware function to initialize session if required. 
	app.Use(async (context, next) =>
			{
				InitializeSessionIfRequired(context);

				// call next in pipeline
				await next();
			});
	
	// ...
	// last element added to the chain.
	app.UseEndpoints(endpoints => RegisterRoutes(endpoints));
}

The //... parts contain other method calls if you want, this is basically the setup. The Use() call above configures a step in the request pipeline that's executed on every request. It will call InitializeSessionIfRequired and when that method completes it will carry on with the next middleware in the pipeline by calling next().

The InitializeSessionIfRequired method looks like this:

private void InitializeSessionIfRequired(HttpContext context)
{
	if(context == null || context.Session==null)
	{
		return;
	}

	if(context.Session.GetInt32(SessionKeys.SessionInitialized)==1)
	{
		// initialized
		return;
	}

	context.Session.Initialize(context);
}

The SessionKeys.SessionInitialized is a constant string, e.g. "Session:Initialized", so you can be sure you use the same key string everywhere. Here it checks a simple int to be present, if it is present, we apparently have already initialized the session object and we can continue with the next middleware element in the pipeline. If not, we're here the first time (or the session object has been removed/cleared), so we have to re-initialize it.

Here I do that in an extension method on ISession (you can also call a method and pass context, it's up to you):

public static void Initialize(this ISession session, HttpContext context)
{
	if(session.GetInt32(SessionKeys.SessionInitialized) == 1)
	{
		// already initialized
		return;
	}

	// place your global.asax' Session_Start() code here
	// Any usage of HttpContext has to be refactored to
	// using the 'context' object passed in.
	
	// mark the session as initialized.
	session.SetInt32(SessionKeys.SessionInitialized, 1);
}

And that's it.

@mjanulaitis
Copy link

So I implemented this years ago. Now I am trying to add an order item to my session from an email. The link works if I am already in my browser and paste it in the URL bar, however when called directly from the email client, the session is wiped out between adding the order item and re-directing to my cart. I can step through my InitializeSessionIfRequired in my middleware and see it get re-created between calls. I am at a total loss on this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment