Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Injecting dependencies into ASP.NET MVC 3 action filters. What's wrong with this approach?

Here's the setup. Say I have some action filter that needs an instance of a service:

public interface IMyService {    void DoSomething(); }  public class MyService : IMyService {    public void DoSomething(){} } 

I then have an ActionFilter that needs an instance of that service:

public class MyActionFilter : ActionFilterAttribute {    private IMyService _myService; // <--- How do we get this injected     public override void OnActionExecuting(ActionExecutingContext filterContext)    {        _myService.DoSomething();        base.OnActionExecuting(filterContext);    } } 

In MVC 1/2 injecting dependencies into action filters was a bit of a pain in the ass. The most common approach was to use a custom action invoker as can be seen here: http://www.jeremyskinner.co.uk/2008/11/08/dependency-injection-with-aspnet-mvc-action-filters/ The main motivation behind this workaround was because this following approach was considered sloppy and tight coupling with the container:

public class MyActionFilter : ActionFilterAttribute {    private IMyService _myService;     public MyActionFilter()       :this(MyStaticKernel.Get<IMyService>()) //using Ninject, but would apply to any container    {     }     public MyActionFilter(IMyService myService)    {       _myService = myService;    }     public override void OnActionExecuting(ActionExecutingContext filterContext)    {        _myService.DoSomething();        base.OnActionExecuting(filterContext);    } } 

Here we're using constructor injection and overloading the constructor to use the container and inject the service. I do agree that does tightly couple the container with the ActionFilter.

My question though is this: Now in ASP.NET MVC 3, where we have an abstraction of the container being used (through the DependencyResolver) are all these hoops still necessary? Allow me to demonstrate:

public class MyActionFilter : ActionFilterAttribute {    private IMyService _myService;     public MyActionFilter()       :this(DependencyResolver.Current.GetService(typeof(IMyService)) as IMyService)    {     }     public MyActionFilter(IMyService myService)    {       _myService = myService;    }     public override void OnActionExecuting(ActionExecutingContext filterContext)    {        _myService.DoSomething();        base.OnActionExecuting(filterContext);    } } 

Now I know that some purists might scoff at this, but seriously, what would be the downside? It's still testable as you can use the constructor that takes an IMyService at test time and inject a mock service that way. You're not tied down to any implementation of DI container since you're using the DependencyResolver, so are there any downsides to this approach?

Incidentally, here's another nice approach for doing this in MVC3 using the new IFilterProvider interface: http://www.thecodinghumanist.com/blog/archives/2011/1/27/structuremap-action-filters-and-dependency-injection-in-asp-net-mvc-3

like image 401
BFree Avatar asked Aug 25 '11 14:08

BFree


People also ask

Why we use action filters in MVC?

ASP.NET MVC provides Action Filters for executing filtering logic either before or after an action method is called. Action Filters are custom attributes that provide declarative means to add pre-action and post-action behavior to the controller's action methods.

What are the different types of MVC action filter does ASP.NET MVC 3.0 life cycle has?

Summary. In this tutorial, you were introduced to ASP.NET MVC action filters. You learned about the four different types of filters: authorization filters, action filters, result filters, and exception filters.

What are the key benefits of using the filter feature in ASP.NET MVC?

ASP.NET MVC Filters are used to inject extra logic at the different levels of MVC Framework request processing. Filters provide a way for cross-cutting concerns (logging, authorization, and caching).


2 Answers

Yes, there are downsides, as there are lots of issues with IDependencyResolver itself, and to those you can add the use of a Singleton Service Locator, as well as Bastard Injection.

A better option is to implement the filter as a normal class into which you can inject whichever services you'd like:

public class MyActionFilter : IActionFilter {     private readonly IMyService myService;      public MyActionFilter(IMyService myService)     {         this.myService = myService;     }      public void OnActionExecuting(ActionExecutingContext filterContext)     {         if(this.ApplyBehavior(filterContext))             this.myService.DoSomething();     }      public void OnActionExecuted(ActionExecutedContext filterContext)     {         if(this.ApplyBehavior(filterContext))             this.myService.DoSomething();     }      private bool ApplyBehavior(ActionExecutingContext filterContext)     {         // Look for a marker attribute in the filterContext or use some other rule         // to determine whether or not to apply the behavior.     }      private bool ApplyBehavior(ActionExecutedContext filterContext)     {         // Same as above     } } 

Notice how the filter examines the filterContext to determine whether or not the behavior should be applied.

This means that you can still use attributes to control whether or not the filter should be applied:

public class MyActionFilterAttribute : Attribute { } 

However, now that attribute is completely inert.

The filter can be composed with the required dependency and added to the global filters in global.asax:

GlobalFilters.Filters.Add(new MyActionFilter(new MyService())); 

For a more detailed example of this technique, although applied to ASP.NET Web API instead of MVC, see this article: http://blog.ploeh.dk/2014/06/13/passive-attributes

like image 93
Mark Seemann Avatar answered Sep 20 '22 21:09

Mark Seemann


I'm not positive, but I believe you can just use an empty constructor (for the attribute part) and then have a constructor that actually injects the value (for the filter part).*

Edit: After a little reading up, it appears that the accepted way to do this is via property injection:

public class MyActionFilter : ActionFilterAttribute {     [Injected]     public IMyService MyService {get;set;}          public override void OnActionExecuting(ActionExecutingContext filterContext)     {         MyService.DoSomething();         base.OnActionExecuting(filterContext);     } } 

Regarding the why not use a Service Locator question: It mostly just reduces the flexibility of your dependency injection. For example, what if you were injecting a logging service, and you wanted to automatically give the logging service the name of the class it's being injected into? If you use constructor injection, that would work great. If you're using a Dependency Resolver/Service Locator, you'd be out of luck.

Update

Since this got accepted as the answer, I'd like to go on the record to say that I prefer Mark Seeman's approach because it separates the Action Filter responsibility away from the Attribute. Furthermore, Ninject's MVC3 extension has some very powerful ways to configure action filters via bindings. See the following references for more details:

  • https://github.com/ninject/ninject.web.mvc/wiki/Dependency-injection-for-filters
  • https://github.com/ninject/ninject.web.mvc/wiki/Conditional-bindings-for-filters
  • https://github.com/ninject/ninject.web.mvc/wiki/Filter-configurations

Update 2

As @usr pointed out in the comments below, ActionFilterAttributes are instantiated when the class is loaded, and they last the entire lifetime of the application. If the IMyService interface is not supposed to be a Singleton, then it ends up being a Captive Dependency. If its implementation isn't thread-safe, you could be in for a lot of pain.

Whenever you have a dependency with a shorter lifespan than your class's expected lifespan, it's wise to inject a factory to produce that dependency on-demand, rather than injecting it directly.

like image 29
StriplingWarrior Avatar answered Sep 21 '22 21:09

StriplingWarrior