Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

IoC / DI with MVC Attributes

One of my MVC attributes has a dependancy on a service which I was hoping to inject via the constructor. Obviously the MVC attribute requires a parameterless constructor too.

    public MyAttribute()
    {
       ... 
    }

    public MyAttribute(IMyService)
    {
      ...
    }

I was thinking I could do a property injection rather than constructor injection however my controllers(and their attributes) are in a seperate class library with no reference to an IoC container.

Is there are way of using a service within an attribute filter without referencing the IoC container?

For what it's worth I'm using Ninject for MVC3

Thanks

like image 544
Dave Hogan Avatar asked Oct 11 '11 09:10

Dave Hogan


People also ask

Does MVC support dependency injection?

The Dependency Injection (DI) Design Pattern The Dependency Resolver in ASP.NET MVC can allow you to register your dependency logic somewhere else (e.g. a container or a bag of clubs). The advantages of using Dependency Injection pattern and Inversion of Control are the following: Reduces class coupling.


1 Answers

As a general solution (without any special integration support of your DI framework), MVC3 asks the IDependencyResolverfor an IFilterProvider. In other words, the trick is to:

  1. Remove the FilterAttributeFilterProvider from the System.Web.Mvc.FilterProviders collection.
  2. Register a IDependencyResolver for your particular DI framework (if you're not already doing that).
  3. Register a custom IFilterProvider in your container that can inject the properties of any requested attribute.

This looks like this:

var container = new [your favorite container];

// 1. Remove the FilterAttributeFilterProvider from the collection.
var providers = FilterProviders.Providers
    .OfType<FilterAttributeFilterProvider>().ToList();

providers.ForEach(p => FilterProviders.Providers.Remove(p));

// 2. Register a IDependencyResolver
DependencyResolver.SetResolver(new YourDiResolver(container));

// 3. Register a customer IFilterProvider.
container.Register<IFilterProvider, YourAttributeFilterProvider>();

The YourAttributeFilterProvider will look like this:

private class YourAttributeFilterProvider
    : FilterAttributeFilterProvider
{
    private readonly [your favorite container] container;

    public YourAttributeFilterProvider(
        [your favorite container] container)
        : base(false)
    {
        this.container = container;
    }

    public override IEnumerable<Filter> GetFilters(
        ControllerContext controllerContext, 
        ActionDescriptor actionDescriptor)
    {
        var filters = base.GetFilters(controllerContext,
            actionDescriptor).ToList();

        // Inject properties into attribute here.
        filters.ForEach(f => container.InjectProperties(f.Instance));

        return filters;
    }
}

Many frameworks, such as Ninject and Autofac, have built-in support for this through their MVC integration packages. Still it is valuable to understand how to do this manually.

WARNING:

One big warning about dependency injection in MVC filter attributes. MVC caches attributes and reuses instances for the duration of app domain. This means that they practically become singletons, and they drag their dependencies along. In other words: those dependencies will become singletons as well, which is also known as the captive dependency problem. So be sure you only inject singletons into your attributes, because your production system will be troubled by concurrency bugs if you don't.

Although most DI frameworks have support for injection into filter attributes, none of the frameworks helps into fixing this issue. So a better solution instead is to keep your attributes passive, as explained here and here.

like image 55
Steven Avatar answered Nov 15 '22 09:11

Steven