前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Core官方DI剖析(1)--ServiceProvider类和ServiceCollection类

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

原创
作者头像
莫问今朝
发布2018-11-26 09:25:59
2K0
发布2018-11-26 09:25:59
举报
文章被收录于专栏:博客园
代码语言:txt
复制
前段时间看了蒋老师的Core文章,对于DI那一块感觉挺有意思,然后就看了一下Core官方DI的源码,这也算是第一个看得懂大部分源码的框架,虽然官方DI相对来说特别简单,
代码语言:txt
复制
官方DI相对于其它框架(例如 autofac)使用起来麻烦许多,既没有一次注入程序集中所有类的功能,也没有方便的属性注入,所以感觉起来官方的DI框架只是一个简单的标准,

:bell:属性注入:一种被称为service Locator的模式,蒋老师在Core文章中也推荐了建议不要使用这种模式

代码语言:txt
复制
首先从`ServiceDescriptor`和`ServiceCollection`来认识,这两个类也是注册时使用的类

ServiceDescriptor,ServiceCollection

代码语言:txt
复制
这两个类是我们使用注册服务的两个类型,注册服务时,DI都会封装成一个`ServiceDescriptor`类型进行缓存到`ServiceCollection`类型中,其中`ServiceCollection`有三个扩展类型

ServiceCollectionServiceExtensions : 实现了各种我们所使用了注册方式

ServiceCollectionDescriptorExtensions 实现了各种TryAdd和删除替换等操作

ServiceCollectionContainerBuilderExtensions 实现了构造ServiceProvider实例

ServiceCollection

代码语言:txt
复制
使用官方DI时注册我们都是将服务注册到一个`ServiceCollection`对象中,`ServiceCollection`类型看名称感觉就是一个服务集合的类型,其实并没有错,`IServiceCollection`集合就是一个继承`IList<ServiceDescriptor>`集合接口的一个类型,而`ServiceDescriptor`类型则是一个注册的服务描述类型,我们传入注册最后都会封装为一个`ServiceDescriptor`类型然后缓存到`ServiceCollection`集合之中

调用ServiceCollection实例对象的方法进行注册

代码语言:txt
复制
static void Main(string[] args)
{
     //      使用ServiceCollaction对象的扩展方法进行注册服务
     IServiceCollection services = new ServiceCollection()
          //      提供具体实例类
          .AddScoped<IFoo, Foo>()
          //      提供实例化具体的工厂
          .AddScoped(typeof(IBar), _ => new Bar())
          //      提供具体实例化对象,此方法只适用于Singleton生命周期
          .AddSingleton(typeof(IBaz),new Baz());
}
代码语言:txt
复制
**IServiceCollection类型的继承关系** 				
代码语言:txt
复制
/// <summary>
/// Specifies the contract for a collection of service descriptors.
/// </summary>
public interface IServiceCollection : IList<ServiceDescriptor>{}
代码语言:txt
复制
`ServiceCollection`本身类型中只有一些IList<T>具体实现方法,而所有注册的方法都是以扩展方法提供在一个				`ServiceCollectionServiceExtensions` `ServiceCollectionDescriptorExtensions`这两个扩展类中

:bell:ServiceCollectionDescriptorExtensions扩展类中大多都是TryAdd添加(不存在则添加),添加时参数直接为ServiceDescriptor对象或者有删除或替换操作

:bell:ServiceCollectionServiceExtensions 扩展类则以上面例子那样进行传入基类与派生类类型(派生类对象或工厂)

代码语言:txt
复制
**ServiceCollection类型可用成员**
代码语言:txt
复制
/// <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

代码语言:txt
复制
在大部分我们都是调用`ServiceCollectionServiceExtensions`扩展类的方法进行注册到Collection之中的,在这个扩展中提供了大量的重载,以便允许我们采用不同的方式进行注册,*泛型*  *类型参数* 等 
代码语言:txt
复制
//	列出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
代码语言:txt
复制
虽然在`ServiceCollectionServiceExtensions`扩展类中具有大量的重载,但是这是重载都是一些"虚"方法,其最终只是使用了3个方法进行注册
代码语言:txt
复制
//		使用基类和派生类类型实例化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;
}
代码语言:txt
复制
所调用的注册服务方式最后都是调用上面三个方法进行注册,微软只是只是提供了大量的壳子,从上面可以看出`ServiceDescriptor`类具有三个构造器起码,分别以三种方式进行实例化

ServiceCollectionDescriptorExtensions

代码语言:txt
复制
`ServiceCollectionDescriptorExtensions`扩展类中具有  `Replace` `RemoveAll` `Add`(参数为`ServiceDescriptor`)和很多重载的`TryAdd`方法

Replace(替换方法) 由新的ServiceDescriptor对象替换ServiceType的第一个ServiceDescriptor对象

代码语言:txt
复制
//		使用一个新的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对象

代码语言:txt
复制
//		移除指定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对象

代码语言:txt
复制
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已被注册,那么再次注册就不会成功

代码语言:txt
复制
public static void TryAdd(this IServiceCollection collection,ServiceDescriptor descriptor)
{
     if (!collection.Any(d => d.ServiceType == descriptor.ServiceType))
          collection.Add(descriptor);
}

有许多类似TryAddTransient方法进行了包装TryAdd

代码语言:txt
复制
public static void TryAddTransient(this IServiceCollection collection,Type service)
{
     //		使用ServiceDescriptor的静态方法创建实例化方法,
     //		此静态方法用于实例一个ServiceDescriptor对象,也是拥有大量重载
     var descriptor = ServiceDescriptor.Transient(service, service);
     TryAdd(collection, descriptor);
}

TryAddEnumerable方法在添加时除了判断基类型之外也会判断其派生类型是否被注册过

代码语言:txt
复制
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

代码语言:txt
复制
这个扩展类是创建`IServiceProvider`的,在这个扩展类中只具有`BuildServiceProvider()`方法,这个方法也就是我们用来获取`ServiceProvider`类型,`ServiceProvider`是获取服务对象的类型
代码语言:txt
复制
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);
代码语言:txt
复制
可以看到这个方法具有三个重载,在所有重载中都有一个`ServiceProviderOptions`类型,这是一个什么类型呢, 首先看一下这个类型定义
ServiceProviderOptions
代码语言:txt
复制
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;
}
代码语言:txt
复制
这个类中具有三个数据,一个是当前类的默认实例`Default` ,一个是实例化`ServiceProvider`的模式  `ServiceProvderMode`是一个枚举,默认为`Dynamic`,这个属性是`internal`修饰的,所以在外部使用时是不可以设置的,然而目前这三种都是使用了`Dynamic`	
代码语言:txt
复制
internal enum ServiceProviderMode
{
   Dynamic,
   Runtime,
   Expressions,
   ILEmit
}
代码语言:txt
复制
还有一个Bool类型属性`ValidateScopes`,如果这个类型为true,则不能从顶级容器中获取scoped生命周期的服务

ServiceDescriptor

代码语言:txt
复制
此类型是服务注册的描述类型,此类型中拥有注册的`ServiceType(基类型)`  `ImplementationType(派生类型)/具体服务对象/实例化服务类型的工厂` 和注册服务的生命周期`Lifetime`
代码语言:txt
复制
//      注册的类型的生命周期
/// <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类型中具有三个构造函数,就是使用派生类型,工厂和具体实例对象三种实例化服务对象方式

代码语言:txt
复制
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;
}
代码语言:txt
复制
此类中方法具有一个获取实际注册类型`GetImplementationType()`和一批实例化`ServiceDescriptor`对象的方法	`GetImplementationType()`方法根据其实例化`ServiceDescriptor`的方法进行判断获取实例化的实际类型,

:bell: 访问修饰符是internal,所以此方法并没有对外开放,只允许内部使用

代码语言:txt
复制
/// <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;
}
代码语言:txt
复制
实例化本类对象的方法具有很多重载,跟`ServiceCollectionDescriptorExtensions``ServiceCollectionServiceExtensions`扩展类一样,其中`ServiceCollectionDescriptorExtensions`扩展类中便利用了这些方法进行实例化此对象
代码语言:txt
复制
//		真正实例化对象的方法,重载都是调用此类方法 
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

代码语言:txt
复制
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());
}
1541327917954
1541327917954

从结果看出TryAdd方法并没有将IBaz再次注册到ServiceCollection对象

TryAddEnumerable

代码语言:txt
复制
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());
}
1541328317419
1541328317419

:bell:注意:使用TryAddEnumerable进行注册时不能使用工厂方法实例对象那种方式

代码语言:txt
复制
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);
}
1541329289018
1541329289018

测试ServiceProviderOptions的ValidateScopes

代码语言:txt
复制
static void Main(string[] args)
{
     //      顶级容器
     IServiceProvider provider = new ServiceCollection()
          .AddScoped(typeof(IFoo), typeof(Foo))
          //        设置不能从顶级容器中获取scoped生命周期服务
          .BuildServiceProvider(true);
     //  顶级容器构造Foo对象
     var foo1= provider.GetService<IFoo>();
}
代码语言:txt
复制
如果运行上面程序,则会抛出一个InvalidOperationException`异常
1541861534866
1541861534866
代码语言:txt
复制
可以看到并不允许让我们创建顶级容器的scoped服务对象,但是如果我们使用子容器就不会抛出异常
代码语言:txt
复制
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>();
}
1541861706374
1541861706374

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • ServiceDescriptor,ServiceCollection
  • ServiceCollection
    • ServiceCollectionServiceExtensions
      • ServiceCollectionDescriptorExtensions
        • ServiceCollectionContainerBuilderExtensions
          • ServiceProviderOptions
        • ServiceDescriptor
        • 测试
          • TryAdd
            • TryAddEnumerable
            • 测试ServiceProviderOptions的ValidateScopes
            相关产品与服务
            容器服务
            腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档