Simple Injector: how to inject HttpContext?

2019-03-20 08:16发布

问题:

I have started using Simple Injector as my DI container (mostly for performance reason: if somebody has suggestions, please let me know) but some of the classes I wrote use HttpContextBase as constructor parameter. I have resolved for now removing it from the constructor and creating a Property, something like this:

    public HttpContextBase HttpContext
    {
        get
        {
            if (null == _httpContext)
                _httpContext = new HttpContextWrapper(System.Web.HttpContext.Current);
            return _httpContext;
        }
        set
        {
            _httpContext = value;
        }
    }

but I don't like this solution... any advices?

回答1:

You should always favor constructor injection over anything else. This is almost always possible. You can register your HttpContextBase as follows:

container.Register<HttpContextBase>(() =>
    new HttpContextWrapper(HttpContext.Current), 
    Lifestyle.Scoped);

This can cause a problem when calling Verify(), since during application startup HttpContext.Current is null, and HttpContextWrapper does not allow passing null into the constructor.

It's always good to try to keep your configuration verifiable, and you can change that registration to the following:

container.Register<HttpContextBase>(() =>
{
    var context = HttpContext.Current;
    if (context == null && container.IsVerifying) return new FakeHttpContext();
    return new HttpContextWrapper(context);
},
    Lifestyle.Scoped);

FakeHttpContext is an empty HttpContextBase implementation to prevent returning null in case the container is verifying. The FakeHttpContext is simply this:

public class FakeHttpContext : HttpContextBase { }

Do note however that HttpContext is runtime data and injecting runtime data into components during construction is an anti-pattern. Instead of injecting the HttpContext or any abstraction over it into your components, you should create an application-specific abstraction that provides the consumer with what it actually needs (for instance a user identity or tenant id). The implementation of this abstraction can simply call HttpContext.Current internally which completely prevents the need for HttpContext to be injected.