【.NET Core框架】依赖注入(DependencyInjection)

简介

简单说,就是将对象的创建和销毁工作交给DI容器来进行,调用方只需要接收注入的对象实例即可。

涉及到的nuget包:

Microsoft.Extensions.DependencyInjection.Abstractions:抽象定义;
Microsoft.Extensions.DependencyInjection:依赖容器实现;

注册服务

  • Add
  • TryAdd{Lifetime}
    当你将同样的服务注册了多次时,如:
services.AddSingleton<IMyService, MyService>();
services.AddSingleton<IMyService, MyService>();

那么当使用IEnumerable<{Service}>解析服务时,就会产生多个MyService实例的副本。
为此,框架提供了TryAdd{Lifetime}扩展方法,当DI容器中已存在指定类型的服务时,则不进行任何操作;反之,则将该服务注入到DI容器中。

TryAdd:通过参数ServiceDescriptor将服务类型、实现类型、生命周期等信息传入进去
TryAddTransient:对应AddTransient
TryAddScoped:对应AddScoped
TryAddSingleton:对应AddSingleton
TryAddEnumerable:这个和TryAdd的区别是,TryAdd仅根据服务类型来判断是否要进行注册,而TryAddEnumerable则是根据服务类型和实现类型一同进行判断是否要进行注册,常常用于注册同一服务类型的多个不同实现。举个例子吧:

// 注册了 IMyService - MyService1
services.TryAddEnumerable(ServiceDescriptor.Singleton<IMyService, MyService1>());
// 注册了 IMyService - MyService2
services.TryAddEnumerable(ServiceDescriptor.Singleton<IMyService, MyService2>());
// 未进行任何操作,因为 IMyService - MyService1 在上面已经注册了
services.TryAddEnumerable(ServiceDescriptor.Singleton<IMyService, MyService1>());

DependencyInjection源码

ServiceDescriptor

public class ServiceDescriptor
{
    public ServiceLifetime Lifetime { get; }
    public Type ServiceType { get; }
    public Type ImplementationType { get; }
    public object ImplementationInstance { get; }
    public Func<IServiceProvider, object> ImplementationFactory { get; }
}

ServiceDescriptor包含接口类型、服务实例类型、服务实例对象、服务实例对象工厂和生命周期
ServiceLifetime是枚举类型:

 public enum ServiceLifetime
 {
     Singleton,
     Scoped,
     Transient
 }

当我们调用ServiceProvider的GetService方法的时候,它会根据我们提供的服务类型找到对应的ServiceDecriptor对象。如果该ServiceDecriptor对象的ImplementationInstance属性返回一个具体的对象,该对象将直接用作被提供的服务实例。如果ServiceDecriptor对象的ImplementationFactory返回一个具体的委托,该委托对象将直接用作创建服务实例的工厂。如果这两个属性均为Null,ServiceProvider才会根据ImplementationType属性返回的类型调用相应的构造函数创建被提供的服务实例。

ServiceCollection

ServiceCollection就是ServiceDescriptor的集合,看接口:

public interface IServiceCollection : IList<ServiceDescriptor>{}

ServiceCollection源码:

public class ServiceCollection : IServiceCollection
{
     //      ServiceDescriptor缓存集合,ServiceDescriptor对象缓存到这个属性中
     private readonly List<ServiceDescriptor> _descriptors = new List<ServiceDescriptor>();
     
     //      注册到当前ServiceCollection对象中的ServiceDescriptor数量
     public int Count => _descriptors.Count;

     public bool IsReadOnly => false;

     //      设置索引器
     public ServiceDescriptor this[int index]
     {
          get=> _descriptors[index];
          set=> _descriptors[index] = value;
     }

     //      清空所有注册到此ServiceCollection上的ServiceDescriptor对象
     public void Clear() => _descriptors.Clear();

     //      查询此ServiceCollection是否包含指定ServiceDescriptor对象
     public bool Contains(ServiceDescriptor item)=> _descriptors.Contains(item);

     //  拷贝ServiceDescriptor
     public void CopyTo(ServiceDescriptor[] array, int arrayIndex) =>_descriptors.CopyTo(array, arrayIndex);

     //      从此ServiceCollection移除指定ServiceDescriptor
     public bool Remove(ServiceDescriptor item)=>_descriptors.Remove(item);

     //      获取此ServiceCollection的迭代器
     public IEnumerator<ServiceDescriptor> GetEnumerator()=> _descriptors.GetEnumerator();

     public int IndexOf(ServiceDescriptor item) => _descriptors.IndexOf(item);

     public void Insert(int index, ServiceDescriptor item) => _descriptors.Insert(index, item);

     public void RemoveAt(int index)=> _descriptors.RemoveAt(index);
}

ServiceCollectionServiceExtensions:ServiceCollection的扩展类

//		使用基类和派生类类型实例化ServiceDescriptor对象,然后进行缓存,
private static IServiceCollection Add(IServiceCollection collection,Type serviceType,Type implementationType,ServiceLifetime lifetime)
{
    var descriptor = new ServiceDescriptor(serviceType, implementationType, lifetime);
    collection.Add(descriptor);
    return collection;
}
//		使用基类型和工厂实例化ServiceDescriptor对象,然后进行缓存
private static IServiceCollection Add(IServiceCollection collection,Type serviceType,Func<IServiceProvider, object> implementationFactory,ServiceLifetime lifetime)
{
    var descriptor = new ServiceDescriptor(serviceType, implementationFactory, lifetime);
    collection.Add(descriptor);
    return collection;
}
//		使用基类型和具体实例对象实例化ServiceDescriptor对象,然后进行缓存
//		此方法只适用于Singleton生命周期
public static IServiceCollection AddSingleton(this IServiceCollection services,Type serviceType,object implementationInstance)
{
     var serviceDescriptor = new ServiceDescriptor(serviceType, implementationInstance);
     services.Add(serviceDescriptor);
     return services;
}

ServiceCollectionContainerBuilderExtensions:
这个扩展类是创建IServiceProvider的,在这个扩展类中只具有BuildServiceProvider()方法,这个方法也就是我们用来获取ServiceProvider类型,ServiceProvider是获取服务对象的类型

public static ServiceProvider BuildServiceProvider(this IServiceCollection services)
    //		使用默认的ServiceProviderOptions实例
     =>BuildServiceProvider(services, ServiceProviderOptions.Default);

public static ServiceProvider BuildServiceProvider(this IServiceCollection services, bool validateScopes)
      =>services.BuildServiceProvider(new ServiceProviderOptions { ValidateScopes = validateScopes });

 public static ServiceProvider BuildServiceProvider(this IServiceCollection services, ServiceProviderOptions options)
      => new ServiceProvider(services, options);

IServiceProvider(服务提供者)

public interface IServiceProvider
{
    object GetService(Type serviceType);
}

IServiceProvider接口的实现类是ServiceProvider,我们不能直接创建该对象,只能间接地通过调用IServiceCollection接口的扩展方法BuildServiceProvider得到它。
IServiceProvider除了GetService方法可以获取服务,还有以下扩展方法:

public static class ServiceProviderExtensions
{ 
    public static T GetService<T>(this IServiceProvider provider);
    public static object GetRequiredService(this IServiceProvider provider, Type serviceType);
    public static T GetRequiredService<T>(this IServiceProvider provider);
}

提供一个服务实例的集合:
如果我们在调用GetService方法的时候将服务类型指定为IEnumerable,那么返回的结果将会是一个集合对象。除此之外, 我们可以直接调用IServiceProvider如下两个扩展方法GetServeces达到相同的目的。在这种情况下,ServiceProvider将会利用所有与指定服务类型相匹配的ServiceDescriptor来提供具体的服务实例,这些均会作为返回的集合对象的元素。如果所有的ServiceDescriptor均与指定的服务类型不匹配,那么最终返回的是一个空的集合对象。

public static class ServiceProviderExtensions
{
    public static IEnumerable<T> GetServices<T>(this IServiceProvider provider);
    public static IEnumerable<object> GetServices(this IServiceProvider provider, Type serviceType);
}

依赖注入容器不仅是服务实例的提供者,同时还需要维护服务实例的生命周期

服务范围(ServiceScope)

ServiceScope为某个ServiceProvider对象圈定了一个“作用域”,枚举类型ServiceLifetime中的Scoped选项指的就是这么一个ServiceScope。在依赖注入的应用编程接口中,ServiceScope通过一个名为IServiceScope的接口来表示。
如下面的代码片段所示,继承自IDisposable接口的IServiceScope具有一个唯一的只读属性ServiceProvider返回确定这个服务范围边界的ServiceProvider。表示ServiceScope由它对应的工厂ServiceScopeFactory来创建,后者体现为具有如下定义的接口IServiceScopeFactory。
如下面的代码片段所示,IServiceProvider的扩展方法CreateScope正是利用提供的IServiceScopeFactory服务实例来创建作为服务范围的IServiceScope对象。

public interface IServiceScope : IDisposable
{
    IServiceProvider ServiceProvider { get; }
}
public interface IServiceScopeFactory
{
    IServiceScope CreateScope();
}
public static class ServiceProviderServiceExtensions
{
   public static IServiceScope CreateScope(this IServiceProvider provider) => provider.GetRequiredService<IServiceScopeFactory>().CreateScope();
}

若要充分理解ServiceScope和ServiceProvider之间的关系,我们需要简单了解一下ServiceProvider的层级结构。除了直接通过一个ServiceCollection对象创建一个独立的ServiceProvider对象之外,一个ServiceProvider还可以根据另一个ServiceProvider对象来创建,如果采用后一种创建方式,我们指定的ServiceProvider与创建的ServiceProvider将成为一种“父子”关系。

   1: internal class ServiceProvider : IServiceProvider, IDisposable
   2: {
   3:     private readonly ServiceProvider _root;
   4:     internal ServiceProvider(ServiceProvider parent)
   5:     {
   6:         _root = parent._root;
   7:     }
   8:     //其他成员
   9: }

虽然在ServiceProvider在创建过程中体现了ServiceProvider之间存在着一种树形化的层级结构,但是ServiceProvider对象本身并没有一个指向“父亲”的引用,它仅仅会保留针对根节点的引用。如上面的代码片段所示,针对根节点的引用体现为ServiceProvider类的字段_root。当我们根据作为“父亲”的ServiceProvider创建一个新的ServiceProvider的时候,父子均指向同一个“根”。
由于ServiceProvider自身是一个内部类型,我们不能采用调用构造函数的方式根据一个作为“父亲”的ServiceProvider创建另一个作为“儿子”的ServiceProvider,但是这个目的可以间接地通过创建ServiceScope的方式来完成。如下面的代码片段所示,我们首先创建一个独立的ServiceProvider并调用其GetService方法获得一个ServiceScopeFactory对象,然后调用后者的CreateScope方法创建一个新的ServiceScope,它的ServiceProvider就是前者的“儿子”。

   1: class Program
   2: {
   3:     static void Main(string[] args)
   4:     {
   5:         IServiceProvider serviceProvider1 = new ServiceCollection().BuildServiceProvider();
   6:         IServiceProvider serviceProvider2 = serviceProvider1.GetService<IServiceScopeFactory>().CreateScope().ServiceProvider;
   7:  
   8:         object root = serviceProvider2.GetType().GetField("_root", BindingFlags.Instance| BindingFlags.NonPublic).GetValue(serviceProvider2);
   9:         Debug.Assert(object.ReferenceEquals(serviceProvider1, root));        
  10:     }
  11: }

如果读者朋友们希望进一步了解ServiceScope的创建以及它和ServiceProvider之间的关系,我们不妨先来看看作为IServiceScope接口默认实现的内部类型ServiceScope的定义。如下面的代码片段所示,ServiceScope仅仅是对一个ServiceProvider对象的简单封装而已。值得一提的是,当ServiceScope的Dispose方法被调用的时候,这个被封装的ServiceProvider的同名方法同时被执行。

   1: {
   2:     private readonly ServiceProvider _scopedProvider;
   3:     public ServiceScope(ServiceProvider scopedProvider)
   4:     {
   5:         this._scopedProvider = scopedProvider;
   6:     }
   7:  
   8:     public void Dispose()
   9:     {
  10:         _scopedProvider.Dispose();
  11:     }
  12:  
  13:     public IServiceProvider ServiceProvider
  14:     {
  15:         get {return _scopedProvider; }
  16:     }
  17: }

IServiceScopeFactory接口的默认实现类型是一个名为ServiceScopeFactory的内部类型。如下面的代码片段所示,ServiceScopeFactory的只读字段“_provider”表示当前的ServiceProvider。当CreateScope方法被调用的时候,这个ServiceProvider的“子ServiceProvider”被创建出来,并被封装成返回的ServiceScope对象。

   1: internal class ServiceScopeFactory : IServiceScopeFactory
   2: {
   3:     private readonly ServiceProvider _provider;
   4:     public ServiceScopeFactory(ServiceProvider provider)
   5:     {
   6:         _provider = provider;
   7:     }
   8:  
   9:     public IServiceScope CreateScope()
  10:     {
  11:         return new ServiceScope(new ServiceProvider(_provider));
  12:     }
  13: }

三种生命周期管理模式

只有在充分了解ServiceScope的创建过程以及它与ServiceProvider之间的关系之后,我们才会对ServiceProvider支持的三种生命周期管理模式(Singleton、Scope和Transient)具有深刻的认识。就服务实例的提供方式来说,它们之间具有如下的差异:

  • Singleton:ServiceProvider创建的服务实例保存在作为根节点的ServiceProvider上,所有具有同一根节点的所有ServiceProvider提供的服务实例均是同一个对象。
  • Scoped:ServiceProvider创建的服务实例由自己保存,所以同一个ServiceProvider对象提供的服务实例均是同一个对象。
  • Transient:针对每一次服务提供请求,ServiceProvider总是创建一个新的服务实例。

创建生命周期作用域

public HomeController(IServiceScopeFactory scopeFactory)
        {
            using (var scope = scopeFactory.CreateScope())
            {
                var provider = scope.ServiceProvider;
                var env = provider.GetRequiredService<IWebHostEnvironment>();
            }
        }

ASP.NET Core默认注册了哪些服务

HostBuilder在创建ServiceCollection之后,会注册一些默认的服务。这些服务和我们自行注册的服务并没有任何区别

IHostingEnvironment
IWebHostingEnvironment
ILoggerFactory
ILogger<>
IApplicationBuilderFactory
IHttpContextFactory
IOptions<>
DiagnosticSource
DiagnosticListener
IStartupFilter
ObjectPoolProvider
IStartup

对泛型的支持

ServiceProvider提供的服务实例不仅限于普通的类型,它对泛型服务类型同样支持。在针对泛型服务进行注册的时候,我们可以将服务类型设定为携带具体泛型参数的“关闭泛型类型”(比如IFoobar<IFoo,IBar>),除此之外服务类型也可以是包含具体泛型参数的“开放泛型类型”(比如IFoo<,>)。前者实际上还是将其视为非泛型服务来对待,后者才真正体现了“泛型”的本质。

比如我们注册了某个泛型服务接口IFoobar<,>与它的实现类Foobar<,>之间的映射关系,当我们指定一个携带具体泛型参数的服务接口类型IFoobar<IFoo,IBar>并调用ServiceProvider的GetService方法获取对应的服务实例时,ServiceProvider会针对指定的泛型参数类型(IFoo和IBar)来解析与之匹配的实现类型(可能是Foo和Baz)并得到最终的实现类型(Foobar<Foo,Baz>)。

几种依赖注入方式

  • Startup构造函数可注入三个服务:IConfiguration 、IHostEnvironment 、IWebHostEnvironment
  • Startup类中的Configure方法可以注入服务
  • 在中间件类型的Invoke/InvokeAsync方法中注入
  • 在Action中注入
public void Index([FromServices]IFoobar foobar)
    {
    }
  • 在视图中注入
@inject IFoobar Foobar

ActivatorUtilities

如何通过容器创建未注册到容器的服务?
如果有个服务没有注册到容器,但是它依赖的服务已注册到容器,我们可以使用ActivatorUtilities创建此服务

            var service1 = ActivatorUtilities.CreateInstance<Service1>(serviceProvider, parameters: new object[0]);
            service1 = ActivatorUtilities.CreateInstance(serviceProvider, typeof(Service1)) as Service1;

            var service2 = ActivatorUtilities.CreateInstance<Service2>(serviceProvider, service1);
            service2 = ActivatorUtilities.CreateInstance(serviceProvider, typeof(Service2), service1) as Service2;

            var factory1 = ActivatorUtilities.CreateFactory(typeof(Service1), Type.EmptyTypes);
            service1 = factory1(serviceProvider, new object[0]) as Service1;

            var factory2 = ActivatorUtilities.CreateFactory(typeof(Service2), new Type[] { typeof(Service1) });
            service2 = factory2(serviceProvider, new object[] { service1 }) as Service2;

以下两个类未注册到容器

    public class Service1
    {
        private IConfiguration _configuration = null;
        public Service1(IConfiguration configuration)
        {
            _configuration = configuration;
        }
    }

    public class Service2
    {
        private IConfiguration _configuration = null;
        private Service1 _service1 = null;
        public Service2(IConfiguration configuration, Service1 service1)
        {
            _configuration = configuration;
            _service1 = service1;
        }
    }

扩展方法Replace

Replace方法可替换掉之前注入的服务,比如我们可以替换框架中用到的服务,达到修改源码的目的

    public static class ServiceCollectionExtensions
    {
        public static IServiceCollection Replace<TService, TImplementation>(this IServiceCollection services)
            where TImplementation : TService
        {
            return services.Replace<TService>(typeof(TImplementation));
        }

        public static IServiceCollection Replace<TService>(this IServiceCollection services, Type implementationType)
        {
            return services.Replace(typeof(TService), implementationType);
        }

        public static IServiceCollection Replace(this IServiceCollection services, Type serviceType, Type implementationType)
        {
            if (services == null)
            {
                throw new ArgumentNullException(nameof(services));
            }

            if (serviceType == null)
            {
                throw new ArgumentNullException(nameof(serviceType));
            }

            if (implementationType == null)
            {
                throw new ArgumentNullException(nameof(implementationType));
            }

            if (!services.TryGetDescriptors(serviceType, out var descriptors))
            {
                throw new ArgumentException($"No services found for {serviceType.FullName}.", nameof(serviceType));
            }

            foreach (var descriptor in descriptors)
            {
                var index = services.IndexOf(descriptor);

                services.Insert(index, descriptor.WithImplementationType(implementationType));

                services.Remove(descriptor);
            }

            return services;
        }

        private static bool TryGetDescriptors(this IServiceCollection services, Type serviceType, out ICollection<ServiceDescriptor> descriptors)
        {
            return (descriptors = services.Where(service => service.ServiceType == serviceType).ToArray()).Any();
        }

        private static ServiceDescriptor WithImplementationType(this ServiceDescriptor descriptor, Type implementationType)
        {
            return new ServiceDescriptor(descriptor.ServiceType, implementationType, descriptor.Lifetime);
        }
    }

案例

一个服务多个不同实现依赖注入

实现类:

    public interface IPerson
    {
        string Get();
    }
    public class PersonA:IPerson
    { 
    public string Get() { return "PersonA"; }
    }
    public class PersonB : IPerson
    {
        public string Get() { return "PersonB"; }
    }

ConfigureService:

       services.AddSingleton<PersonA>();
            services.AddSingleton<PersonB>();
            services.AddSingleton(provider =>
            {
                Func<string, IPerson> accesor = (key) =>
                {
                    if (key == "a")
                    {
                        return provider.GetService<PersonA>();
                    }
                    else if (key == "b")
                    {
                        return provider.GetService<PersonB>();
                    }
                    else
                    {
                        throw new ArgumentException("key error");
                    }
                };
                return accesor;
            });

注入

public HomeController(Func<string,IPerson> func)
        {
            var personA = func("a");
            var personB = func("b");
            
            Console.WriteLine(personA.Get());
            Console.WriteLine(personB.Get());
            string a = "";
        }

参考

https://www.cnblogs.com/Tmc-Blog/p/9893349.html 优雅使用DI
https://www.cnblogs.com/artech/p/di-4-asp-net-core.html 蒋金楠

posted @ 2021-01-10 19:52  .Neterr  阅读(427)  评论(0编辑  收藏  举报