web-dev-qa-db-fra.com

Injection de dépendances dans les attributs

J'essaie d'injecter une dépendance dans un AuthorizeAttribute personnalisé comme suit:

public class UserCanAccessArea : AuthorizeAttribute
{
    readonly IPermissionService permissionService;

    public UserCanAccessArea() :
        this(DependencyResolver.Current.GetService<IPermissionService>()) { }

    public UserCanAccessArea(IPermissionService permissionService)
    {
        this.permissionService = permissionService;
    }

    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        string AreaID =
            httpContext.Request.RequestContext.RouteData.Values["AreaID"] as string;

        bool isAuthorized = false;

        if (base.AuthorizeCore(httpContext))
            isAuthorized = permissionService.UserCanAccessArea(AreaID, httpContext.User);

        return isAuthorized;
    }
}

Cela fonctionne mais semble se résoudre comme un singleton, ce qui signifie que je reçois les problèmes décrits dans ma question permanente

Ce que je voudrais faire, c'est utiliser l'injection de propriété mais comme mon attribut lui-même n'est pas résolu par Unity, je ne peux pas trouver un moyen de configurer le conteneur pour intercepter et résoudre une propriété. J'ai essayé ce qui suit:

public class UserCanAccessArea : AuthorizeAttribute
{
    public IPermissionService permissionService { get; set; }

    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        string AreaID =
            httpContext.Request.RequestContext.RouteData.Values["AreaID"] as string;

        bool isAuthorized = false;

        if (base.AuthorizeCore(httpContext))
            isAuthorized = permissionService.UserCanAccessArea(AreaID, httpContext.User);

        return isAuthorized;
    }
}

Récipient:

container.RegisterType<UserCanAccessArea>(new InjectionProperty("permissionService"));

Mais la propriété est toujours nulle au moment de l'exécution.

Quelqu'un at-il atteint cet objectif et si oui, avez-vous un exemple?

21
James

Vous devez empêcher complètement l'injection de dépendances dans les attributs. La raison de ceci est expliquée dans cet article: Injection de dépendance dans les attributs: ne le faites pas! . En résumé, l'article explique que:

  • L'injection de constructeur n'est pas possible, car la création d'une instance d'attribut ne peut pas être interceptée; le CLR est aux commandes.
  • L'utilisation de l'injection de propriété est fragile, car elle se traduit par couplage temporel , ce qui devrait être évité.
  • L'injection de dépendances dans les attributs ne permet pas de vérifier l'exactitude de la configuration du conteneur.
  • Des frameworks comme MVC et les attributs de cache de l'API Web, ce qui rend très facile la création accidentelle dépendances captives provoquant des bogues.

Vous avez deux choix ici:

  1. Rendre les attributs passifs, en séparant les données (l'attribut) de son comportement (le service) comme expliqué dans les article référencé et cet article connexe de Mark Seemann.
  2. Transformez vos attributs en objets humbles comme expliqué dans cette réponse . Cela signifie que vous:
    1. extraire toute la logique de l'attribut dans un service personnalisé qui contient toutes les dépendances.
    2. Enregistrez ce service dans votre conteneur.
    3. laissez la méthode de l'attribut (AuthorizeCore dans votre cas) ne faire que résoudre le service à partir du localisateur de service/DependencyResolver et appelez la méthode du service. Il est important de noter ici que vous ne pouvez pas faire d'injection de constructeur, d'injection de propriété et que le service ne peut pas être stocké dans l'état privé des attributs (comme vous l'avez déjà remarqué).

Quelle option utiliser:

  • Utilisez l'option 1 si vous êtes très soucieux de garder votre conception propre, ou si vous avez plus de quelques attributs que vous devez appliquer de cette façon, ou si vous souhaitez appliquer des attributs définis dans un assembly qui ne dépend pas de System.Web .Mvc.
  • Sinon, utilisez l'option 2.
46
Steven