Sessions in ASP.NET Core

ASP.NET Core is being designed so that your application is only dependent on features that it actually needs. This is achieved in large part by creating a composable framework, where the developer opts in to non-essential features - a number of which are baked in to traditional versions of ASP.NET. One of the features that this applies to is Session State. This article looks at how to obtain and use session state in ASP.NET Core applications.

If you are new to ASP.NET, session state is a mechanism that enables you to store and retrieve user specific values temporarily. These values can be stored for the duration of the visitor's session on your site. In most cases, they are stored in server memory, although options exist for using persistent and/or distributed storage mechanisms if, for example, you are using multiple web servers for your application (web farm etc). This article is only interested in the in-memory option. The type of data you will use session state for is anything that relates to the current user. It might be their name, or a discount level they are entitled to, or anything that you don't want to repeatedly query a database for.

Session management in ASP.NET Core is delivered via a pluggable component, or "middleware" and is available in a Nuget package called Microsoft.AspNet.Core.Session. When you use session management, you also need a persistence mechanism for session variables. In-memory storage is also available as an optional package called Microsoft.Extensions.Caching.Memory. To make them available to your application, add the following entries to the dependencies node of your project.json file:

"Microsoft.AspNetCore.Session": "1.0.0l",
"Microsoft.Extensions.Caching.Memory": "1.0.0"

You should see "Restoring..." appear next to the References node in Solution Explorer and the session package being added.

ASP.NET  Core  Session

Now that the packages are available to your application, you can opt in to using them. You do this in the ConfigureServices method of the Startup class (Startup.cs file).

public void ConfigureServices(IServiceCollection services)
{
    // Add framework services.
    services.AddMvc();
    services.AddCaching();
    services.AddSession(options => { 
            options.IdleTimeout = TimeSpan.FromMinutes(30); 
            options.CookieName = ".MyApplication";
        });
}

The highlighted lines show the code you need to add to the ConfigureServices method to register both caching and session with the services used in your application. The method takes an Action<SessionOptions> delegate that enables you to change the default options such as the location for session cookies, or the default timeout period (which is 20 minutes as in previous versions). The sample code above sets the timeout value to 30 minutes. This means that if a user is idle for more then 30 minutes, the session expires and its contents are removed from memory (if that is the backing store you choose to use). It also changes the name of the cookie used to manage sessions, which by default is .AspNet.Session.

At this point, you have included the required packages for session management within your solution and then registered them as services. Now you need to tell the application to use the session management features. You do this in the Configure method of the StartUp.cs file, which is where you register all middleware, but make sure you add session to the pipeline before adding MVC. :

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    app.UseSession();
    //removed for brevity
}

You can start setting and getting session values after you have referenced Microsoft.AspNetCore.Http in your controller:

using Microsoft.AspNetCore.Http;

There are three methods that enable you to set session values: SetInt32, SetString and Set, which takes a byte array as an argument. This is very different to the traditional session API, which allows you to set a session value by assigning any type to a session key. The new session framework stores items as byte arrays, and internally, SetInt and SetString converts the ints and strings that you pass to a byte array. The main reason behind this decision appears to be to ensure that session values are serialisable for storage on remote servers. The only way to store other types of values is to implement the serialisation to byte arrays yourself. I look at that shortly, but in the meantime, here's how you would use the SetInt32 and SetString methods from within your controller to create and set some session variables:

public IActionResult Index()
{
    HttpContext.Session.SetString("Name", "Mike");
    HttpContext.Session.SetInt32("Age", 21);
    return View();
}

And here's how you might retrieve those values to be passed to a View:

public IActionResult About()
{
    ViewBag.Name = HttpContext.Session.GetString("Name");
    ViewBag.Age = HttpContext.Session.GetInt32("Age");
    return View();
}

There is also a Get method that returns a byte array. As I mentioned earlier, if you want to set other types as session variables, you need to take care of serialisation yourself. You could do this at the point of setting values, but a more reusable approach can be achieved by creating your own extension methods on ISession. The following example shows how you might implement GetBoolean and SetBoolean methods:

public static class SessionExtensions
{
    public static bool? GetBoolean(this ISession session, string key)
    {
        var data = session.Get(key);
        if (data == null)
        {
            return null;
        }
        return BitConverter.ToBoolean(data, 0);
    } 

    public static void SetBoolean(this ISession session, string key, bool value)
    {
        session.Set(key, BitConverter.GetBytes(value));
    }
}

Now you can use these methods too:

public IActionResult Index()
{
    Context.Session.SetString("Name", "Mike");
    Context.Session.SetInt32("Age", 21);
    Context.Session.SetBoolean("Fibber", true);
    return View();
}
public IActionResult About()
{
    ViewBag.Name = Context.Session.GetString("Name");
    ViewBag.Age = Context.Session.GetInt32("Age");
    ViewBag.Liar = Context.Session.GetBoolean("Fibber");
    return View();
}

There are two other methods of interest. One is the Remove method which allows you to delete individual values from the session collection by key:

Context.Session.Remove("Name");

The other is the Clear method. This removes all keys and values associated with the session. There is no obvious counterpart to the pre-ASP.NET Core Abandon method which ends the session.

Session Events

Classic ASP.NET includes a couple of session-related events: Session_Start and Session_End, which you can access via global.asax in order to execute code. In ASP.NET Core 1.0 , you can query the session collection using middleware to establish if a session has already been established to replicate the Session_Start event, but there are no plans to introduce an equivalent to Session_End. Since one of the driving forces behind ASP.NET Core is "cloud-readiness", the focus on session management design has been to make it work in a distributed scenario. Session_End only ever fired when sessions used inproc mode (local server memory) and the team have stated that they won't add features that only work locally.

Webucator, a specialist provider of online ASP.NET training have produced a video version of this article:

Summary

The purpose of this article is to introduce the fact that session state is an opt-in component in ASP.NET Core 1.0 and features some differences in method names and usage when compared with traditional ASP.NET. The example here used memory as a backing store for session values, although the limitations of this approach are still the same as in previous versions of ASP.NET. Memory is volatile, and cannot be relied upon. It can be cleared as a result of a number of causes outside of the developer's control. In future articles, I will look at alternative persistence stores.