Sessions in ASP.NET MVC using Dependency Injection
Posted on Friday, 13th June 2014
A common approach I see whilst browsing tutorials or StackOverflow questions relating to reading and writing to sessions in ASP.NET MVC is the following:
[Serializable]
public class UserProfileSessionData
{
public int UserId { get; set; }
public string EmailAddress { get; set; }
public string FullName { get; set; }
}
public class ExampleLoginController : Controller {
[HttpPost]
public ActionResult Login(LoginModel model)
{
if (ModelState.IsValid)
{
var profileData = new UserProfileSessionData {
UserId = model.UserId,
EmailAddress = model.EmailAddress,
FullName = model.FullName
}
this.Session["UserProfile"] = profileData;
}
}
}
And then retrieving session data like this:
public class ExampleDetailController : Controller
{ ...
public ActionResult ViewProfile()
{
...
var sessionData = (UserProfileSessionData) this.Session["UserProfile"];
...
}
...
}
What’s the problem with doing it this way?
Whilst this solution works, there’s improvements that can be made. Take a look at the code for a moment and try and think of what problems doing the above could lead to as the project started to mature.
First of all, what happens if we want to modify the what information gets stored in the session? Or even worse, we want to switch from session based storage to database storage? Ultimately we’d have to spend a great deal of time going through any controller that references the session and make changes.
Ultimately, what we’re doing by accessing the Session object directly from within the controller is breaking the D (dependency inversion) of the SOLID principles - a set of five principles of object-orientated programming and design aimed at encouraging the creation of extensible, flexible and testable software; something all developers should strive to do.
What is the Dependency Inversion principle?
Once of the most memorable definitions of dependency inversion that I’ve heard and which has stuck with me is:
“Program to an interface, not an implementation”
In the context of the code sample above, by directly programming against the Session object (an implementation) we’re creating a dependency within our controller. We can avoid doing this by using an interface type to abstract and encapsulate the session’s implementation details - allowing us to programming against some kind of simplified interface. Doing this leads to a loosely coupled controller that’s not directly depending on the existence of the Session object, but instead depends a type that implements our IUserInformation type.
This powerful thing about this is the controller does not need to be concerned about the implementation details, meaning we could easily switch the implementation to read the data from a database instead of a session object - and we wouldn’t have to touch anything in the controller.
This also makes the controller far more testable as we can easily mock the session’s abstraction.
Ok, I’m following so far. So how do we ‘invert’ our controller’s dependencies?
There are multiple types of dependency injection but the most common pattern for this type of challenge is constructor injection using an Inversion of Control Container.
In this example I’m going to use StructureMap as my IoC container of choice as it’s one I’m most comfortable with and prefer using in my day-to-day life.
The first step is to include the Nuget package for both StructureMap and StructureMap.Web. Now we’ve got the StructureMap packages we need to hook Structuremap into ASP.NET’ MVC’s dependency resolver by creating a new class (In this instance I called it StructureMapDependencyResolver.cs) and implementing the IDependencyResolver from the System.Web.Mvc package like so:
public class StructureMapDependencyResolver : IDependencyResolver
{
private readonly IContainer _container;
public StructureMapDependencyResolver(IContainer container)
{
_container = container;
}
public object GetService(Type serviceType)
{
if (serviceType.IsAbstract || serviceType.IsInterface)
{
return _container.TryGetInstance(serviceType);
}
return _container.GetInstance(serviceType);
}
public IEnumerable<object> GetServices(Type serviceType)
{
return _container.GetAllInstances<object>().Where(s => s.GetType() == serviceType);
}
}
Once this is done simply add our DependencyResolver to the Application_Start method within Global.asax.cs, like so:
public class MvcApplication : HttpApplication
{
protected void Application_Start()
{
...
ObjectFactory.Initialize(x => x.AddRegistry<WebsiteRegistry>());
DependencyResolver.SetResolver(new StructureMapDependencyResolver(ObjectFactory.Container));
...
}
}
Now that StructureMap is hooked up to our ASP.NET MVC application, all that’s left to do is define our dependencies by creating our WebsiteRegistry.cs class that extends StructureMap.Configuration.DSL.Registry.cs, like so:
public class WebsiteRegistry : Registry
{
public WebsiteRegistry()
{
this.For<IUserInformation>().HybridHttpOrThreadLocalScoped().Use(() => GetUserInformationFromSession());
}
public static IUserInformation GetUserInformationFromSession()
{
HttpSessionStateBase session = new HttpSessionStateWrapper(HttpContext.Current.Session);
// Check to see if the session already exists, if it does then cast it to IUserInformation
if (session["UserSessionData"] != null)
{
return session["UserSessionData"] as IUserInformation;
}
// Otherwise create a new instance of UserInformation.cs
session["UserSessionData"] = new UserInformation();
return session["UserSessionData"] as IUserInformation;
}
}
Once we’ve added our Registry we need to define how our instance of IUserInformation is constructed. In this case we want to deserialize our session object and cast it to an instance of IUserInformation.
We can now refactor our ExampleLoginController.cs file to move the dependencies outside of the controller and program against the IUserInformation interface.
public class ExampleDetailController : Controller {
private readonly IUserInformation userInformation;
public void ExampleLoginController(IUserInformation userInformation) {
this.userInformation = userInformation;
}
[HttpPost]
public ActionResult ViewProfile()
{
...
string emailAddress = this.userInformation.EmailAddress;
...
}
}
Enjoy this post? Don't be a stranger!
Follow me on Twitter at @_josephwoodward and say Hi! I love to learn in the open, meet others in the community and talk Go, software engineering and distributed systems related topics.