Search code examples
asp.net-mvcentity-frameworkasp.net-web-apiinversion-of-controlsimple-injector

Proper SimpleInjector configuration for WebApi and UnitOfWork Pattern


I have read through the SimpleInjector documentation a few times. But have a few questions.

Context:

  • 3 tier app (presentation (mvc + api controllers), service (business logic), data (repositories, entities, etc)
  • Unit of Work is a thin wrapper around EF's DbContext
  • my DbContext and Unit of Work are registered PerWebRequest, using RegisterWebApiRequest causes an exception, because the Unit of Work is used outside of Web API requests.
  • my MVC and Api controllers registered using RegisterWebApiControllers(GlobalConfiguration.Configuration) and RegisterMvcControllers(Assembly.GetExecutingAssembly())
  • Each controller has one or more services injected into it.
  • Each service has one or more repositories injected into it.
  • A service may also have another service injected into it.
  • I want the same Unit Of Work/DbContext to exist in all my services/repositories.

Questions:

  • Because I am using services in my MVC controllers as well as API controllers; does that mean I can not use RegisterWebApiRequest in place of RegisterPerWebRequest?

  • none of my services, repositories, etc, maintain any state, I would get the same functionality using PerWebRequest as Transient; is there any advantage to using PerWebRequest over Transient?


Solution

  • Please read the following q/a: How to configure simple injector container and lifestylse in a MVC web app with WebAPI, WCF, SignalR and Background Tasks. The answer explains that:

    1. Putting your Web API in the same project as your MVC controllers is a bad idea from an architectural perspective.
    2. But if you want to do this, you can use the WebRequestLifestyle in both type of applications. The WebApiRequestLifestyle is meant as lifestyle that works for Web API for both IIS and self-hosted environments, but since you placed the Web API controllers in the same project, you are clearly only interested in IIS-hosted; in that case the WebRequestLifestyle will do just fine.

    Because I am using services in my MVC controllers as well as API controllers; does that mean I can not use RegisterWebApiRequest in place of RegisterPerWebRequest?

    Both lifestyles use a different way of caching. The WebRequestLifestyle uses the HttpContext.Current.Items dictionary to store its SimpleInjector.Scope instance, while the WebApiRequestLifestyle uses the CallContext class to store the Scope during the lifetime of a single asynchronous operation.

    Just as the WebRequestLifestyle can be used while resolving Web API controllers, you can use the WebApiRequestLifestyle (or the underlying ExecutionContextScopeLifestyle) for MVC controllers as well. But if you want this, you will create your own IDependencyResolver implementation for MVC that will explicitly start and end an ExecutionContextScope. The absense of a Scope stored in the CallContext is the reason resolving MVC controllers fails when registering services using the WebApiRequestLifestyle. But while it's possible to use the WebApiRequestLifestyle in MVC, the otherway around is much easier, since no custom code is required.

    none of my services, repositories, etc, maintain any state, I would get the same functionality using PerWebRequest as Transient; is there any advantage to using PerWebRequest over Transient?

    If services don't have state, it doesn't matter what lifestyle they have. The only restriction is that they have dependencies that have a lifestyle that is equal to or longer than their own. Violating this restriction is called Captive Dependencies and can cause all kinds of trouble. Because captive dependencies are bad, Simple Injector v3 checks and prevents this for you.

    Although you can probably make all objects in your configuration scoped (non-transient), making them transient is usually easier to configure, and might result in better performance (although you will probably never notice the difference in real life).