2009-11-30 11 views
12

Ich habe mehrere Dependency-Injection-Dienste, die von Sachen wie HTTP-Kontext abhängig sind. Im Moment konfiguriere ich sie als Singletons der Windsor-Container in der Application_Start-Handler, die offensichtlich ein Problem für solche Dienste ist.ASP.NET MVC & Windsor.Castle: Arbeiten mit HttpContext-abhängigen Diensten

Was ist der beste Weg, damit umzugehen? Ich überlege mir, sie transient und dann nach jeder HTTP-Anfrage freizugeben. Aber was ist der beste Weg, um den HTTP-Kontext in sie zu injizieren? Controller Fabrik oder woanders?

Antwort

5

Mit Castle Windsor können Sie die PerWebRequest Lebensdauer verwenden - das sollte ziemlich gut zu Ihren Anforderungen passen.

Das bedeutet, dass Sie einfach das HTTP-Zeug in Ihre Dienste injizieren können, und der Container wird sich um das richtige Lifetime Management kümmern. Dies erfordert jedoch, dass Sie auch alle diese Dienste (und alle Konsumenten dieser Dienste usw.) als PerWebRequest (oder Transient) registrieren, da sie, wenn Sie sie als Singletons registrieren, an veralteten (und möglicherweise entsorgten) Kontexten festhalten.

+0

Mark, danke für die Info - Ich wusste nicht über PerWebRequest. Ich werde das überprüfen. –

+0

Mark, ich habe in PerWebRequest untersucht, aber ich sehe immer noch nicht, wie Dienste HttpContext erhalten können. Wenn ich versuche, eine Instanz von HttpContextBase im Container selbst zu registrieren, schlägt sie nach der zweiten Anfrage fehl (da in der vorherigen Anfrage bereits eine Instanz registriert war). Ich konnte bisher nichts bei Google finden ... –

+0

Ich habe vielleicht missverstanden, was Sie versuchen zu tun, aber Sie können nicht den HttpContext von Application_Start verwenden, weil an dieser Stelle * kein HttpContext (PerWebRequest oder kein PerWebRequest) ist). Nun, da ich darüber nachdenke, macht es keinen Sinn zu versuchen, die Lebensdauer des HttpContext vom DI Container zu kontrollieren, da diese Lebensdauer bereits vom ASP.NET MVC Framework verwaltet wird.Was Sie * tun * können, ist, sich in eine benutzerdefinierte IControllerFactory einzuklinken und den HttpContext abzufangen, der zu diesem Zeitpunkt an Sie geliefert wurde, und dann eine Factory-Methode zu verwenden, um alles andere, das davon abhängt, zu verkabeln. –

24

Genau wie Mark sagte, müssen Sie diese http-abhängigen Dienste entweder als PerWebRequest oder Transient registrieren. Hier ist ein Beispiel, das zeigt, wie sich registrieren und ein Httprequest oder Httpcontext zu injizieren:

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

Durch die Verwendung von HttpRequestBase statt HttpRequest Sie leicht zum Testen aus verspotten kann. Vergessen Sie auch nicht, PerWebRequestLifestyleModule in Ihrer web.config zu registrieren.

+0

Dank Mauricio, das ist etwas, was ich gesucht habe. –

+0

Es lohnt sich zu wiederholen, dass Sie die Zeile hinzufügen müssen: container.AddFacility (); Das war ein kleiner Fehler für mich, dass ich dein Beispiel bei der ersten Lesung verpasst habe. Danke für die Hilfe, sehr nützlich! – ArtificialGold

+0

Ab Windsor 2.5 wird 'FactorySupportFacility' in diesem Fall nicht mehr benötigt. –

4

Ich lief gerade in genau das gleiche Problem, aber meine Lösung ist etwas anders.

Schnittstelle:

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

Umsetzung:

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

melde ich mich dann auf die IHttpContextProvider als Singleton in den Behälter. Ich bin immer noch ein Anfänger, wenn es um DI geht, also bin ich vielleicht etwas komplizierter, aber von dem, was ich verstehe, kann ich keine Singleton-Komponenten von PerWebRequest Lifestyle-Komponenten abhängig machen, was sinnvoll ist (aber das ist, was alle Beispiele tun). In meiner Lösung bin ich auf HttpContext.Current in einer isolierten Komponente angewiesen und ich bin nicht daran interessiert, das zu testen. Aber jede Komponente, die Zugriff auf den HTTP-Kontext benötigt, kann dies erreichen, indem sie von IHttpContextProvider abhängig ist und dies bei Bedarf leicht nachahmen kann.

Bin ich wirklich über komplizierter Dinge oder gibt es irgendwelche Vorbehalte in meiner Lösung?

+1

Ich sollte erwähnen, dass auf diese Weise keiner meiner Dienste, die vom aktuellen HTTP-Kontext abhängen, als PerWebRequest/Transient registriert werden muss. – Siewers

+0

Es funktioniert gut, danke. –