Search code examples
c#entity-frameworkdependency-injectionsimple-injector

SimpleInjector Request Scope from Service layer


I have a multi-tier application where my implementation of my DbContext and UnitOfWork is lying in my service layer. This layer does not have a reference to System.Web.

Together with the implementations lies my CompositionRoot class which is referred from my UI layer, using extensions, to initialize all my injections.

But my DbContext and UnitOfWork requires a request scope, and I am not able to set this like the example below, since I do not have access to HttpContext or WebRequestLifestyle.

Should I move this RegisterEntityFramework(...) extension to my UI layer in order to use request scope, or should I just reference the System.Web in my service layer - or another approach?

I am new to Dependency Injection so maybe I just being too paranoid about what the best practices are in such senario.

ServiceLayer/EntityFramework/CompositionRoot.cs

public static class CompositionRoot
{
    public static void RegisterEntityFramework(this Container container)
    {
        var lifestyle = Lifestyle.CreateHybrid(
            lifestyleSelector: () => HttpContext.Current != null, // HttpContext not available
            trueLifestyle: new WebRequestLifestyle(),
            falseLifestyle: new LifetimeScopeLifestyle()
        );

        var contextRegistration = lifestyle.CreateRegistration<EntityDbContext, EntityDbContext>(container);
        container.AddRegistration(typeof(EntityDbContext), contextRegistration);
        container.AddRegistration(typeof(IUnitOfWork), contextRegistration);
    }
}

UILayer/App_Start/CompositionRoot.cs

public static class RootComposition
{
    public static void Configure()
    {
        var container = new Container();
        container.RegisterEntityFramework(); // extension

        container.RegisterMvcControllers(Assembly.GetExecutingAssembly());
        container.RegisterMvcAttributeFilterProvider();

        container.Verify();

        DependencyResolver.SetResolver(new SimpleInjectorDependencyResolver(container));
        GlobalConfiguration.Configuration.DependencyResolver = new WebApiDependencyResolver(container);
    }
}

Solution

  • The composition root is part of the startup path of the application. In your case that's your web application. The Composition Root references all assemblies in the system. The only reason to move business layer specific registrations out of this part is when the business layer is reused by multiple end applications (both an MVC application and a WCF web service for instance).

    If you move everything to your web application, the registration of your DbContext will be simple:

    var scopedLifestyle = new WebRequestLifestyle();
    var contextRegistration =
        scopedLifestyle.CreateRegistration<EntityDbContext, EntityDbContext>(container);
    container.AddRegistration(typeof(EntityDbContext), contextRegistration);
    container.AddRegistration(typeof(IUnitOfWork), contextRegistration);
    

    If you have multiple applications and need to reuse the registration logic of the business layer, you don't know exactly what lifestyle to use. In that case you can supply the business layer composition root with the ScopedLifestyle:

    public static void RegisterEntityFramework(this Container container, 
        ScopedLifestyle lifestyle)
    {
        var contextRegistration =
            lifestyle.CreateRegistration<EntityDbContext, EntityDbContext>(container);
        container.AddRegistration(typeof(EntityDbContext), contextRegistration);
        container.AddRegistration(typeof(IUnitOfWork), contextRegistration);
    }
    

    You can call this from within your Application_Start as follows:

    container.RegisterEntityFramework(new WebRequestLifestyle());