12

Sto cercando di iniettare una dipendenza in un personalizzato AuthorizeAttribute come segue:iniezione Property di attributi

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; 
    } 
} 

Questo funziona, ma sembra essere la risoluzione come un Singleton senso ottengo i problemi descritti nel mio pervious question

Quello che mi piacerebbe fare è usare l'iniezione di proprietà ma poiché il mio Attributo non è stato risolto da Unity, non sono in grado di trovare un modo per configurare il contenitore per intercettare e risolvere una proprietà. Ho provato quanto segue:

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; 
    } 
} 

Contenitore:

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

Ma la proprietà è sempre nullo in fase di esecuzione.

Qualcuno ha raggiunto questo e in caso affermativo avete un esempio?

risposta

27

È necessario evitare completamente l'inserimento delle dipendenze negli attributi. La ragione di ciò è spiegata in questo articolo: Dependency Injection in Attributes: don’t do it!. In sintesi l'articolo spiega che:

  • L'iniezione del costruttore non è possibile, poiché la creazione di un'istanza di attributo non può essere intercettata; il CLR ha il controllo.
  • L'utilizzo dell'iniezione di proprietà è fragile, poiché risulta in Temporal Coupling, che deve essere impedito.
  • L'immissione delle dipendenze in attributi rende impossibile verificare la correttezza della configurazione del contenitore.
  • Framework come MVC e gli attributi della cache API Web, rendendo molto facile creare accidentalmente captive dependencies causando bug.

Hai due scelte qui:

  1. rendere il passivo attributi, suddividendo i dati (l'attributo) dal suo comportamento (il servizio), come illustrato nella referenced article e this related article da Mark Seemann.
  2. Trasforma i tuoi attributi in humble objects come spiegato in this answer. Ciò significa che:
    1. estrarre tutta la logica dall'attributo in un servizio personalizzato che contiene tutte le dipendenze.
    2. Registra il servizio nel tuo contenitore.
    3. lasciare che il metodo dell'attributo (AuthorizeCore nel tuo caso) non faccia altro che risolvere il servizio dal localizzatore di servizio/DependencyResolver e chiamare il metodo del servizio. È importante notare che non è possibile eseguire l'iniezione del costruttore, l'iniezione di proprietà e il servizio non può essere archiviato nello stato privato degli attributi (come già notato).

dell'opzione da utilizzare:

  • Utilizzare l'opzione 1 se si sono molto interessati nel mantenere il design pulito, o se si hanno più di un paio di attributi che è necessario applicare in questo modo, o si desidera applicare gli attributi sono definiti in un assembly che non dipende da System.Web.Mvc.
  • Utilizzare l'opzione 2 in caso contrario.
+0

Ho esplorato l'opzione 1 e sembra che questo non possa essere utilizzato se è stato specificato che il dbcontext sia risolto in InRequestScope (Ninject). Altrimenti funziona perfettamente. L'ho provato dapprima con il localizzatore di servizi (anti-pattern, ma risolve la creazione di oggetti di servizio). Il problema con l'attributo autorizzato personalizzato è che è stato creato in fase di runtime e non segue InRequestScope. Perfavore, correggimi se sbaglio. –

+0

Voglio usare l'opzione InRequestScope, perché voglio avere uno e lo stesso dbcontext quando lavori con repository diversi, ed essere in grado di usare UnitOfWork - un posto dove chiamare i savechanges nel dbcontext. –

+0

Un'idea non è usare DI per il servizio, ma solo creare un oggetto reale usato nel filtro Autorizzazione. E 'un buon approccio? Renderà il mio codice brutto come il servizio che voglio avere altri servizi e repository dipendenti .... –