Injecting Dependencies into ASP.NET MVC 3 Action Filters. What's Wrong with This Approach

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

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.

ASP.NET MVC 3 Dependency Injection - Controllers, Views & Action Filters

It's been a while since I originally asked this but I thought I would share what I ended up doing.

In cases where I could not use constructor or attribute injection I solved it by using the DependencyResolver (service locator pattern). For example if I require the service IService I would simply inject it like so:

public IService Service => DependencyResolver.Current.GetService<IService>();

While some may consider this an anti-pattern I have found this performs well, leads to less problems and with new advances in C# I don't think it looks too bad.

However if you are using ASP.NET Core you should never have to use the service locator pattern as it has been rebuilt with dependency injection at the heart of it.

Action Filter Dependency Injection in ASP.NET MVC 3 RC2 with StructureMap

Thought I would come back and provide the solution.

As @Thomas pointed out above, Action Filters are now cached in MVC 3. This means that if you inject an object with an intended short life time (e.g. http request), it's going to be cached.

To fix, instead of injecting an ISession we inject a Func<ISession>. Then each time we need access to ISession we invoke the function. This ensures that even if the ActionFilter is cached, the ISession is scoped correctly.

I had to configure StructureMap like so to inject the "lazy" instance (unfortunately it doesn't inject a lazy instance automatically like it does with Ctor injection):

            x.SetAllProperties(p => {
p.OfType<Func<ISession>>();
});

My updated ActionFilter is below:

[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class UnitOfWorkAttribute : ActionFilterAttribute {

public Func<ISession> SessionFinder { get; set; }

public override void OnActionExecuting(System.Web.Mvc.ActionExecutingContext filterContext) {
var session = SessionFinder();
session.BeginTransaction();
}

public override void OnResultExecuted(System.Web.Mvc.ResultExecutedContext filterContext) {
var session = SessionFinder();

var txn = session.Transaction;

if (txn == null || !txn.IsActive) return;

if (filterContext.Exception == null || filterContext.ExceptionHandled)
{
session.Transaction.Commit();
}
else
{
session.Transaction.Rollback();
session.Clear();
}
}
}

How to use Action Filters with Dependency Injection in ASP.NET CORE?

If you want to avoid the Service Locator pattern you can use DI by constructor injection with a TypeFilter.

In your controller use

[TypeFilter(typeof(MyActionFilterAttribute), Arguments = new object[] {10})]
public IActionResult() NiceAction
{
...
}

And your ActionFilterAttribute does not need to access a service provider instance anymore.

public class MyActionFilterAttribute : ActionFilterAttribute
{
public int Limit { get; set; } // some custom parameters passed from Action
private ICustomService CustomService { get; } // this must be resolved

public MyActionFilterAttribute(ICustomService service, int limit)
{
CustomService = service;
Limit = limit;
}

public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
await next();
}
}

For me the annotation [TypeFilter(typeof(MyActionFilterAttribute), Arguments = new object[] {10})]seems to be awkward. In order to get a more readable annotation like [MyActionFilter(Limit = 10)]your filter has to inherit from TypeFilterAttribute. My answer of How do I add a parameter to an action filter in asp.net? shows an example for this approach.

Ninject and MVC3: Dependency injection to action filters

Here's how you could proceed:

public class MvcApplication : Ninject.Web.Mvc.NinjectHttpApplication
{
private class MyModule : NinjectModule
{
public override void Load()
{
Bind<IService>().To<ServiceImpl>();
Bind<IAuthenticationHelper>().To<AuthenticationHelperImpl>();
}
}

public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
}

public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}

protected override void OnApplicationStarted()
{
AreaRegistration.RegisterAllAreas();

RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
}

protected override IKernel CreateKernel()
{
var modules = new INinjectModule[] {
new MyModule()
};
var kernel = new StandardKernel(modules);
DependencyResolver.SetResolver(new NinjectDependencyResolver(kernel));
return kernel;
}
}

and then you could have your custom authorize attribute:

public class CustomAuthorizeAttribute : AuthorizeAttribute
{
[Inject]
public IService Service { get; set; }

[Inject]
public IAuthenticationHelper AuthenticationHelper { get; set; }

public override void OnAuthorization(AuthorizationContext filterContext)
{
}
}

and a controller action decorated with it:

[CustomAuthorize]
public ActionResult Index()
{
return View();
}

and the dependencies should be injected.

When using dependency injection in asp.net-mvc, what is the best way to deal with single action only dependencies?

Creation of your application components should be fast and their constructors should be simple. Any heavy initialization should be postponed.

When this becomes impossible due to the existence of some legacy code, you should wrap the component behind a proxy implementation for the same interface. For instance:

public class LazyServiceProxy : IService
{
private readonly Lazy<IService> service;
public LazyServiceProxy(Lazy<IService> service) {
this.service = service;
}

void IService.Method(object param1) => this.service.Value.Method(param1);
}

Also note that the fact that your dependency is only used in a single action is an indication of low cohesion between the action methods of your controller. Low cohesion is a sign of a Single Responsibility Principle (SRP) violation. The SRP dictates that each class should have a single responsibility so this might meen it's good to move the action and its dependency to its own controller. While it is a common practice to group actions together based on their common url prefix (e.g. /customers/action), MVC completely allows you to split actions into several controllers, while maintaining their original url.

ASP.NEt MVC 3 P1 Dependency Injection to Filters

Everything was as it should be. The answer can be found here http://forums.asp.net/t/1598945.aspx

Problem with Ninject and MVC3 Dependency injection action filter on Controller and Action

This happens if your controller and one of its actions have the LogActionAttribute at the same time.



Related Topics



Leave a reply



Submit