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.
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.
'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.
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.
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.