Today a co-worker made me a nice question regarding ASP.NET MVC3, sounds like a simple question with a simple answer, but I think it worth the blog post.
How do I inject a concern into my custom authorization attribute (or any other filter attribute) in the ASP.NET MVC3 framework?
The case is pretty simple, let’s say you have the following service and of course you are using a container to inject your dependencies
public interface ILogger {
void Log(string message);
}
Well, let’s say that we need to use that service inside our custom attribute for logging:
public class LogAttribute : FilterAttributeProvider {
public override void OnActionExecuting(ActionExecutingContext context) {
// I need to use the ILogger service here!
}
}
Well, we have two options here, the proper and the hack… let’s see the hack option first!
For the hack option we just need to use the Service Locator included with the ASP.NET MVC3 framework:
public class LogAttribute : FilterAttribute {
public override void OnActionExecuting(ActionExecutingContext context) {
var log = DependencyResolver.Current.GetInstance<ILogger>();
// do whatever you want to do here
}
}
This probably would be one of the only two places you should use a Service Locator, and of course I don’t like it… Thanks to the guys in the ASP.NET MVC framework we have a ‘proper-like’ way to do it, using a filter provider:
The idea is simple, we have an specific special class to provide the attributes that we need to do the job, in ASP.NET MVC3 that class is an implementation of FilterAttributeFilterProvider, most container integrations already has support for it and bundle your filter provider, in this example we would use Autofac (because was the container used in that particular project).
First we need to register the needed service and the provider in your registration code (generally in your Global.asax.cs file), maybe something like this:
var builder = new ContainerBuilder();
builder.RegisterControllers(Assembly.GetExecutingAssembly());
builder.RegisterType<Logger>().As<ILogger>().InstancePerHttpRequest();
builder.RegisterFilterProvider();
DependencyResolver.SetResolver(new AutofacDependencyResolver(builder.Build()));
To make this work remember to add a reference to Autofac’s MVC3 integration assembly.
Now just create a property dependency in your FilterAttribute
public class LogAttribute : FilterAttribute {
public ILogger Log { get; set; }
public override void OnActionExecuting(ActionExecutingContext context) {
// Now we can use Log here!
}
}
And well, that’s all… just matter of apply the attribute to your actions/controllers
Personally I don’t like neither of the approaches, I think attributes should be only markers, not behaviour concerns. A good implementation would be splitting the attribute in two, a marker interface and a behaviour class that would act if the attribute is present. With a little of hack this could be possible (would involve creating your own FilterProvider class that would get the present attributes and apply the correct filter attribute action to the engine, something like that)… Well, you got the point =)
As always, I’m open to suggestions and I hope this would help you my friend.
Brad Wilson has a really good blog post series about Dependency Injection and MVC3, including Filter Providers, go and give a read.
Sample solution in bitbucket https://bitbucket.org/cprieto/blogsamplecode/src/