5

Estoy en el proceso de cambiar mi proyecto Asp.Net MVC3 para usar Autofac para la inyección de servicio en mis controladores. Hasta ahora, esto ha sido bastante sencillo. Todos mis servicios tienen una propiedad Telerik OpenAccess db que inyecté a través de los constructores (en una clase base de servicio). Y todos mis controladores tienen propiedades de construcción para servicios que también se inyectan.Inyección de propiedad Autofac

tengo una clase llamada AuditInfo que encapsula las propiedades auditables de un controlador:

public class AuditInfo 
{  
    public string RemoteAddress { get; set; } 

    public string XForwardedFor { get; set; } 

    public Guid UserId { get; set; } 

    public string UserName { get; set; } 
} 

Mi OpenAccess propiedad db en mis clases de servicio debe tener una instancia de esta clase se inyecta a ella con el fin de utilizar como auditando información en varias llamadas a la base de datos.

El problema es que esta no es una clase que se puede instanciar en Application_Start porque al menos dos propiedades de ella, RemoteAddress y XForwardedFor se rellenan en la etapa más temprana de OnActionExecuting, es decir, una vez que existen las variables de solicitud.

Por lo tanto, ejecutar este método en el OnActionExecuting de mi clase BaseController como tal:

protected override void OnActionExecuting(ActionExecutingContext filterContext) 
{ 
    base.OnActionExecuting(filterContext); 
    db.AuditInfo = AuditInfo;          
} 

public AuditInfo AuditInfo 
{ 
    get 
    { 
     return new AuditInfo() 
     { 
      RemoteAddress = this.Request.ServerVariables["REMOTE_ADDR"], 
      XForwardedFor = this.Request.ServerVariables["X_FORWARDED_FOR"], 
      UserId = this.UserId, 
      UserName = this.UserName 
     }; 
    } 
} 

Por lo tanto - mi problema/preguntas son:

  1. no me gusta este alcance directo de a la propiedad OpenAccess db en OnActionExecuting.
  2. me gustaría este AuditInfo básicamente a inyectar en cualquier propiedad en cualquier lugar AuditInfo
  3. no creo que pueda usar la inyección de constructor para AuditInfo porque los servicios dependen de db - controladores dependen de los servicios - db depende de AuditInfo PERO AuditInfo no está disponible hasta que se crea una instancia de un controlador y se recibe su primera solicitud. => dependencia circular ...

¿Cómo configuraría autofac para que inserte AuditInfo en cualquier clase que lo tenga como propiedad? ¿O hay una mejor manera de eludir la dependencia circular y usar alguna forma de propiedades de constructor lambda/perezoso?

¿Es en absoluto preocupante que AuditInfo se reinicialice potencialmente innecesariamente en cada solicitud, aunque muchas solicitudes pueden ser parte de la misma sesión y no tener direcciones IP/información de usuario diferentes?

Gracias

+0

estoy de acuerdo con problema/pregunta 3 - como señala Steven se puede usar 'HttpContext.Current'. Así que 'AuditInfo' no depende del controlador, por lo que no existe una dependencia circular, por lo que podría crear el constructor' AuditInfo' si lo desea. –

+0

Bueno, creo que esto se debe a que la solución propuesta está utilizando una variable estática global para hacer referencia al objeto Request en lugar de inicializar el objeto AuditInfo dentro del controlador donde el objeto Request existe de forma natural. Creo que las variables del servidor ni siquiera existen en Application_Start, donde se está produciendo la inyección, porque una acción debe llamarse primero, ¿no? – t316

Respuesta

2

Resulta Autofac's MVC Integration can resolve an HttpRequestBase for you. Por lo tanto, no es necesario que haga referencia directamente al HttpContext.Current.Request.

Implementación de Autofac uses HttpContext.Current detrás de escena. Esto funciona porque el marco MVC establece HttpContext.Current antes de que se ejecute su código (o Autofac).De modo que no existe una dependencia circular: la Solicitud "existe naturalmente" en HttpContext.Current.Request tanto como en su controlador. (This question tipo de explica cómo)

por lo que podría hacer un IAuditInfoFactory como Steven sugiere pero exigir una HttpRequestBase en su constructor en lugar de utilizar HttpContext.Current si te hace sentir mejor acerca de no hacer referencia a las variables estáticas.

Además, no hay dependencia circular y se podía inyectar el constructor de la AuditInfo si quieres:

builder.Register(c => c.Resolve<IAuditInfoFactory>().CreateNew()) 
    .As<AuditInfo>() 
    .InstancePerHttpRequest(); 
+0

Steven hizo un gran trabajo al enmarcar la base de la respuesta correcta, sin embargo, la falta de inyección de HttpRequestBase y la suma predeterminada de .kramer de ese punto hace que su respuesta sea un poco más completa. No estoy seguro, cómo se supone que debo manejar el marcado equitativo de la respuesta correcta en una situación como esta, por favor no dude en hacérmelo saber. Por ahora estoy marcando la respuesta de default.kramer como correcta. Gracias – t316

2

La respuesta es: Utilice una fábrica.

Inyectar una IAuditInfoFactory en el tipo que lo necesita, y crear una aplicación como esta:

public class HttpRequestAuditInfoFactory : IAuditInfoFactory 
{ 
    // Service for requesting information about the current user. 
    private readonly ICurrentUserServices user; 

    public HttpRequestAuditInfoFactory(ICurrentUserServices user) 
    { 
     this.user = user; 
    } 

    AuditInfo IAuditInfoFactory.CreateNew() 
    { 
     var req = HttpContext.Current.Request; 

     return new AuditInfo() 
     { 
      RemoteAddress = req.ServerVariables["REMOTE_ADDR"], 
      XForwardedFor = req.ServerVariables["X_FORWARDED_FOR"], 
      UserId = this.user.UserId, 
      UserName = this.user.UserName 
     }; 
    } 
} 

Puede registrar esa clase de la siguiente manera:

builder.RegisterType<HttpRequestAuditInfoFactory>() 
    .As<IAuditInfoFactory>() 
    .SingleInstance(); 

Ahora se puede inyectar

+0

Gracias por la respuesta rápida. Esto debería funcionar, pero ¿esta solución realmente no se basa en hacer referencia a HttpContext.Current.Request y leer variables de él en un contexto extranjero en lugar de almacenar las variables necesarias durante el ciclo de vida natural de HttpContext.Current.Request e insertar esas variables en el contexto extranjero con acoplamiento flojo? Significado: la implementación de IAuditInfoFactory necesita hacer referencia a System.Web y saber directamente sobre HttpContext.Current.Request para que esto funcione ... – t316

+0

Esta implementación concreta de 'HttpRequestAuditInfoFactory' realmente toma una dependencia dura del' HttpContext'. En ese sentido, es consciente de la plataforma. Esto no es un problema, pero por esa razón no es (o no debería ser) parte de su aplicación, pero debería ser parte de lo que llamamos [Composition Root] (http://blog.ploeh.dk/2011/ 07/28/CompositionRoot.aspx) (CR). Esta es la ruta de inicio de la aplicación. Ninguna otra parte que no sea la CR debe conocer la existencia de esta 'HttpRequestAuditInfoFactory'. La aplicación solo conoce el 'IAuditInfoFactory'. – Steven

+0

Como el resto de la aplicación conoce 'IAuditInfoFactory' pero no sobre' HttpRequestAuditInfoFactory', hace que sea fácil migrar (parte de) su aplicación a, por ejemplo, un servicio de Windows. Necesitará la implementación específica del Servicio de Windows de 'IAuditInfoFactory' y la cableará, en lugar del' HttpRequestAuditInfoFactory' en el CR (el método 'main') del Servicio de Windows. – Steven

Cuestiones relacionadas