How to integrate with third-party IoC/DI frameworks in ASP. NET Core applications ?, Iocdi

Source: Internet
Author: User
Tags webhost

How to integrate with third-party IoC/DI frameworks in ASP. NET Core applications ?, Iocdi

We know that the entire ASP. NET Core is built on the DI framework with ServiceCollection/ServiceProvider as the Core. It even provides extensions that allow us to integrate with a third-party DI framework. Readers who know this well should be very clear. Integration with third-party DI frameworks can be achieved by returning a ServiceProvider in the ConfigureServices method that defines the Startup type. But is it so simple?

1. ServiceProvider returned by the ConfigureServices method seems useless !?

We can use a simple example to illustrate this problem. We first define the following MyServiceProvider, which is actually an encapsulation of another ServiceProvider. For simplicity, we use a dictionary to store the ing between service interfaces and implementation types. This relationship can be registered by calling the Registe method. In the GetService method of the service instance, if the service type provided has been registered, we will create and return the corresponding instance object, otherwise we will use the encapsulated ServiceProvider to provide the service. To ensure that the service instance can be recycled normally, if the service type implements the IDisposable interface, we will add it to the set indicated by field _ disposables. When the Dispose method of MyServiceProvider is called, The Dispose method of these service instances is called.

   1: public class MyServiceProvider : IServiceProvider, IDisposable
   2: {
   3:     private IServiceProvider        _innerServiceProvider;
   4:     private Dictionary<Type, Type>  _services;
   5:     private List<IDisposable>       _disposables;
   6:  
   7:     public MyServiceProvider(IServiceProvider innerServiceProvider)
   8:     {
   9:         _innerServiceProvider  = innerServiceProvider;
  10:         this._services         = new Dictionary<Type, Type>();
  11:         _disposables           = new List<IDisposable>();
  12:     }
  13:  
  14:  
  15:     public MyServiceProvider Register<TFrom, TTo>() where TTo: TFrom, new()
  16:     {
  17:         _services[typeof(TFrom)] = typeof(TTo);
  18:         return this;
  19:     }
  20:  
  21:     public object GetService(Type serviceType)
  22:     {
  23:         Type implementation;
  24:         if (_services.TryGetValue(serviceType, out implementation))
  25:         {
  26:             object service = Activator.CreateInstance(implementation);
  27:             IDisposable disposbale = service as IDisposable;
  28:             if (null != disposbale)
  29:             {
  30:                 _disposables.Add(disposbale);
  31:             }
  32:             return service;
  33:         }
  34:         return _innerServiceProvider.GetService(serviceType);
  35:     }
  36:  
  37:     public void Dispose()
  38:     {
  39:         (_innerServiceProvider as IDisposable)?.Dispose();
  40:         foreach (var it in _disposables)
  41:         {
  42:             it.Dispose();
  43:         }
  44:         _disposables.Clear();
  45:     }
  46: }

We use MyServiceProvider in an ASP. NET Core application as follows. In the following code snippet, In the registered Starup type, we asked the ConfigureServices method to return a MyServiceProvider object. The ing between IFoobar of the Service Interface and Foobar of the implementation type is registered on this MyServiceProvider object. When processing a request, we use the RequestServices attribute of the current HttpContext object to obtain the ServiceProvider that provides services for request processing, and try to use it to obtain the registered IFoobar service.

   1: public class Program
   2: {
   3:     public static void Main(string[] args)
   4:     {
   5:         new WebHostBuilder()
   6:             .UseKestrel()
   7:             .UseStartup<Startup>()
   8:             .Build()
   9:             .Run();
  10:     }
  11: }
  12:  
  13: public class Startup
  14: {
  15:     public IServiceProvider ConfigureServices(IServiceCollection services)
  16:     {
  17:         return new MyServiceProvider(services.BuildServiceProvider())
  18:             .Register<IFoobar, Foobar>();
  19:     }
  20:  
  21:     public void Configure(IApplicationBuilder app)
  22:     {
  23:         app.UseDeveloperExceptionPage()
  24:             .Run(async context => await context.Response.WriteAsync(context.RequestServices.GetRequiredService<IFoobar>().GetType().Name));
  25:     }
  26: }
  27: public interface IFoobar { }
  28: public class Foobar : IFoobar { }

The whole application is so simple that it seems there is no problem, but the following error occurs when we start the application and access the application using a browser. The error message indicates that the Service Interface IFoobar has not been registered.

Ii. Why?

We have registered the ing between IFoobar and Foobar in the returned ServiceProvider. Why does the ServiceProvider returned by RequestServices say the service has not been registered yet? The only explanation is that the ServiceProvider returned by the ConfigureServices method is not the same as the ServiceProvider returned by RequestServices in HttpContext. In fact, they are not the same object.

I have mentioned in "Speaking of two different serviceproviders" that the ServiceProvider returned by the ConfigureServices method serves as the ServiceProvider of WebHost. For each request received, webHost will create a new ServiceProvider based on this ServiceProvider as the RequestServices attribute of HttpContext. The two serviceproviders have parent-child management. As an example, if the ServiceProvider returned by RequestServices is created based on the ServiceProvider returned by the ConfigureServices method, it should also be able to identify the registered service type IFoobar. Why is there still an error?

To understand this problem, you need to know how this so-called "sub-ServiceProvider" is created, which involves the concept of servicecontrol. To put it simply, serviceserver encapsulates a ServiceProvider. The former determines the lifecycle of the latter. ServiceScopeFactory creates ServiceScopeFactory, which registers as a service to the "parent ServiceProvider. When "parent ServiceProvider" needs to create a "sub-ServiceProvider", it will call the GetService method to obtain the ServiceScopeFactory object (the service interface used is IServiceScopeFactory), and use the latter to create a servicelist, the ServiceProvider provided by this servicelist is the "sub-ServiceProvider" returned ".

However, for our MyServiceProvider object, when we call its GetService method to obtain the ServiceScopeFactory object, we actually get the ServiceScopeFactory associated with the encapsulated SerivceProvider, the "sub-ServiceProvider" created naturally has nothing to do with MyServiceProvider.

3. How can this problem be solved?

Now that we know the root cause of the problem, we naturally have a solution. The solution is not complicated. We only need the GetService method of MyServiceProvider to return ServiceScopeFactory that reflects its own service registration. For this reason, we define the next servicelist and the corresponding ServiceScopeFactory.

   1: internal class ServiceScope : IServiceScope
   2: {
   3:     private MyServiceProvider _serviceProvider;
   4:  
   5:     public ServiceScope(IServiceScope innserServiceScope, Dictionary<Type, Type> services)
   6:     {
   7:         _serviceProvider = new MyServiceProvider(innserServiceScope.ServiceProvider, services);
   8:     }
   9:     public IServiceProvider ServiceProvider
  10:     {
  11:         get { return _serviceProvider; }
  12:     }
  13:  
  14:     public void Dispose()
  15:     {
  16:         _serviceProvider.Dispose();
  17:     }
  18: }
  19:  
  20: internal class ServiceScopeFactory : IServiceScopeFactory
  21: {
  22:     private IServiceScopeFactory _innerServiceFactory;
  23:     private Dictionary<Type, Type> _services;
  24:  
  25:     public ServiceScopeFactory(IServiceScopeFactory innerServiceFactory, Dictionary<Type, Type> services)
  26:     {
  27:         _innerServiceFactory = innerServiceFactory;
  28:         _services = services;
  29:     }
  30:     public IServiceScope CreateScope()
  31:     {
  32:         return new ServiceScope(_innerServiceFactory.CreateScope(), _services);
  33:     }
  34: }

In addition, we added a constructor for MyServiceProvider, And the GetService method also added the corresponding code for IServiceScopeFactory.

   1: public class MyServiceProvider : IServiceProvider, IDisposable
   2: {
   3:     public MyServiceProvider(IServiceProvider innerServiceProvider, Dictionary<Type, Type> services)
   4:     {
   5:         _innerServiceProvider = innerServiceProvider;
   6:         _services = services;
   7:         _disposables = new List<IDisposable>();
   8:     }
   9:  
  10:     public object GetService(Type serviceType)
  11:     {
  12:         if (serviceType == typeof(IServiceScopeFactory))
  13:         {
  14:             IServiceScopeFactory innerServiceScopeFactory = _innerServiceProvider.GetRequiredService<IServiceScopeFactory>();
  15:             return new ServiceScopeFactory(innerServiceScopeFactory, _services);
  16:         }
  17:         ...        
  18:     }
  19:     ...
  20: }

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.