Working with Session in ASP.NET MVC Core (or: Why You Can't Migrate)
[Editor's note: Peter rewrote this article after a reader pointed out he over-engineered his original solution. ("What can I say: The code worked -- I just didn't need nearly as much code as I thought I did to get there.") Which explains the reader comments at the end.]
The first thing to know about working with the Session object in ASP.NET MVC Core is that it's different (and it doesn't matter if you've worked in ASP, ASP.NET Web Forms, or ASP.NET MVC: In ASP.NET MVC Core, Session is different). Different enough that it makes Microsoft's point that you can't reasonably expect to migrate your ASP.NET MVC application to ASP.NET MVC Core. I'm not going to argue with that ... but if you do need to migrate an application, I have some ideas on how to ease the pain.
In ASP.NET MVC (just "MVC' from here on in), configuration is handled through a combination of the web.config file, the Global.asax file, and the classes in the files in the Startup folder. In ASP.NET MVC Core (just "MVC Core' from here), all configuration is done in just one place: the Startup class in the project's Startup.cs file.
That's good because there's a lot of configuration to do: You have to pick both which services you want to use and what middleware you want in the ASP.NET processing pipeline. Of course, if you're an MVC developer coming to MVC Core, you want all the processing you're used to from MVC. Fortunately, that's easy to do:
In the Startup class's ConfigureServices method, call the AddMvc method on the IServiceCollection object passed to the method (AddMvc makes a default set of services available to your application).
In the Configure method, call the UseMvc method on the IApplicationBuilder object passed to the method (that creates a default pipeline for processing requests and responses).
Sadly, neither of those will add support for using Session. To add Session support, you're going to add this code to the ConfigureServices method before your call to the AddMvc method:
so.IdleTimeout = TimeSpan.FromSeconds(60);
Without this code, when you first try to use Session, you'll get a message about "InvalidOperationException: Unable to resolve service for type 'Microsoft.AspNetCore.Session.ISessionStore'."
As you can see, the AddSession method accepts a lambda expression that is, in turn, passed a SessionOptions object (I called the object "so" in my sample code). You use the SessionOptions object to set the options you would have set in the sessionState element in an MVC project's web.config file (by the way: passing an options object to a lambda expression is a pattern that you'll see crop up in several of the configuration methods in MVC Core).
You're now ready to use the Session object ... but it won't be where you're looking for it in your Controller classes: The base Controller class no longer has a Session property.
The Controller class does have an HttpContext property that returns the HttpContext object and it does have a Session property. You can use the Session object in that property.
Reading and Writing Session
Sadly, however, when it comes time to set or retrieve Session values, the wheels really fall off: The Session object no longer has an indexer. Instead, you must use the methods SetString, GetString, SetInt and GetInt to change or retrieve values. One note: Many of these are extension methods so, if at first you don't see them in your IntelliSense list, you may just need to wait a few seconds for them to appear.
This means that, in MVC Core, the code to add the string "x001" under the key "TransId" looks like this:
public ActionResult Index()
Notably absent from the methods available is a SetObject or GetObject method (though there are methods that return byte arrays, as if I cared).
If you want to save or restore objects, Microsoft's recommendation is to convert them to JSON strings using the NewtonSoft utilities and then use SetString and GetString. Microsoft has even provided sample extension methods for Session that do just that.
As you can see, it's not possible, using a global search-and-replace, to migrate MVC Session code that looks like this:
Session["Trans"] = transact;
to this code:
But, if you're willing to write some code, you can enable a "global search-and-replace migration." The first step is to create an extension method like the following that will attach itself to the Session object:
public static SessionIndexer AddIndexer(this ISession session)
return new SessionIndexer(session);
That SessionIndexer class that's returned from this method provides an indexer that stores values in the Session object passed to the class:
public class SessionIndexer
private ISession Session;
public SessionIndexer(ISession Session)
this.Session = Session;
public object this[string key]
With these utilities in place, existing code that looks like this:
Session["Trans"] = transact;
can be migrated with a global search-and-replace that finds "Session[" and substitutes "Session.SetIndexer()[":
Session.AddIndexer()["Trans"] = "transact";
Don't get me wrong: You still don't want to migrate existing MVC applications. Really, you don't. And don't ask me how I know this.
About the Author
Peter Vogel is a system architect and principal in PH&V Information Services. PH&V provides full-stack consulting from UX design through object modeling to database design. Peter tweets about his VSM columns with the hashtag #vogelarticles. His blog posts on user experience design can be found at http://blog.learningtree.com/tag/ui/.