Using Unity to Register MVC Types That Don't Exist (Yet)

Viewed 368

The UnityMvcActivator is called right out of the gate when starting my MVC application, and it instantiates, configures, and sets the container to the DependencyResolver:

DependencyResolver.SetResolver(new UnityDependencyResolver(UnityConfig.Container));

Which immediately registers all the types via:

public static void RegisterTypes(IUnityContainer container)
{
    container.RegisterMvcComponents();
}

...but I'm trying to register types that aren't created until a little further down the road:

public static IUnityContainer RegisterMvcComponents(this IUnityContainer container)
{
    var lifetimeManager = new HierarchicalLifetimeManager();

    container.RegisterInstance<HttpSessionStateBase>(
        new HttpSessionStateWrapper(HttpContext.Current.Session), lifetimeManager);
    container.RegisterInstance<HttpContextBase>(
        new HttpContextWrapper(HttpContext.Current), lifetimeManager);
    container.RegisterInstance<HttpServerUtilityBase>(
        new HttpServerUtilityWrapper(HttpContext.Current.Server), lifetimeManager);
    container.RegisterInstance(HttpContext.Current.User.Identity, lifetimeManager);

    return container;
}

I can't get the container back from the DependencyResolver when I finally make it to my OWIN Startup class - which is where all the other initialization is taking place - so how can I register these types?

EDIT:

Thinking I was clever, I tried adding some post-start action to the activator by adding this assembly directive and moving my configuration method call to the newly created method:

[assembly: WebActivatorEx.PostApplicationStartMethod(
           typeof(CCCS.Admin.Web.Ui.UnityMvcActivator), 
           nameof(CCCS.Admin.Web.Ui.UnityMvcActivator.PostStart))]

public static void PostStart() => UnityConfig.Container.RegisterMvcComponents();

... and that got me halfway, but the User and Session still aren't available.

1 Answers

This is more of an XY problem related to your design, as all the HttpContext related members wont be available at startup.

You would better off creating abstractions to defer the access to those implementation concerns.

public interface IHttpContextAccessor {
    HttpContextBase HttpContext { get; }
}

public class HttpContextProvider : IHttpContextAccessor {
    public virtual HttpContextBase HttpContext {
        get {
            return new HttpContextWrapper(HttpContext.Current);
        }
    }
}

Now all those registrations can be replaced with the one abstraction which would provide access to all the other related types.

public static IUnityContainer RegisterMvcComponents(this IUnityContainer container) {
    var lifetimeManager = new HierarchicalLifetimeManager();

    container.RegisterType<IHttpContextAccessor, HttpContextProvider>(lifetimeManager);

    return container;
}

Note that the container should ideally only be accessed in the composition root of the application and not passed around as a dependency. That is seen as a code smell and an indicator that the design should be reviewed and refactored if possible.

When access is needed to HttpContext related members it is now a matter of injecting the accessor

private readonly IHttpContextAccessor accessor;

public MyDependent(IHttpContextAccessor accessor) {
    this.accessor = accessor;
}

public void SomeMethodAccessedInAnAction() {
    var context = access.HttpContext; // HttpContextBase
    var session = context.Session; // HttpSessionStateBase
    var server = context.Server; // HttpServerUtilityBase
    var user = context.User; // IPrincipal
    //...
}
Related