Core官方DI剖析(1)--ServiceProvider類和ServiceCollection類

前段時間看了蔣老師的Core文章,對於DI那一塊感受挺有意思,而後就看了一下Core官方DI的源碼,這也算是第一個看得懂大部分源碼的框架,雖然官方DI相對來講特別簡單,c#

官方DI相對於其它框架(例如 autofac)使用起來麻煩許多,既沒有一次注入程序集中全部類的功能,也沒有方便的屬性注入,因此感受起來官方的DI框架只是一個簡單的標準,

🔔屬性注入:一種被稱爲service Locator的模式,蔣老師在Core文章中也推薦了建議不要使用這種模式緩存

首先從`ServiceDescriptor`和`ServiceCollection`來認識,這兩個類也是註冊時使用的類

ServiceDescriptor,ServiceCollection

這兩個類是咱們使用註冊服務的兩個類型,註冊服務時,DI都會封裝成一個`ServiceDescriptor`類型進行緩存到`ServiceCollection`類型中,其中`ServiceCollection`有三個擴展類型

ServiceCollectionServiceExtensions : 實現了各類咱們所使用了註冊方式框架

**ServiceCollectionDescriptorExtensions ** 實現了各類TryAdd和刪除替換等操做ide

ServiceCollectionContainerBuilderExtensions 實現了構造ServiceProvider實例函數

ServiceCollection

使用官方DI時註冊咱們都是將服務註冊到一個`ServiceCollection`對象中,`ServiceCollection`類型看名稱感受就是一個服務集合的類型,其實並無錯,`IServiceCollection`集合就是一個繼承`IList<ServiceDescriptor>`集合接口的一個類型,而`ServiceDescriptor`類型則是一個註冊的服務描述類型,咱們傳入註冊最後都會封裝爲一個`ServiceDescriptor`類型而後緩存到`ServiceCollection`集合之中

調用ServiceCollection實例對象的方法進行註冊測試

static void Main(string[] args)
{
     //      使用ServiceCollaction對象的擴展方法進行註冊服務
     IServiceCollection services = new ServiceCollection()
          //      提供具體實例類
          .AddScoped<IFoo, Foo>()
          //      提供實例化具體的工廠
          .AddScoped(typeof(IBar), _ => new Bar())
          //      提供具體實例化對象,此方法只適用於Singleton生命週期
          .AddSingleton(typeof(IBaz),new Baz());
}
**IServiceCollection類型的繼承關係**
/// <summary>
/// Specifies the contract for a collection of service descriptors.
/// </summary>
public interface IServiceCollection : IList<ServiceDescriptor>{}
`ServiceCollection`自己類型中只有一些IList<T>具體實現方法,而全部註冊的方法都是以擴展方法提供在一個				`ServiceCollectionServiceExtensions` `ServiceCollectionDescriptorExtensions`這兩個擴展類中

🔔ServiceCollectionDescriptorExtensions擴展類中大多都是TryAdd添加(不存在則添加),添加時參數直接爲ServiceDescriptor對象或者有刪除或替換操做ui

🔔ServiceCollectionServiceExtensions 擴展類則以上面例子那樣進行傳入基類與派生類類型(派生類對象或工廠)this

**ServiceCollection類型可用成員**
/// <summary>
/// Default implementation of <see cref="IServiceCollection"/>.
/// </summary>
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

在大部分咱們都是調用`ServiceCollectionServiceExtensions`擴展類的方法進行註冊到Collection之中的,在這個擴展中提供了大量的重載,以便容許咱們採用不一樣的方式進行註冊,*泛型*  *類型參數* 等
//	列出Sinleton生命週期一部分,Scoped和Transient生命週期都一致

//		基類型和派生類型
public static IServiceCollection AddSingleton(this IServiceCollection services,Type serviceType,Type implementationType);

//		基類型和派生類型工廠
public static IServiceCollection AddSingleton(this IServiceCollection services,Type serviceType,Func<IServiceProvider, object> implementationFactory)
     
//		基類型和派生類型泛型     
public static IServiceCollection AddSingleton<TService, TImplementation>(this IServiceCollection services)
     where TService : class where TImplementation : class, TService
     
//		此方法註冊 services必須是一個實例化的類型
public static IServiceCollection AddSingleton<TService>(this IServiceCollection services) 
          where TService : class
               
//		 基類型與派生類型實例對象,此方式適用於Sinleton生命週期      
public static IServiceCollection AddSingleton<TService>(this IServiceCollection services,TService implementationInstance)
            where TService : class
雖然在`ServiceCollectionServiceExtensions`擴展類中具備大量的重載,可是這是重載都是一些"虛"方法,其最終只是使用了3個方法進行註冊
//		使用基類和派生類類型實例化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;
}
所調用的註冊服務方式最後都是調用上面三個方法進行註冊,微軟只是只是提供了大量的殼子,從上面能夠看出`ServiceDescriptor`類具備三個構造器起碼,分別以三種方式進行實例化

ServiceCollectionDescriptorExtensions

`ServiceCollectionDescriptorExtensions`擴展類中具備  `Replace` `RemoveAll` `Add`(參數爲`ServiceDescriptor`)和不少重載的`TryAdd`方法

Replace(替換方法) 由新的ServiceDescriptor對象替換ServiceType的第一個ServiceDescriptor對象code

//		使用一個新的ServiceDescriptor對象替換指定基類的第一個ServiceDescriptor
public static IServiceCollection Replace(this IServiceCollection collection,ServiceDescriptor descriptor)
{
     //		獲取註冊的第一個serviceType進行刪除並添加進這個新的ServiceDescriptor
    var registeredServiceDescriptor = collection.FirstOrDefault(s => s.ServiceType == descriptor.ServiceType);
    if (registeredServiceDescriptor != null)
        collection.Remove(registeredServiceDescriptor);
    collection.Add(descriptor);
    return collection;
}

RemoveAll(刪除方法) 從Collection刪除指定ServiceType的全部ServiceDescriptor對象orm

//		移除指定ServiceType的全部ServiceDescriptor
public static IServiceCollection RemoveAll(this IServiceCollection collection, Type serviceType)
{
    for (var i = collection.Count - 1; i >= 0; i--)
    {
        var descriptor = collection[i];
        if (descriptor.ServiceType == serviceType)
            collection.RemoveAt(i);
    }
    return collection;
}
//		移除指定泛型類型的全部ServiceDescriptor
public static IServiceCollection RemoveAll<T>(this IServiceCollection collection) => 			RemoveAll(collection, typeof(T));

Add(添加方法) 參數直接爲ServiceDescriptor對象

public static IServiceCollection Add(this IServiceCollection collection,ServiceDescriptor descriptor)
{
   	collection.Add(descriptor);
   	return collection;
}
public static IServiceCollection Add(this IServiceCollection collection,IEnumerable<ServiceDescriptor> descriptors)
{
   	foreach (var descriptor in descriptors)
         collection.Add(descriptor);
     return collection;
}

TryAdd和TryAddEnumerable方法

TryAdd和TryAddEnumerable這兩個方法是若是不存在則添加,其中TryAdd方法具備大量的包裝方法,跟ServiceCollectionServiceExtensionsAdd方法差很少,

TryAdd方法若是當前ServiceType已被註冊,那麼再次註冊就不會成功

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

有許多相似TryAddTransient方法進行了包裝TryAdd

public static void TryAddTransient(this IServiceCollection collection,Type service)
{
     //		使用ServiceDescriptor的靜態方法建立實例化方法,
     //		此靜態方法用於實例一個ServiceDescriptor對象,也是擁有大量重載
     var descriptor = ServiceDescriptor.Transient(service, service);
     TryAdd(collection, descriptor);
}

TryAddEnumerable方法在添加時除了判斷基類型以外也會判斷其派生類型是否被註冊過

public static void TryAddEnumerable(this IServiceCollection services,ServiceDescriptor descriptor)
{
     //      ServiceDescriptor.GetImplementationType()是獲取派生類型
     //      使用TryAddEnumerable進行判斷時也會判斷其派生類型
     var implementationType = descriptor.GetImplementationType();
     if (implementationType == typeof(object) ||
         implementationType == descriptor.ServiceType)
     {
          throw new ArgumentException(
               Resources.FormatTryAddIndistinguishableTypeToEnumerable(
                    implementationType,
                    descriptor.ServiceType),
               nameof(descriptor));
     }
	//	若是當前基類型和當前派生類型沒有註冊過,便進行註冊
     if (!services.Any(d =>
                       d.ServiceType == descriptor.ServiceType &&
                       d.GetImplementationType() == implementationType))
          services.Add(descriptor);
}

public static void TryAddEnumerable(this IServiceCollection services,IEnumerable<ServiceDescriptor> descriptors)
{
    foreach (var d in descriptors)
        services.TryAddEnumerable(d);
}

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);
能夠看到這個方法具備三個重載,在全部重載中都有一個`ServiceProviderOptions`類型,這是一個什麼類型呢, 首先看一下這個類型定義

ServiceProviderOptions

public class ServiceProviderOptions
{
     internal static readonly ServiceProviderOptions Default = new ServiceProviderOptions();
     /// <summary>
     /// 當此屬性爲true,不能從獲取頂級容器中的scoped
     /// </summary>
     public bool ValidateScopes { get; set; }
     /// <summary>
     ///        實例化ServiceProvider模式,當前只能使用Dynamic模式
     /// </summary>
     internal ServiceProviderMode Mode { get; set; } = ServiceProviderMode.Dynamic;
}
這個類中具備三個數據,一個是當前類的默認實例`Default` ,一個是實例化`ServiceProvider`的模式  `ServiceProvderMode`是一個枚舉,默認爲`Dynamic`,這個屬性是`internal`修飾的,因此在外部使用時是不能夠設置的,然而目前這三種都是使用了`Dynamic`
internal enum ServiceProviderMode
{
   Dynamic,
   Runtime,
   Expressions,
   ILEmit
}
還有一個Bool類型屬性`ValidateScopes`,若是這個類型爲true,則不能從頂級容器中獲取scoped生命週期的服務

ServiceDescriptor

此類型是服務註冊的描述類型,此類型中擁有註冊的`ServiceType(基類型)`  `ImplementationType(派生類型)/具體服務對象/實例化服務類型的工廠` 和註冊服務的生命週期`Lifetime`
//      註冊的類型的生命週期
/// <inheritdoc />
public ServiceLifetime Lifetime { get; }
//      註冊類型的基類型
/// <inheritdoc />
public Type ServiceType { get; }
//     註冊類型的實例類型(派生類型)
/// <inheritdoc />
public Type ImplementationType { get; }
//      註冊類型的實例對象
/// <inheritdoc />
public object ImplementationInstance { get; }
//      註冊類型實例化對象的工廠
/// <inheritdoc />
public Func<IServiceProvider, object> ImplementationFactory { get; }

ServiceDescriptor類型中具備三個構造函數,就是使用派生類型,工廠和具體實例對象三種實例化服務對象方式

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;
}
此類中方法具備一個獲取實際註冊類型`GetImplementationType()`和一批實例化`ServiceDescriptor`對象的方法	`GetImplementationType()`方法根據其實例化`ServiceDescriptor`的方法進行判斷獲取實例化的實際類型,

🔔 訪問修飾符是internal,因此此方法並無對外開放,只容許內部使用

/// <summary>
///     獲取當前註冊類型的實例類型
/// </summary>
/// <returns></returns>
internal Type GetImplementationType()
{
     if (ImplementationType != null)
          return ImplementationType;
     else if (ImplementationInstance != null)
          return ImplementationInstance.GetType();
     else if (ImplementationFactory != null)
     {
          var typeArguments = ImplementationFactory.GetType().GenericTypeArguments;
          return typeArguments[1];
     }
     return null;
}
實例化本類對象的方法具備不少重載,跟`ServiceCollectionDescriptorExtensions``ServiceCollectionServiceExtensions`擴展類同樣,其中`ServiceCollectionDescriptorExtensions`擴展類中便利用了這些方法進行實例化此對象
//		真正實例化對象的方法,重載都是調用此類方法 
public static ServiceDescriptor Describe(Type serviceType, Func<IServiceProvider, object> implementationFactory, ServiceLifetime lifetime)
     => new ServiceDescriptor(serviceType, implementationFactory, lifetime);

 public static ServiceDescriptor Describe(Type serviceType, Type implementationType, ServiceLifetime lifetime)
    => new ServiceDescriptor(serviceType, implementationType, lifetime);
//		此方法只有Sinleton生命週期才能調用
public static ServiceDescriptor Singleton(Type serviceType,object implementationInstance)
    =>new ServiceDescriptor(serviceType, implementationInstance);

測試

TryAdd

static void Main(string[] args)
{
     IServiceCollection services = new ServiceCollection()
          .AddScoped(typeof(IBaz),typeof(Baz2));
     //		嘗試註冊使用TryAdd再次註冊IBaz類型
     services.TryAdd(new ServiceDescriptor(typeof(IBaz), typeof(Baz1), ServiceLifetime.Scoped));
     var provider=  services.BuildServiceProvider();
     //		獲取全部IBaz的註冊對象
     IList<IBaz> baz=  provider.GetServices<IBaz>().ToList();
     Console.WriteLine("獲取到的數量:"+baz.Count);
     //		循環輸出全部實際對象類型
     foreach (var item in baz)
          Console.WriteLine("實際類型:" + item.GetType());
}

從結果看出TryAdd方法並無將IBaz再次註冊到ServiceCollection對象

TryAddEnumerable

static void Main(string[] args)
{
     IServiceCollection services = new ServiceCollection()
          .AddScoped(typeof(IBaz),typeof(Baz2));
     //		使用TryAddEnumerable嘗試註冊
     services.TryAddEnumerable(new ServiceDescriptor(typeof(IBaz), typeof(Baz2), ServiceLifetime.Scoped));
     services.TryAddEnumerable(new ServiceDescriptor(typeof(IBaz), typeof(Baz1), ServiceLifetime.Scoped));
     var provider=  services.BuildServiceProvider();
     IList<IBaz> baz=  provider.GetServices<IBaz>().ToList();
     Console.WriteLine("獲取到的數量:"+baz.Count);
     foreach (var item in baz)
          Console.WriteLine("實際類型:" + item.GetType());
}

🔔注意:使用TryAddEnumerable進行註冊時不能使用工廠方法實例對象那種方式

static void Main(string[] args)
{
     IServiceCollection services = new ServiceCollection()
          .AddScoped(typeof(IBaz),typeof(Baz2));
     //      使用工廠方法實例化對象方式
     var service = ServiceDescriptor
          .Scoped<IBaz>(_ => new Baz1());
     //      使用TryAddEnumerable進行註冊,會拋出一個System.ArgumentException異常
     services.TryAddEnumerable(service);
}

測試ServiceProviderOptions的ValidateScopes

static void Main(string[] args)
{
     //      頂級容器
     IServiceProvider provider = new ServiceCollection()
          .AddScoped(typeof(IFoo), typeof(Foo))
          //        設置不能從頂級容器中獲取scoped生命週期服務
          .BuildServiceProvider(true);
     //  頂級容器構造Foo對象
     var foo1= provider.GetService<IFoo>();
}
若是運行上面程序,則會拋出一個InvalidOperationException`異常

能夠看到並不容許讓咱們建立頂級容器的scoped服務對象,可是若是咱們使用子容器就不會拋出異常
static void Main(string[] args)
{
     //      頂級容器
     IServiceProvider provider = new ServiceCollection()
          .AddScoped(typeof(IFoo), typeof(Foo))
          //        設置不能從頂級容器中獲取scoped生命週期服務
          .BuildServiceProvider(true);
     //      子容器
     IServiceProvider childProvider= provider.CreateScope().ServiceProvider;
     var foo2= childProvider.GetService<IFoo>();
}

相關文章
相關標籤/搜索