2

Mon but: Injecter un IFilterProvider dans le cas où celui-ci est fourni par DI, mais pour revenir à la méthode globale FilterProviders.Providers.GetFilters() par défaut.IFilterProvider Injection sans un localisateur de service

Il existe de nombreuses ressources sur Internet (y compris une version "officielle" de Microsoft) qui montrent comment injecter l'interface IFilterProvider dans une classe. Cependant, tous utilisent le localisateur de service anti-pattern (par Mark Seeman) pour le faire. Voici une liste de ceux que je trouvais:

  1. http://msdn.microsoft.com/en-us/vs2010trainingcourse_aspnetmvcdependencyinjection_topic4.aspx
  2. http://merbla.blogspot.com/2011/02/ifilterprovider-using-autofac-for-mvc-3.html
  3. http://thecodinghumanist.com/blog/archives/2011/1/27/structuremap-action-filters-and-dependency-injection-in-asp-net-mvc-3
  4. IFilterProvider and separation of concerns

Alors quel est le problème avec ces méthodes? Ils injectent tous un conteneur DI dans la classe cible au lieu de l'inverse. Si vous voyez quelque chose dans votre classe comme container.Resolve(), alors vous ne cédez pas le contrôle de la durée de vie des objets au conteneur DI, ce qui est vraiment l'inversion du contrôle. En outre, le problème avec la tentative de création d'un repli sur l'instance globale FilterProviders.Providers est que même si elle a la même signature que l'interface IFilterProvider, elle n'implémente pas réellement cette interface. Alors, comment le membre statique global peut-il être injecté en tant que défaut logique tout en permettant l'utilisation de DI pour le remplacer?

+0

Il est bon de référencer votre conteneur à l'intérieur d'une classe tant que vous placez cette classe dans votre racine de composition. –

+0

D'accord. Mais ce ne serait que pour le code de configuration, pas pour le code d'application. Dans mon cas, IFitlerProvider fait partie d'une API DLL open source qui n'a aucun contrôle sur la racine de la composition. – NightOwl888

Répondre

2

Créer une classe d'emballage à base de IFilterProvider qui renvoie le résultat FilterProviders.Providers.GetFilters global(), comme celui-ci:

public class FilterProvider 
    : IFilterProvider 
{ 
    #region IFilterProvider Members 

    public IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor) 
    { 
     return FilterProviders.Providers.GetFilters(controllerContext, actionDescriptor); 
    } 

    #endregion 
} 

Ensuite, réglez le constructeur dans vos classes qui nécessitent la dépendance.

public class MyBusinessClass 
    : IMyBusinessClass 
{ 
    public MyBusinessClass(
     IFilterProvider filterProvider 
     ) 
    { 
     if (filterProvider == null) 
      throw new ArgumentNullException("filterProvider"); 
     this.filterProvider = filterProvider; 
    } 
    protected readonly IFilterProvider filterProvider; 

    public IEnumerable<AuthorizeAttribute> GetAuthorizeAttributes(ControllerContext controllerContext, ActionDescriptor actionDescriptor) 
    { 
     var filters = filterProvider.GetFilters(controllerContext, actionDescriptor); 

     return filters 
       .Where(f => typeof(AuthorizeAttribute).IsAssignableFrom(f.Instance.GetType())) 
       .Select(f => f.Instance as AuthorizeAttribute); 
    } 
} 

Ensuite, configurez votre conteneur DI pour utiliser le type de béton FilterProvider comme valeur par défaut.

container.Configure(x => x 
    .For<IFilterProvider>() 
    .Use<FilterProvider>() 
); 

Si vous devez remplacer la valeur par défaut et fournir votre propre IFilterProvider, maintenant il est juste une question de la création d'un nouveau type de béton basé sur IFilterProvider et échanger ce qui est enregistré dans le conteneur DI. Le plus important, il n'y a aucun localisateur de service n'importe où dans ce modèle.

Questions connexes