The event model in the System.Web.HttpApplication
is part of ASP.NET, not MVC. It was not designed for use with dependency injection.
The answer that Cyril suggested is using a service locator to get a reference to the service. This is far from ideal, since you are taking on a dependency to the service locator in your code.
The MVC-centric way of implementing cross cutting concerns (such as loading user data into session state) is to use globally registered filters. You can either implement IAuthorizationFilter
or IActionFilter
to get the desired effect. In this case it makes sense to use IActionFilter
since you want to wait until you are sure there is an authorized user before it is called.
NOTE: While this answers your specific question, it is best not to use session state for this scenario in MVC. An alternative is to use ASP.NET Identity with Claims to store user profile data instead of using Session.
using System;
using System.Web.Mvc;
using System.Security.Principal;
public class GetUserActionFilter : IActionFilter
{
private readonly IUserRepository userRepository;
public GetUserActionFilter(IUserRepository userRepository)
{
if (userRepository == null)
throw new ArgumentNullException("userRepository");
this.userRepository = userRepository;
}
public void OnActionExecuted(ActionExecutedContext filterContext)
{
// Do nothing - this occurs after the action method has run
}
public void OnActionExecuting(ActionExecutingContext filterContext)
{
IPrincipal user = filterContext.HttpContext.User;
if (user == null)
{
return;
}
IIdentity identity = user.Identity;
if (identity == null)
{
return;
}
// Make sure we have a valid identity and it is logged in.
if (identity.IsAuthenticated)
{
string key = "__CurrentUserData";
var userData = filterContext.HttpContext.Session[key];
if (userData == null)
{
// User data doesn't exist in session, so load it
userData = userRepository.GetUserData(identity.Name);
// Add it to session state
filterContext.HttpContext.Session[key] = userData;
}
}
}
}
Now, to add your filter globally, you need to:
- Register the filter and its dependencies with Autofac.
- Pass the container to the static
RegisterGlobalFilters
method.
Register the Filter
Using a named instance to differentiate it from other potential IActionFilter
instances.
builder.RegisterType<GetUserActionFilter>()
.Named<IActionFilter>("getUserActionFilter");
Pass the Container
FilterConfig.cs
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters, IContainer container)
{
filters.Add(container.ResolveNamed<IActionFilter>("getUserActionFilter"));
filters.Add(new HandleErrorAttribute());
}
}
Global.asax.cs
public class MvcApplication : System.Web.HttpApplication
{
// This method serves as the composition root
// for the project.
protected void Application_Start()
{
// Register Autofac DI
IContainer container = ContainerConfig.RegisterContainer();
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters, container);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
AuthConfig.RegisterAuth();
}
}
ContainerConfig.cs
public class ContainerConfig
{
public static IContainer RegisterContainer()
{
//Create a new ContainerBuilder
var builder = new ContainerBuilder();
// Register all the controllers using the assembly object
builder.RegisterControllers(Assembly.GetExecutingAssembly());
//Registering default convention -- IExample and Example
builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly())
.Where(t => t.Name.Single(i => i.Name == "I" + t.Name))
.AsImplementedInterfaces();
// Register our filter
builder.RegisterType<GetUserActionFilter>()
.Named<IActionFilter>("getUserActionFilter");
//Build the container
var container = builder.Build();
//Set the default resolver to use Autofac
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
// Return the container to our composition root.
return container;
}
}
Note that I just used a repository service here, since HttpContext
is available already through the action filter directly and additional logic is needed here because we don't know for sure if it exists in session state or not or whether there is even a user to lookup, so our filter does those checks in addition to loading session state.