前段时间看了蒋老师的Core文章,对于DI那一块感受挺有意思,而后就看了一下Core官方DI的源码,这也算是第一个看得懂大部分源码的框架,虽然官方DI相对来讲特别简单,c#
官方DI相对于其它框架(例如 autofac)使用起来麻烦许多,既没有一次注入程序集中全部类的功能,也没有方便的属性注入,因此感受起来官方的DI框架只是一个简单的标准,
🔔属性注入:一种被称为
service Locator
的模式,蒋老师在Core文章中也推荐了建议不要使用这种模式缓存
首先从`ServiceDescriptor`和`ServiceCollection`来认识,这两个类也是注册时使用的类
这两个类是咱们使用注册服务的两个类型,注册服务时,DI都会封装成一个`ServiceDescriptor`类型进行缓存到`ServiceCollection`类型中,其中`ServiceCollection`有三个扩展类型
ServiceCollectionServiceExtensions : 实现了各类咱们所使用了注册方式框架
**ServiceCollectionDescriptorExtensions ** 实现了各类TryAdd和删除替换等操做ide
ServiceCollectionContainerBuilderExtensions 实现了构造
ServiceProvider
实例函数
使用官方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`扩展类的方法进行注册到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`扩展类中具备 `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
方法具备大量的包装方法,跟ServiceCollectionServiceExtensions
中Add
方法差很少,
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); }
这个扩展类是建立`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`类型,这是一个什么类型呢, 首先看一下这个类型定义
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生命周期的服务
此类型是服务注册的描述类型,此类型中拥有注册的`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);
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
对象
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); }
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>(); }