net5學習筆記---依賴注入

王延領發表於2021-05-15

小王的故事

小王去上班

​ 小王是個程式設計師,每個工作日他都要去上班,諸多交通工具他最喜歡的交通工具是騎電車。在騎行的過程中放空自己使他很快。
​ 突然有一天天氣預報說近期有很大的雨,小王再想騎電車去上班顯然是不可能了。那麼他就要選擇開汽車去。
但是由於小王每天過於依賴騎電動車,開汽車水平有限,那麼他就要重新學習開汽車。
​ 因此小王很煩惱,我就想去上個班,還要掌握不同的交通工具,這真是讓人煩惱,難道我就想做一個單純的打工族就這麼難嗎?小王就把這件事告訴了他的老婆,她老婆是個老司機,她說“這事你就不用管了,我是老司機啊,開車這事我來控制就好了,你就記得給我多買些禮物就好“。從此之後小王就安心的為了老婆的賺錢,開車這事就完全有他老婆負責。

小王的日記:

​ 我曾經很享受自己去上班(自己控制),每天去上班就拿出我心愛的電動車(例項化物件),直到有一天天氣預報告訴我要下大雨,再依賴自行車就會被大雨淋著,而換交通工具我發現我就要重新學習開車(高耦合),知道我老婆大人說她是老司機(ioc容器),開車這事由她來控制(控制反轉),不管怎麼去上班,事先告訴他用什麼交通工具就行(依賴注入),從此我每個工作日只要叫上我老婆她就直接帶我去上班了。


依賴注入

​ 從小王的故事我們可以看到一些關鍵詞依賴控制 而今天要說的就是什麼是依賴注入。說到依賴注入(DI)還有個概念叫控制反轉(IOC)。
控制反轉(IOC—Inversion of Control)不是什麼技術,而是一種設計思想。它的思路是設計好的依賴類交給容器控制,而不是在物件中直接例項化控制。就是在系統執行時ioc容器動態向一個物件提供他依賴的其他物件。而他的實現就是用依賴注入來實現。
依賴注入(DI—Dependency Injection)是元件之間依賴關係由容器決定。為此我們要明白依賴和注入關係
依賴:由於應用程式需要ioc容器提供物件外部的資源所以應用程式依賴於ioc容器
注入:某個物件所需要的外部資源(包括物件、資源、常量資料)注入到了ioc容器中。


net5 內建依賴注入

依賴注入是net core和net5的核心概念,我們在瞭解完概念之後也要對我們框架中對他的實現有個清楚的認識。當然官方文件是最好的瞭解方式。接下來我從我的角度去說一下對net 5(net core)依賴注入的理解.首先我們要知道net5依賴注入的實現由net 內建的容器和第三方的容器。我們主要說內建的實現情況。

內建依賴注入

Microsoft.Extensions.DependencyInjection

net5 內建依賴注入主要用到了Microsoft.Extensions.DependencyInjectionMicrosoft.Extensions.DependencyInjection.Abstraction兩個引用。我們nuget引用就行。它們是開源的。
DependencyInjectionClass
我們可以看到DependencyInjection這個專案並不大,但卻是整個net5(NET Core)的基礎,因為它提供了依賴注入容器的預設實現,而依賴注入是net5(net core)的核心基礎.

原始碼解析

image-20210515133637360

IServiceCollection

public class ServiceCollection : IServiceCollection
{
     //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
         {
             return _descriptors[index];
         }
         set
         {
             _descriptors[index] = value;
         }
     }
     //清空所有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);
}

IServiceCollection&&ServiceDescriptor

namespace Microsoft.Extensions.DependencyInjection
{
    /// <summary>
    /// 指定服務描述符集合的約定
    /// </summary>
    public interface IServiceCollection : IList<ServiceDescriptor>
    {
    }
}

我們從程式碼中可以看到內建依賴注入,它的內建容器是ServiceProvider,我們會把我們的服務注入到ServiceProvider 中來,而IServiceCollection是ServiceProvider的list的集合。

ServiceDescriptor

     public static IServiceCollection Add(this IServiceCollection collection,IEnumerable<ServiceDescriptor> descriptors)
        {
            if (collection == null)
            {
                throw new ArgumentNullException(nameof(collection));
            }
            if (descriptors == null)
            {
                throw new ArgumentNullException(nameof(descriptors));
            }
            foreach (var descriptor in descriptors)
            {
                collection.Add(descriptor);
            }
            return collection;
        }

我們可以看到服務註冊的時候,提供了ServiceDescriptor

 /// <summary>
    /// Describes a service with its service type, implementation, and lifetime.
    /// </summary>
    [DebuggerDisplay("Lifetime = {Lifetime}, ServiceType = {ServiceType}, ImplementationType = {ImplementationType}")]
    public class ServiceDescriptor
    {}

它是對服務的型別、生命週期、獲取服務的方式的描述。
image-20210515103733823
型別

//註冊的型別的生命週期
public ServiceLifetime Lifetime { get; }
//基型別
public Type ServiceType { get; }
//例項型別(派生型別)
public Type ImplementationType { get; }
//例項物件
public object ImplementationInstance { get; }
//註冊型別例項化物件的工廠
public Func<IServiceProvider, object> ImplementationFactory { get; }

建構函式

//派生型別
public ServiceDescriptor(Type serviceType,object instance)
     : this(serviceType, ServiceLifetime.Singleton)
{
      Lifetime = lifetime;
      ServiceType = serviceType;
      ImplementationInstance = instance;
}
 //工廠
public ServiceDescriptor(Type serviceType,Func<IServiceProvider, object> factory,ServiceLifetime lifetime)
     : this(serviceType, lifetime)
{
      Lifetime = lifetime;
      ServiceType = serviceType;
      ImplementationFactory = factory;
}
//具體例項物件
public ServiceDescriptor(Type serviceType,Type implementationType,ServiceLifetime lifetime)
     : this(serviceType, lifetime)
{
      Lifetime = lifetime;
      ServiceType = serviceType;
      ImplementationType = implementationType;
}
/// <summary>
///獲取當前註冊型別的例項型別(內部類)
/// </summary>
/// <returns></returns>
internal Type GetImplementationType(){}
//真正例項化物件的方法,過載都是呼叫此類方法 
public static ServiceDescriptor Describe(Type serviceType, Func<IServiceProvider, object> implementationFactory, ServiceLifetime lifetime){}
public static ServiceDescriptor Singleton(Type serviceType,object implementationInstance){}
public static ServiceDescriptor Scoped(Type service, Func<IServiceProvider, object> implementationFactory){}

ServiceCollectionServiceExtensions&ServiceCollectionDescriptorExtensions&ServiceCollectionContainerBuilderExtensions

這三個方法時ServiceCollection的三個擴充套件方法分別實現:我們所使用了註冊方式;TryAdd和RemoveAll,Replace等操作和構造ServiceProvider例項。
ServiceCollectionServiceExtensions:
程式碼太多(不同生命週期的註冊)就不貼出了,截個圖算了。
image-20210515121957047
ServiceCollectionDescriptorExtensions:
![image-20210515122419417](https://gitee.com/wyl1924/cdn/raw/master/img/blog/image-20210515122419417.png

public static void TryAdd(this IServiceCollection collection,ServiceDescriptor descriptor)
{
     if (!collection.Any(d => d.ServiceType == descriptor.ServiceType))
          collection.Add(descriptor);
}

注:Add,RemoveAll,Replace,沒什麼好說的,其中TryAdd,TryAddEnumerable需要注意到的是:中有服務集合中不存在才會進行過註冊。
ServiceCollectionContainerBuilderExtensions

    public static class ServiceCollectionContainerBuilderExtensions
    {
        public static ServiceProvider BuildServiceProvider(this IServiceCollection services)     
        public static ServiceProvider BuildServiceProvider(this IServiceCollection services, bool validateScopes)    
        public static ServiceProvider BuildServiceProvider(this IServiceCollection services, ServiceProviderOptions options)
        
}

IServiceProvider

public sealed class ServiceProvider : IServiceProvider, IDisposable, IServiceProviderEngineCallback
{
    //	ServiceProvider的擴充套件介面  
    //	使用這個介面的子類進行呼叫快取各種註冊服務和呼叫訪問者物件進行獲取例項物件
     private readonly IServiceProviderEngine _engine;  
     /// 快取型別(訪問者模式)
     private readonly CallSiteValidator _callSiteValidator;
     internal ServiceProvider(IEnumerable<ServiceDescriptor> serviceDescriptors, ServiceProviderOptions options)
     {
          IServiceProviderEngineCallback callback = null;
          if (options.ValidateScopes)
          {
               callback = this;
               _callSiteValidator = new CallSiteValidator();
          }
          //例項化工作引擎型別
          switch (options.Mode)
          {
               case ServiceProviderMode.Dynamic:
                    // 例項化 DynamicServiceProviderEngine
                    _engine = new DynamicServiceProviderEngine(serviceDescriptors, callback);
                    break;
               case ServiceProviderMode.Runtime:
                    _engine = new RuntimeServiceProviderEngine(serviceDescriptors, callback);
                    break;
                    case ServiceProviderMode.ILEmit:
                    _engine = new ILEmitServiceProviderEngine(serviceDescriptors, callback);
               break;
                    case ServiceProviderMode.Expressions:
                    _engine = new ExpressionsServiceProviderEngine(serviceDescriptors, callback);
                    break;
               default:
               	throw new NotSupportedException(nameof(options.Mode));
          }
     }
     // 獲取指定型別的服務物件
     public object GetService(Type serviceType) => _engine.GetService(serviceType);
     public void Dispose() => _engine.Dispose();
    //建立服務例項快取
	void IServiceProviderEngineCallback.OnCreate(ServiceCallSite callSite)
       =>_callSiteValidator.ValidateCallSite(callSite);
    //服務例項的校驗
     void IServiceProviderEngineCallback.OnResolve(Type serviceType, IServiceScope scope)
      =>_callSiteValidator.ValidateResolution(serviceType, scope, _engine.RootScope);
}

我們可以看到ServiceProvider用來獲取服務的例項,它提供了一個擴充套件IServiceProviderEngine來實現其功能。x下面我們來看一下原始碼

public static class ServiceProviderServiceExtensions
{
	 //	從IServiceProvider獲取“T”型別的服務。  
     public static T GetService<T>(this IServiceProvider provider)
          => (T)provider.GetService(typeof(T));
     
     //	從IServiceProvider獲取“T”型別的服務。
     public static object GetRequiredService(this IServiceProvider provider, Type serviceType)
     {
          // 如果當前ServiceProvider實現了 ISupportRequiredService 
          // 則直接呼叫當前ServiceProvier的GetRequiredService獲取服務例項
          var requiredServiceSupportingProvider = provider as ISupportRequiredService;
          if (requiredServiceSupportingProvider != null)
             return requiredServiceSupportingProvider.GetRequiredService(serviceType);
          //如果當前ServiceProvider未實現ISupportRequiredService
          //就直接呼叫GetService獲取服務例項,但是如果服務例項為空,則丟擲異常
          var service = provider.GetService(serviceType);
          if (service == null)
              throw new InvalidOperationException(Resources.FormatNoServiceRegistered(serviceType));
          return service;
     }
     public static T GetRequiredService<T>(this IServiceProvider provider)
          => (T)provider.GetRequiredService(typeof(T));
     //獲取指定註冊型別<T>的所有服務例項
     public static IEnumerable<T> GetServices<T>(this IServiceProvider provider)
          => provider.GetRequiredService<IEnumerable<T>>();
     //獲取指定註冊型別<T>的所有服務例項
     public static IEnumerable<object> GetServices(this IServiceProvider provider, Type serviceType)
     {
          //製造一個serviceType型別的IEnumberable<>集合,serviceTypele型別作為當前集合的泛型引數
          var genericEnumerable = typeof(IEnumerable<>).MakeGenericType(serviceType);
          return (IEnumerable<object>)provider.GetRequiredService(genericEnumerable);
     }

     //建立一個子IServiceProvider(容器)例項
     public static IServiceScope CreateScope(this IServiceProvider provider)
          => provider.GetRequiredService<IServiceScopeFactory>().CreateScope();
}

NET 內建依賴注入實現

我們再Startup.ConfigureServices(IServiceCollection services)進行註冊下面我直接用程式碼來看一下實現

public class UserService : IUserService
{
    public string GetName()
    {
        return "王延領";
    }
}
public interface IUserService
{
    string GetName();
}

1.面向介面註冊

services.AddScoped(typeof(IUserService), typeof(UserService));
services.AddScoped<IUserService, UserService>();

2.實現形式註冊

services.AddScoped<UserService>();
services.AddScoped(typeof(UserService));

兩種形式都可以,但擴充套件性顯然是第一種好。

3.批量注入

var assembly = Assembly.GetExecutingAssembly()
                        .DefinedTypes
                        .Where(a => a.Name.EndsWith("Service") && !a.Name.StartsWith("I"));
foreach (var item in assembly)
{
    services.AddScoped(item.GetInterfaces().FirstOrDefault(), item);
}

第三方容器(Autofact)注入

後續單獨一篇文章再寫吧。

相關文章