views:

700

answers:

3

I have several dependency injection services which are dependent on stuff like HTTP context. Right now I'm configuring them as singletons the Windsor container in the Application_Start handler, which is obviously a problem for such services.

What is the best way to handle this? I'm considering making them transient and then releasing them after each HTTP request. But what is the best way/place to inject the HTTP context into them? Controller factory or somewhere else?

+1  A: 

With Castle Windsor you can use the PerWebRequest lifetime - that should fit pretty well with your requirements.

That means you can just inject the HTTP stuff into your services, and the container will take care of the proper lifetime management. However, this requires you to also register all these services (and all consumers of those services and so on) as PerWebRequest (or Transient) because if you register them as Singletons, they will hold on to stale (and possibly disposed) contexts.

Mark Seemann
Mark, thanks for the info - I didn't know about PerWebRequest. I'll check it out.
Igor Brejc
Mark, I've looked into PerWebRequest, but I still don't see how services can obtain HttpContext. When I try to register an instance of HttpContextBase it in the container myself, it fails after the second request (since an instance was already registered in the previous request). I couldn't find anything on Google so far...
Igor Brejc
I may have misunderstood what you are trying to do, but you can't use the HttpContext from Application_Start because at this point there *is* no HttpContext (PerWebRequest or no PerWebRequest). Now that I think about it, it makes no sense to attempt to control the lifetime of the HttpContext from the DI Container, since this lifetime is already being managed by the ASP.NET MVC framework. What you *can* do is to hook into a custom IControllerFactory and grab the HttpContext served to you at that point, and then use a factory method to wire up everything else that depends on it.
Mark Seemann
Mark: a colleague of mine suggested accessing HttpContext.Current property, without injection or factory methods. It's not very DI-friendly, but it looks like it works. I've registered these services as PerWebRequest, just to be on the safe side.
Igor Brejc
+8  A: 

Just like Mark said, you need to register these http-dependent services either as PerWebRequest or Transient. Here's a sample that shows how to register and inject a HttpRequest or 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)));  
}

By using HttpRequestBase instead of HttpRequest you can easily mock it out for testing. Also, don't forget to register PerWebRequestLifestyleModule in your web.config.

Mauricio Scheffer
Thanks Mauricio, this is something I was looking for.
Igor Brejc
Might be worth reiterating that you need to add the line:container.AddFacility<FactorySupportFacility>();This was a small gotcha for me that I missed off your example on first reading. Thanks for the help, very useful!
ArtificialGold
A: 

Thanks Mauricio very much, this is what i am finding ...

Nguyen Viet Hien