vendredi 2 septembre 2011 10:40
tja
[ASP.NET MVC 3] Deep Dive Injection de Dépendance – FilterProviders – Partie 6
Cet article fait partie d’une série consacrée à l’injection de dépendance dans MVC 3 :
Bien que je n’ai pas écrit depuis long temps, il faut bien terminer la série avant que MVC 4 sorte.
FilterProviders
L’enregistrement des filtres se fait grâce au point d’enregistrement statique qui est une collection FilterProviders.Providers. Cette collection fournie une méthode de façade GetFilters qui agrège des filtres de tous les providers dans une liste unique. L’ordre d’enregistrement n’est pas important.
Par défaut il y a trois fournisseurs de filtres enregistrés dans l’application :
- Global filters (GlobalFilters.Filters).
- Filtres attributs (FilterAttributeFilterProvider).
- Instances de contrôleurs (ControllerInstanceFilterProvider) : et oui les contrôleurs et leurs actions sont considérés en tant que filtres.
De plus la façade GetFilters récupère toutes les instances implémentant l’interface IFilterProvider enregistrés dans le dependency resolver en utilisant la méthode DependencyResolver.Current.GetAllInstances<IFilterProvider>().
L’ordre des filtres
L’ordre des filtres est déterminé par rapport à la métadata contenue dans la classe Filter. L’ordre est d’abord déterminé par le numéro d’ordre puis par le scope.
Voyons d’abord la signature de la méthode IFilterProvider .
1: public interface IFilterProvider
2: {
3: IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor);
4: }
Cette méthode permet de récupérer l’ensemble des filtres enregistrés. La classe Filter et l’énumération FilterScope sont définies comme ceci :
1: public class Filter
2: {
3: public const int DefaultOrder = -1;
4:
5: public Filter(object instance, FilterScope scope, int? order = null);
6:
7: public object Instance { get; }
8: public int Order { get; }
9: public FilterScope Scope { get; }
10: }
11:
12: public enum FilterScope
13: {
14: First = 0,
15: Global = 10,
16: Controller = 20,
17: Action = 30,
18: Last = 100,
19: }
Comme nous avons dit un peu plus tôt, le controller est un filtre également avec l’ordre = Int32.MinValue et Scope First afin de s’assurer qu’ils s’exécutent en premier.
Note : Il est impossible d’enregistrer les GlobalFilters avec le dependency resolver.
La DI dans la pratique
Nouveautés MVC3 : FilterAttributeFilterProvider permet de garantir l’injection de dépendances dans les filtres.
La responsabilité de la collection des FilterProviders est de retourner les instances des filtres au Framework MVC en s’assurant de la DI où possible. Voici un exemple d’un provider des filtres avec Unity :
1: public class UnityFilterAttributeFilterProvider : FilterAttributeFilterProvider
2: {
3: private readonly IUnityContainer _container;
4:
5: public UnityFilterAttributeFilterProvider(IUnityContainer container)
6: {
7: if (container == null)
8: throw new ArgumentNullException("container", "The container cannot be null.");
9:
10: _container = container;
11: }
12:
13: protected override IEnumerable<FilterAttribute> GetControllerAttributes(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
14: {
15: var attributes = base.GetControllerAttributes(controllerContext, actionDescriptor);
16:
17: foreach (var attribute in attributes)
18: {
19: _container.BuildUp(attribute.GetType(), attribute);
20: }
21:
22: return attributes;
23: }
24:
25: protected override IEnumerable<FilterAttribute> GetActionAttributes(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
26: {
27: var attributes = base.GetActionAttributes(controllerContext, actionDescriptor);
28: foreach (var attribute in attributes)
29: {
30: _container.BuildUp(attribute.GetType(), attribute);
31: }
32:
33: return attributes;
34: }
35: }
Ce qui est intéressant de remarquer ce que dans les surcharges des méthodes GetControllerAttributes et GetActionAttributes nous utilisons l’instance de notre containeur Unity pour injecter de dépendances des services dans des attributs (filtres) existant tant qu’au niveau du contrôleur qu’au niveau des ses actions. Ceci est possible grâce à la méthode BuildUp de Unity.
Le Framework MVC est responsable de la création des filtres donc impossible d’utiliser l’injection de dépendance par défaut. Il faut passer par l’injection de setters. Voici un exemple d’un filtre nécessitant l’injection de dépendance :
1: public class InjectedFilterAttribute : ActionFilterAttribute
2: {
3: [Dependency]
4: public IOrderService OrderService { get; set; }
5:
6: public override void OnResultExecuted(ResultExecutedContext filterContext)
7: {
8: filterContext.HttpContext.Response.Write(String.Format("<p>The filter says 2 + 3 is {0}.</p>", OrderService.CalculateVAT(10)));
9: }
10: }
Le seul désavantage de cette technique est qu’on doit décorer le setter par lequel l’injection de dépendance doit se faire avec l’attribut Dependency ce qui nous couple un peu au containeur DI utilisé. Mais c’est le moindre mal par rapport au bénéfices qu’on tire de l’injection de dépendance.
Rappel MVC2 : Pour de l’injection de dépendance on passait par ControllerActionInvoker.
Il ne nous reste que d’enregistrer notre provider des filtre dans le bootstrapper de l’application :
1: // DI in Filters
2: // Supprimer les anciens providers de filtres.
3: var oldProvider = FilterProviders.Providers.Single(f => f is FilterAttributeFilterProvider);
4: FilterProviders.Providers.Remove(oldProvider);
5:
6: // Provider de filtres vient d'un enregistrement statique
7: var provider = new UnityFilterAttributeFilterProvider(container);
8: FilterProviders.Providers.Add(provider);
9:
10: // Ou d'un enregistrement de Dependency Resolver.
11: container.RegisterInstance<IFilterProvider>("attributes", provider);
12: DependencyResolver.SetResolver(new UnityDependencyResolver(container));
Dans un post future je vais donner un cas d’utilisation concret d’un tel provider des filtres.
// Thomas
Ce post vous a plu ? Ajoutez le dans vos favoris pour ne pas perdre de temps à le retrouver le jour où vous en aurez besoin :