ASP.NET MVC’s action filters allow you to execute some code before/after an action in a declarative manner, manipulating what goes into or comes out of the action. In essence, they are what’s called aspects in Aspect Oriented Programming; a way of encapsulating crosscutting concerns into reusable components. For example, instead of this:
public ActionResult SomeActionWhichRequiresAuthentication()
{
if (!User.Identity.IsAuthenticated)
{
return RedirectToAction("Login", "Authentication");
}
else
{
// ...
}
}
You’ll do this:
[Authorize]
public ActionResult SomeActionWhichRequiresAuthentication()
{
// ...
}
However, a problem with ASP.NET MVC’s action filters is that there’s no support for having an IOC container manage them, which means that if I need to do something more complicated that requires calling into a domain service/repository for example, I cannot get these injected easily. Jeremy Skinner has a solution which enables property injection, but what I really want is constructor injection.
Solution: The Proxy Pattern
What I came up with, was to build a filter proxy. Using it looks like this:
[Filter(typeof(AuthorizationFilter))]
public ActionResult SomeActionWhichRequiresAuthentication()
{
// ...
}
Internally, the FilterAttribute resolves the actual filter using the IOC container and forwards all its calls to it:
public class FilterAttribute : ActionFilterAttribute
{
private readonly Type _actionFilterType;
private IActionFilter _action;
public FilterAttribute(Type actionFilterType)
{
_actionFilterType = actionFilterType;
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
// resolve the action filter using the IOC container
var container = (ICanResolveDependencies) HttpContext.Current.ApplicationInstance;
_action = (IActionFilter)container.Resolve(_actionFilterType);
_action.OnActionExecuting(filterContext);
base.OnActionExecuting(filterContext);
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
_action.OnActionExecuted(filterContext);
base.OnActionExecuted(filterContext);
}
}
This allows the IOC container to manage the instantiation and lifetime of the action filter. Note the assumption that the applications Application object (Global.asax) implements the ICanResolveDependencies interface, which essentially is an abstraction of the IOC container.