2009-11-30 13 views
12

Tengo varios servicios de inyección de dependencia que dependen de cosas como el contexto HTTP. En este momento estoy configurándolos como singletons el contenedor Windsor en el controlador Application_Start, lo que obviamente es un problema para dichos servicios.ASP.NET MVC & Windsor.Castle: trabajando con servicios dependientes de HttpContext

¿Cuál es la mejor manera de manejar esto? Estoy considerando hacerlos transient y luego soltarlos después de cada solicitud HTTP. Pero, ¿cuál es la mejor forma/lugar para inyectar el contexto HTTP en ellos? Controlador de fábrica o en otro lugar?

Respuesta

5

Con Castle Windsor puede usar el PerWebRequest de por vida, que se ajusta bastante bien a sus necesidades.

Eso significa que puede simplemente inyectar el material HTTP en sus servicios, y el contenedor se encargará de la administración adecuada de por vida. Sin embargo, esto requiere que usted también registre todos estos servicios (y todos los consumidores de esos servicios, etc.) como PerWebRequest (o Transient) porque si los registra como Singletons, se mantendrán en contextos obsoletos (y posiblemente eliminados).

+0

Mark, gracias por la información - No sabía acerca de PerWebRequest. Lo comprobaré. –

+0

Mark, he investigado PerWebRequest, pero todavía no veo cómo los servicios pueden obtener HttpContext. Cuando trato de registrar una instancia de HttpContextBase en el contenedor yo mismo, falla después de la segunda solicitud (ya que una instancia ya estaba registrada en la solicitud anterior). Hasta ahora no he podido encontrar nada en Google ... –

+0

Pude haber entendido mal lo que está intentando hacer, pero no puede usar HttpContext desde Application_Start porque en este punto * no * hay HttpContext (PerWebRequest o no PerWebRequest)) Ahora que lo pienso, no tiene sentido intentar controlar la vida útil de HttpContext desde DI Container, ya que esta vida ya está siendo administrada por el framework ASP.NET MVC.Lo que * puede * hacer es engancharse en una IControllerFactory personalizada y tomar el HttpContext que se le sirvió en ese momento, y luego usar un método de fábrica para conectar todo lo demás que dependa de él. –

24

Al igual que dijo Mark, debe registrar estos servicios dependientes de http como PerWebRequest o Transient. He aquí un ejemplo que muestra cómo registrar e inyectar un HttpRequest o HttpContext:

public class Service { 
    private readonly HttpRequestBase request; 

    public Service(HttpRequestBase request) { 
     this.request = request; 
    } 

    public string RawUrl { 
     get { 
      return request.RawUrl; 
     } 
    } 
} 

... 

protected void Application_Start(object sender, EventArgs e) { 
    IWindsorContainer container = new WindsorContainer(); 
    container.AddFacility<FactorySupportFacility>(); 
    container.AddComponentLifeStyle<Service>(LifestyleType.Transient); 

    container.Register(Component.For<HttpRequestBase>() 
     .LifeStyle.PerWebRequest 
     .UsingFactoryMethod(() => new HttpRequestWrapper(HttpContext.Current.Request))); 

    container.Register(Component.For<HttpContextBase>() 
     .LifeStyle.PerWebRequest 
     .UsingFactoryMethod(() => new HttpContextWrapper(HttpContext.Current))); 
} 

Mediante el uso de HttpRequestBase en lugar de HttpRequest puede burlarse fácilmente hacia fuera para la prueba. Además, no olvide registrar PerWebRequestLifestyleModule en su web.config.

+0

Gracias Mauricio, esto es algo que estaba buscando. –

+0

Puede valer la pena reiterar que debe agregar la línea: container.AddFacility (); Este fue un pequeño error para mí que me perdí tu ejemplo en la primera lectura. Gracias por la ayuda, ¡muy útil! – ArtificialGold

+0

A partir de Windsor 2.5, 'FactorySupportFacility' ya no es necesario para este caso. –

4

Me acabo de topar con este mismo problema, pero mi solución es algo diferente.

Interfaz:

public interface IHttpContextProvider 
{ 
    /// <summary> 
    /// Gets the current HTTP context. 
    /// </summary> 
    /// <value>The current HTTP context.</value> 
    HttpContextBase Current { get; } 
} 

Implementación:

/// <summary> 
/// A default HTTP context provider, returning a <see cref="HttpContextWrapper"/> from <see cref="HttpContext.Current"/>. 
/// </summary> 
public class DefaultHttpContextProvider : IHttpContextProvider 
{ 
    public HttpContextBase Current 
    { 
     get { return new HttpContextWrapper(HttpContext.Current); } 
    } 
} 

entonces registrar el IHttpContextProvider como un singleton en el recipiente. Todavía soy un poco novato cuando se trata de DI, así que tal vez estoy complicando las cosas, pero por lo que puedo entender, no puedo tener ningún componente único que dependa de los componentes de estilo de vida de PerWebRequest, lo cual tiene sentido (pero eso es lo que hacen todos los ejemplos). En mi solución, dependo de HttpContext.Current en un componente aislado y no estoy interesado en probar eso. Pero cada componente que necesita acceso al contexto HTTP puede obtener eso dependiendo de IHttpContextProvider y simularlo fácilmente según sea necesario.

¿Realmente estoy complicando las cosas o hay alguna advertencia en mi solución?

+1

Debo mencionar que, de esta manera, ninguno de mis servicios que dependen del contexto HTTP actual debe registrarse como PerWebRequest/Transient. – Siewers

+0

Funciona bien, gracias. –

Cuestiones relacionadas