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
- haría que la dependencia parecen opcional, que no lo es y
- 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
¿Es este ASP.NET MVC 3? –
@Darin: Sí que requiere un mínimo de MVC 3 es aceptable. Actualizado las etiquetas. –
Relacionados: http://stackoverflow.com/questions/7192543/injecting-dependencies-into-asp-net-mvc-3-action-filters-whats-wrong-with-this/7194467#7194467 –