2012-01-06 14 views
6

Me he encontrado con un problema de diseño interesante con una biblioteca de clases que estoy escribiendo. Tengo una implementación personalizada de la AuthorizeAttribute que quiero que los clientes puedan utilizar como esto:Inyección de dependencias en AuthorizeAttribute en una biblioteca de clases

[Protected("permission_name")] 

En el código anterior, PermissionAttribute hereda de AuthorizeAttribute y utiliza un defecto local (DefaultContext creado usando HttpContext).

Detrás de escena, el atributo usa un SecurityService para verificar contra usuarios, roles y permisos (SecurityService usa un servicio de persistencia provisto por el cliente que puede conectar en la raíz de composición de su aplicación).

Entonces mis atributos necesitan una referencia al SecurityService para funcionar. Como los constructores de Atributos solo pueden tener constantes de tiempo de compilación, no puedo usar la inyección de constructor.

no quiero forzar a mis clientes a utilizar un marco DI - deben ser capaces de descubrir y cable hasta las dependencias necesarias en su raíz composición sin el uso de una biblioteca COI, si así lo desean.

Éstos son mis opciones:

  • tiene la biblioteca de utilizar un producto único SecurityService.
  • uso de inyección propiedad, que funcionaría, pero
    1. haría que la dependencia parecen opcional, que no lo es y
    2. no sé donde puedo hacer la inyección de la propiedad en una aplicación MVC en un atributo Autorizar .

Una posible solución a 2. anterior es que se puede hacer establecer una instancia de SecurityService como una propiedad estática en el atributo al inicio de la aplicación y el uso de una cláusula de guardia para evitar que sea establecer más de una vez, como esto:

class ProtectedAttribute : ... 
{ 
    private static ISecurityService _SecurityService ; 
    public static ISecurityService SecurityService 
    { 
     get 
     { 
      return _SecurityService ; 
     } 
     set 
     { 
      if (_SecurityService != null) 
       throw new InvalidOperationException("You can only set the SecurityService once per lifetime of this app.") ; 
      _SecurityService = value ; 
     } 
    } 
} 

El SecurityService podría ser una fachada servicio abstracto de modo que pueda ser extendido/reemplazado por una aplicación diferente.

¿Hay una mejor manera de resolver este problema?

ACTUALIZACIÓN: Adición de código para mostrar cómo voy a hacerlo:

Añadir una propiedad pública en el atributo que devuelve el nombre del permiso:

public class ProtectedAttribute : ... 
{ 
    private string _Permission ; 
    public string Permission { get { return _Permission ; } /*...*/ } 

    public ProtectedAttribute(string permission) { /*...*/ } 
} 

instalación de un filtro de autorización y configurar la dependencia a través de Ninject (si se utiliza Ninject):

using Ninject.Web.Mvc.FilterBindingSyntax; 

public class MyModule : Ninject.Modules.NinjectModule 
{ 
    public override void Load() 
    { 
    // mySecurityService instance below can have a singleton lifetime - perfect! 
    this.BindFilter<MyAuthorizationFilter>(FilterScope.Action, 0) 
     .WhenActionMethodHas<ProtectedAttribute>() 
     .WithConstructorArgument("securityService", mySecurityService) 
     .WithConstructorArgumentFromActionAttribute<ProtectedAttribute>("permission", p => p.PermissionName) ; 
    } 
} 

Ohhh ... es hermosa sniffle

+0

¿Es este ASP.NET MVC 3? –

+0

@Darin: Sí que requiere un mínimo de MVC 3 es aceptable. Actualizado las etiquetas. –

+1

Relacionados: http://stackoverflow.com/questions/7192543/injecting-dependencies-into-asp-net-mvc-3-action-filters-whats-wrong-with-this/7194467#7194467 –

Respuesta

7

Con ASP.NET MVC 3 puede usar la inyección de constructor con filtros de acción gracias al nuevo IFilterProvider. De esta forma, ya no es necesario decorar las acciones de su controlador con filtros de acción.Puede aplicarlos gracias a esta interfaz y utilizando un atributo de marcador.

Y si no quiere molestarse en implementarlo manualmente, siempre puede usar un marco DI existente como Ninject que proporciona un fluent way para definir las dependencias del filtro de acción.

+0

+1 for 'IFilterProvider 'eso da una solución bastante limpia. Problema similar [ASP.NET MVC IFilterProvider y separación de preocupaciones] (http://stackoverflow.com/questions/10708565/asp-net-mvc-ifilterprovider-and-separation-of-concerns) (Castle Windsor). –

0

Mis aplicaciones heredan de una clase de aplicación base que expone el contenedor IOC.

public interface IInjectableApplication 
    { 
     IUnityContainer Container { get; } 
    } 

entonces tengo una clase de atributo base, que es consciente de este

public abstract IocAwareActionFilterAttribute : ActionFilterAttribute{ 
    protected T ResolveItem<T>(ResultExecutedContext context) 
     { 
      var app = context.HttpContext.ApplicationInstance as IInjectableApplication; 
      if (app == null) { throw new NullReferenceException("Application is not IInjectable."); } 

      T c = (T)app.Container.Resolve(typeof(T)); 

      if (c == null) { throw new NullReferenceException(string.Format("Could not find injected {0}.", typeof(T).FullName)); } 
      return c; 
     } 

} 

Mientras que esto no es cierto de inyección, ya que los atributos no se construyen 'normalmente', esto proporciona un comportamiento similar. No hay razón por la que no deba ser adaptable a otros IOC

Cuestiones relacionadas