在DI容器中注册类型,DI容器就可以帮我们创建类型的实例;如果注册类型实现了IAsyncDisposable
或者IDisposable
接口,对象销毁时DI容器还会帮我们调用DisposeAsync
或Dispose
方法。这是如何实现的呢?一起来看看吧。本文是基于Dependency Injection 8.0
编写。如果已熟练使用,可以直接从第三节开始观看。
功能演示
说明:对象的销毁由GC管理,这里的“销毁”是指调用Dispose
方法。
先介绍一下DI容器中类的三种生命周期:Singleton
(单例)、Scoped
(在每个ServiceProviderEngineScope
中只创建一次,可以理解为局部单例)、Transient
(每次都创建新对象)。
先定义三个代表生命周期的接口ISingletonService
、IScopedService
和ITransientService
;分别在实现类中打印创建信息和Dispose信息。并且在打印信息里添加了HashCode,可以观察是哪个对象被创建和“销毁”。代码在每个Scope中对每个不同生命周期的类创建2个对象,共12次调用,来看看实际上一共创建了几个对象。
public interface ISingletonService { }
public interface IScopedService{ }
public interface ITransientService { }
public class SingletonService : ISingletonService, IDisposable
{
public SingletonService()
{
Console.WriteLine($"{this.GetType()} 被创建了 - {this.GetHashCode()}...");
}
public void Dispose()
{
Console.WriteLine($"{this.GetType()} 被销毁了- {this.GetHashCode()}...");
}
}
可以看到,Singleton
对象创建了1个,Scoped
对象创建了2个(因为有两个ServiceProviderEngineScope
),Transient
对象每次调用都会创建新的对象,共4个。Dispose
方法调用顺序是,先创建的最后调用。
ASP.NET CORE中Scope
在ASP.NET CORE中每次请求会创建一个Scope,通过HttpContext.RequestServices
可以获取这个Scope对象,所以生命周期为Scoped
的类在一次请求中只会创建一次,局部单例。
// HttpContext.RequestServices
public override IServiceProvider RequestServices
{
get { return ServiceProvidersFeature.RequestServices; }
set { ServiceProvidersFeature.RequestServices = value; }
}
public class RequestServicesFeature
{
public IServiceProvider RequestServices
{
get
{
if (!_requestServicesSet && _scopeFactory != null)
{
_context.Response.RegisterForDisposeAsync(this);
//每次请求创建一个Scope
_scope = _scopeFactory.CreateScope();
_requestServices = _scope.ServiceProvider;
_requestServicesSet = true;
}
return _requestServices!;
}
set
{
_requestServices = value;
_requestServicesSet = true;
}
}
}
深入理解
要理解对象的创建和“销毁”,ServiceProvider
类是关键。ServiceProvider
创建和“销毁”对象主要是通过它的两个成员来完成的;分别是ServiceProviderEngine _engine
和ServiceProviderEngineScope Root
ServiceProviderEngine
这是一个抽象类,只有一个方法RealizeService
,该方法返回一个创建实例的委托。该类不直接创建对象,而是提供一个创建对象的委托!ServiceProviderEngineScope
又可以分两类,根scope
和子scope
,它们的主要功能是:
捕获创建的对象存入List<object>? _disposables
中,用于调用Dispose
方法。
ServiceProviderEngine
通过类型信息创建实例主要有三种办法 1.反射,2.表达式树,3.Emit。这些功能实现在ServiceProviderEngine
的子类中,继承关系如下:
ServiceProviderEngine
RuntimeServiceProviderEngine
反射ExpressionsServiceProviderEngine
表达式树ILEmitServiceProviderEngine
EmitCompiledServiceProviderEngine
表达式树和Emit的组合DynamicServiceProviderEngine
默认用的这个Engine,实现方式有点难理解
下面看一下这些类的实现
internal sealed class ExpressionsServiceProviderEngine : ServiceProviderEngine
{
//使用这个类构建表达式树
private readonly ExpressionResolverBuilder _expressionResolverBuilder;
//返回一个创建对象的委托!
public override Func<ServiceProviderEngineScope, object> RealizeService(ServiceCallSite callSite)
{
return _expressionResolverBuilder.Build(callSite);
}
}
internal sealed class ILEmitServiceProviderEngine : ServiceProviderEngine
{
//使用这个类构建emit
private readonly ILEmitResolverBuilder _expressionResolverBuilder;
public override Func<ServiceProviderEngineScope, object?> RealizeService(ServiceCallSite callSite)
{
return _expressionResolverBuilder.Build(callSite);
}
}
internal sealed class RuntimeServiceProviderEngine : ServiceProviderEngine
{ // 反射
public static RuntimeServiceProviderEngine Instance { get; } = new RuntimeServiceProviderEngine();
//使用反射相关方法在CallSiteRuntimeResolver中
public override Func<ServiceProviderEngineScope, object?> RealizeService(ServiceCallSite callSite)
{
return scope => CallSiteRuntimeResolver.Instance.Resolve(callSite, scope);
}
}
internal abstract class CompiledServiceProviderEngine : ServiceProviderEngine
{
//通过以下方式选择默认创建实例的方式
#if IL_EMIT
public ILEmitResolverBuilder ResolverBuilder { get; } //emit构建
#else
public ExpressionResolverBuilder ResolverBuilder { get; } //表达式树构建
#endif
public CompiledServiceProviderEngine(ServiceProvider provider)
{
ResolverBuilder = new(provider);
}
public override Func<ServiceProviderEngineScope, object?> RealizeService(ServiceCallSite callSite) => ResolverBuilder.Build(callSite);
}
默认是使用emit
的方式创建对象,通过以下方法可以验证默认使用的引擎和创建实例的方式
static void EngineInfo(ServiceProvider provider)
{
var p = Expression.Parameter(typeof(ServiceProvider));
//相当于 provider._engine
var lambda = Expression.Lambda<Func<ServiceProvider, object>>(Expression.Field(p, "_engine"), p);
var engine = lambda.Compile()(provider);
var baseType = engine.GetType().BaseType!;
// 相当于(provider._engine as CompiledServiceProviderEngine).ResolverBuilder
var lambda2 = Expression.Lambda<Func<ServiceProvider, object>>(
Expression.Property(Expression.Convert(Expression.Field(p, "_engine"), baseType), "ResolverBuilder"), p);
var builder = lambda2.Compile()(provider);
Console.WriteLine(engine.GetType());
Console.WriteLine(builder.GetType());
//输出信息:
//Microsoft.Extensions.DependencyInjection.ServiceLookup.DynamicServiceProviderEngine
//Microsoft.Extensions.DependencyInjection.ServiceLookup.ILEmitResolverBuilder
}
从上面输出可以看到DynamicServiceProviderEngine
是默认的Engine,并且它的父类CompiledServiceProviderEngine
是使用的ILEmitResolverBuilder
。
下面来介绍DynamicServiceProviderEngine
,这段代码三年前看不懂,我三年后还是不太懂,实现方式有点难以理解,有大佬懂的可以解答下。
首先,Singleton
对象是不会调用这个方法的,只有生命周期是Scoped
和Transient
才会调用这个方法。通过调用RealizeService
获取一个创建对象的委托;对于每一个服务标识
,