开发者

IoC / DI with MVC Attributes

开发者 https://www.devze.com 2023-04-11 23:27 出处:网络
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.

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 controller开发者_运维百科s(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


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.


I've tried to achieve the same few days ago. Please read the post here There is a built in functionality for binding filters.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号