📜 ⬆️ ⬇️

Using a single IoC Container as part of an HTTP request between the Web API and the OWIN Middleware

The purpose of this article is to find a working solution that allows you to have a single dependency container (IoC container) throughout the entire life cycle of a request, to control its creation and destruction.

image

This may be necessary if the web application must have transactivity (and in my opinion, any web application must have it, that is, apply changes (for example, in the database) only if the request is successfully processed and cancel them if at any of the stages an error occurred, indicating an incorrect result and uncontrollable consequences) ( github source code ).
')

Theory


Web API 2 projects are configured using the IAppBuilder OWIN interface, which is designed to help build an incoming request pipeline.

The image above shows the life cycle of the request — it goes through all the components of the chain, then enters the Web API (which is also a separate component) and comes back, forming or decorating the response from the server.

In order to have a single dependency container, we will need to create it explicitly at the beginning of processing the request and destroy it upon completion:

  1. Begin processing the request;
  2. Container creation;
  3. Use of the container in Middleware;
  4. Using the container in the Web API;
  5. Container destruction;
  6. Completion of request processing.

To do this, we just need to configure the container, register it in the Web API (using a DependencyResolver):

// Configure our parent container var container = UnityConfig.GetConfiguredContainer(); // Pass our parent container to HttpConfiguration (Web API) var config = new HttpConfiguration { DependencyResolver = new UnityDependencyResolver(container) }; WebApiConfig.Register(config); 

Write your own Middleware, which will create a child container:

 public class UnityContainerPerRequestMiddleware : OwinMiddleware { public UnityContainerPerRequestMiddleware(OwinMiddleware next, IUnityContainer container) : base(next) { _next = next; _container = container; } public override async Task Invoke(IOwinContext context) { // Create child container (whose parent is global container) var childContainer = _container.CreateChildContainer(); // Set created container to owinContext // (to become available at other places using OwinContext.Get<IUnityContainer>(key)) context.Set(HttpApplicationKey.OwinPerRequestUnityContainerKey, childContainer); await _next.Invoke(context); // Dispose container that would dispose each of container's registered service childContainer.Dispose(); } private readonly OwinMiddleware _next; private readonly IUnityContainer _container; } 

And use it in other Middlewares (in my implementation, I save the container in the global OwinContext using the context.Set, which is passed to each of the following middleware and get it using the context.Get):

 public class CustomMiddleware : OwinMiddleware { public CustomMiddleware(OwinMiddleware next) : base(next) { _next = next; } public override async Task Invoke(IOwinContext context) { // Get container that we set to OwinContext using common key var container = context.Get<IUnityContainer>( HttpApplicationKey.OwinPerRequestUnityContainerKey); // Resolve registered services var sameInARequest = container.Resolve<SameInARequest>(); await _next.Invoke(context); } private readonly OwinMiddleware _next; } 

This could be finished if it were not for one BUT .

Problem


The Middleware Web API has its own request processing loop inside itself, which looks like this:

  1. The request goes to HttpServer to start processing the HttpRequestMessage and transfer it to the routing system;
  2. HttpRoutingDispatcher retrieves data from the request and, using the Route table, determines the controller responsible for processing;
  3. In HttpControllerDispatcher, a previously defined controller is created and the request processing method is called to form the HttpResponseMessage.

The following line in the DefaultHttpControllerActivator is responsible for creating the controller:

 IHttpController instance = (IHttpController)request.GetDependencyScope().GetService(controllerType); 

The main content of the GetDependencyScope method:

 public static IDependencyScope GetDependencyScope(this HttpRequestMessage request) { // … IDependencyResolver dependencyResolver = request.GetConfiguration().DependencyResolver; result = dependencyResolver.BeginScope(); request.Properties[HttpPropertyKeys.DependencyScope] = result; request.RegisterForDispose(result); return result; } 

It shows that the Web API requests the DependencyResolver, which we registered for it in HttpConfiguration and using dependencyResolver.BeginScope () creates a child container, within which an instance of the controller responsible for processing the request will already be created.

For us, this means the following: the container that we use in our Middleware and in the Web API is not the same — moreover, they are on the same nesting level, where the global container is their common parent, i.e. :

  1. Global container;
    1. Child container created in UnityContainerPerRequestMiddleware;
    2. Child container created in the Web API.

For the Web API, this seems quite logical if it is the only place to handle the request - the container is created first and destroyed at the end (this is exactly what we are trying to achieve).

However, at the moment the Web API is only one of the links in the pipeline, which means that creating our own container will have to be abandoned - our task is to redefine this behavior and specify the container within which the Web API is required to create controllers and Resolve'it dependencies.

Decision


To solve the above problem, we can implement our own IHttpControllerActivator, in the Create method of which we will receive the container created earlier and within the framework of it Resolve'it dependencies:

 public class ControllerActivator : IHttpControllerActivator { public IHttpController Create( HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType ) { // Get container that we set to OwinContext using common key var container = request.GetOwinContext().Get<IUnityContainer>( HttpApplicationKey.OwinPerRequestUnityContainerKey); // Resolve requested IHttpController using current container // prevent DefaultControllerActivator's behaviour of creating child containers var controller = (IHttpController)container.Resolve(controllerType); // Dispose container that would dispose each of container's registered service // Two ways of disposing container: // 1. At UnityContainerPerRequestMiddleware, after owin pipeline finished (WebAPI is just a part of pipeline) // 2. Here, after web api pipeline finished (if you do not use container at other middlewares) (uncomment next line) // request.RegisterForDispose(new Release(() => container.Dispose())); return controller; } } 

In order to use it in the Web API, all we have to do is replace the standard HttpControllerActivator in the configuration:

 var config = new HttpConfiguration { DependencyResolver = new UnityDependencyResolver(container) }; // Use our own IHttpControllerActivator implementation // (to prevent DefaultControllerActivator's behaviour of creating child containers per request) config.Services.Replace(typeof(IHttpControllerActivator), new ControllerActivator()); WebApiConfig.Register(config); 

Thus, we get the following mechanism for working with our single container:

1. Start processing the request;

2. Creating a child container from the global;

 var childContainer = _container.CreateChildContainer(); 

3. Assigning a container in OwinContext:

 context.Set(HttpApplicationKey.OwinPerRequestUnityContainerKey, childContainer); 

4. Use of the container in other Middlewares;

 var container = context.Get<IUnityContainer>(HttpApplicationKey.OwinPerRequestUnityContainerKey); 

5. Using the container in the Web API;

5.1. Getting the controller from OwinContext:

 var container = request.GetOwinContext().Get<IUnityContainer>(HttpApplicationKey.OwinPerRequestUnityContainerKey); 

5.2. Creating a controller based on this container:

 var controller = (IHttpController)container.Resolve(controllerType); 

6. Destruction of the container:

 childContainer.Dispose(); 

7. Completion of the request.

Result


Configuring dependencies in accordance with their life cycles we need:

 public static void RegisterTypes(IUnityContainer container) { // ContainerControlledLifetimeManager - singleton's lifetime container.RegisterType<IAlwaysTheSame, AlwaysTheSame>(new ContainerControlledLifetimeManager()); container.RegisterType<IAlwaysTheSame, AlwaysTheSame>(new ContainerControlledLifetimeManager()); // HierarchicalLifetimeManager - container's lifetime container.RegisterType<ISameInARequest, SameInARequest>(new HierarchicalLifetimeManager()); // TransientLifetimeManager (RegisterType's default) - no lifetime container.RegisterType<IAlwaysDifferent, AlwaysDifferent>(new TransientLifetimeManager()); } 

  1. ContainerControlledLifetimeManager - creating a single instance within the application;
  2. HierarchicalLifetimeManager - creating a single instance within the container (where we have achieved that the container is one within the HTTP request);
  3. TransientLifetimeManager - creating an instance for each call (Resolve).

image

In the image above, GetHashCode'y dependencies are displayed in the context of several HTTP requests, where:

  1. AlwaysTheSame - singleton object within the application;
  2. SameInARequest - singleton object within the request;
  3. AlwaysDifferent - a new instance for each Resolve.

"Sources are available on github .

Materials:
1. Conveyor in ASP.NET Web API

Source: https://habr.com/ru/post/311256/


All Articles