Search code examples
c#asp.net-mvcdependency-injectionsimple-injector

Migrating from Windsor to Simple Injector, HttpContext.Current.Session is null


I'm currently trying to migrate from Castle Windsor to Simple Injector.

Within a Service am I trying to inject a HttpSessionStateBase as illustrated on below constructor for the Service:

public UserSession(IResourceRepository resourceRepository, IUser user, 
    IRoleResolver roleResolver, IApproverRepository approverRepository,
    IActiveDirectory activeDirectory, HttpSessionStateBase httpSession, 
    Settings appSettings)
{
    _resourceRepository = resourceRepository;
    _user = user;
    _roleResolver = roleResolver;
    _approverRepository = approverRepository;
    _activeDirectory = activeDirectory;
    _httpSession = httpSession;
    _allowedUsernamePostfixes =  appSettings.AuthenticationAllowedUsernamePostfixes;
}

To do this with Windsor can I use a FactoryMethod to inject HttpSessionStateBase as a new HttpSessionStateWrapper.

private static IWindsorContainer BuildWindsorContainer()
{
    var container = new WindsorContainer();
    container.Kernel.Resolver.AddSubResolver(
        new CollectionResolver(container.Kernel, true));

    container.Register(
        Component.For<IWindsorContainer>().Instance(container)
        );

    container.Install(FromAssembly.This());

    container.Register(
        Component.For<HttpRequestBase>()
            .UsingFactoryMethod(() => new HttpRequestWrapper(HttpContext.Current.Request))
            .LifestyleTransient(),
        Component.For<HttpSessionStateBase>()
            .UsingFactoryMethod(() => new HttpSessionStateWrapper(HttpContext.Current.Session))
            .LifestyleTransient(),


    return container;
}

This Method will work when being called in the Application_Start method of the ASP.NET MVC 4 project inside the Global.asax.cs file.

I have now tried to convert it to Simple Inject by placing the two Windsor FactoryMethods as below Simple Inject code:

container.Register<HttpRequestBase>(
    () => new HttpRequestWrapper(HttpContext.Current.Request), 
    Lifestyle.Transient);

container.Register<HttpSessionStateBase>(
    () => new HttpSessionStateWrapper(HttpContext.Current.Session), 
    Lifestyle.Transient);

The problem is that when I call this method in the Application_Start method then the HttpContext.Current.Session will be null when Simple Inject call the container verify method.

Any one got a solution for this?


Solution

  • The problem is that when I call this method in the Application_Start method then the HttpContext.Current.Session will be null when Simple Inject call the container verify method.

    With Castle Windsor you would have had the same problem if it contained verification functionality like Simple Injector does. If you remove the call to container.Verify() you get the behavior equal to Castle Winsdor's, i.e. you don't get any verification.

    Obviously, this isn't ideal, and this might be one of the reasons you wish to migrate to Simple Injector.

    The problem you are having boils down to a problem of constructing application components using runtime state, which is a code smell as expressed here. So in an ideal situation, you should mitigate this and ensure that HttpContext.Current is not called suring object construction, but only after object construction.

    Although you should really strive to do this, I can understand that this could be quite an undertaking to refactor the application, and is something that takes a lot more time than just changing DI containers. Still it is something you should keep in mind for the (near) future.

    That said, a practical (and temporal) work around is to create fake HttpRequest and HttpSession objects during initialization, as shown below:

    container.Register<HttpRequestBase>(
        () => HttpContext.Current != null
            ? new HttpRequestWrapper(HttpContext.Current.Request)
            : new HttpRequestWrapper(new HttpRequest("", "http://fake", "")), 
        Lifestyle.Scoped);
    
    container.Register<HttpSessionStateBase>(
        () => HttpContext.Current != null
            ? new HttpSessionStateWrapper(HttpContext.Current.Session)
            : (HttpSessionState)typeof(HttpSessionState)
                .GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic)
                .First()
                .Invoke(new object[] { null }),
        Lifestyle.Scoped);
    

    This is quite ugly, especially the creation of HttpSessionState (that has an internal constructor), but it keeps the rest of your configuration verifiable.