在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
獲取一個建立物件的委託;對於每一個服務標識
,在呼叫2次後,替換為用emit的方式去建立例項的委託(如果不理解服務標識
,看我的這篇文章),並且,還是用執行緒池實現的,對於Web開發,而CPU繫結的非同步不推薦使用執行緒池。
前幾次呼叫,反射效能應該是優於emit和表示式樹,因為後兩者有一個構建委託的過程。但是,使用執行緒池的影響應該遠大於前者吧。一開始就直接使用emit建立物件不應該優於使用執行緒池?上百個類使用執行緒池,會導致網站啟動時很卡。當然,也就啟動後一小段時間卡。
internal sealed class DynamicServiceProviderEngine : CompiledServiceProviderEngine
{
public override Func<ServiceProviderEngineScope, object?> RealizeService(ServiceCallSite callSite)
{
int callCount = 0;
return scope =>
{
// Resolve the result before we increment the call count, this ensures that singletons
// won't cause any side effects during the compilation of the resolve function.
var result = CallSiteRuntimeResolver.Instance.Resolve(callSite, scope);
if (Interlocked.Increment(ref callCount) == 2)
{
//不捕獲ExecutionContext,AsyncLocal失效。
_ = ThreadPool.UnsafeQueueUserWorkItem(_ =>
{
_serviceProvider.ReplaceServiceAccessor(callSite, base.RealizeService(callSite));
},
null);
}
return result;
};
}
}
獲取建立物件的委託時,需要傳入引數ServiceCallSite
,callSite物件有型別的建立資訊,指明如何建立這個型別的例項,比如透過建構函式、工廠、建立陣列(IEnumerable<>服務)、常量等建立例項。並且在後面呼叫建立物件的委託時,如果需要快取,會把建立出來的物件快取到callSite物件中。
ServiceProviderEngineScope
下面再來介紹下ServiceProviderEngineScope
,主要成員及相關介紹在下面程式碼中。
如何建立ServiceProviderEngineScope
物件呢?透過services.CreateScope()
就是建立一個scope
物件。建立scope
物件時,會傳遞ServiceProvider
並儲存到RootProvider
屬性中。而每個scope
物件又有一個名字為ServiceProvider
的屬性,它的值是this
;所以scope.ServiceProvider == scope
。
我們發現這個物件也有GetService
方法,發現它只是轉發了一下,其實還是呼叫ServiceProvider
的GetService
獲取物件。這個方法還把當前scope
作為引數傳遞過去了,表示建立出來的物件由當前scope
物件管理。
我們透過scope.ServiceProvider.GetService<ISingletonService>()
獲取物件和services.GetService<ISingletonService>()
基本一樣,只是傳遞的scope
不同,前者是傳遞自己,後者是傳遞ServiceProvider
物件中的scope
物件(也就是Root
屬性)。
再次說明:scope.ServiceProvider
是返回自己,scope.RootProvider
才是真正的ServiceProvider
物件
internal sealed class ServiceProviderEngineScope : IServiceScope, IServiceProvider, IKeyedServiceProvider, IAsyncDisposable, IServiceScopeFactory
{
//快取建立出來的物件,只會快取“非根Scope”建立的且生命週期為“Scoped”的例項
internal Dictionary<ServiceCacheKey, object?> ResolvedServices { get; }
// 存放捕獲的 disposables 物件
private List<object>? _disposables;
//是否為根Scope
public bool IsRootScope { get; }
//這裡有點繞,ServiceProvider屬性返回的是自己。每個子Scope就是 engineScope
public IServiceProvider ServiceProvider => this;
//RootProvider屬性是儲存的ServiceProvider物件
internal ServiceProvider RootProvider { get; }
//所有建立Scope的方法都是呼叫 ServiceProvider的CreateScope方法
public IServiceScope CreateScope() => RootProvider.CreateScope();
//唯一建構函式
public ServiceProviderEngineScope(ServiceProvider provider, bool isRootScope)
{
ResolvedServices = new Dictionary<ServiceCacheKey, object?>();
RootProvider = provider; //儲存ServiceProvider物件
IsRootScope = isRootScope;//只有ServiceProvider物件中的scope物件的這個值是true,其它都是false
}
public object? GetService(Type serviceType)
{
//每個子scope還是呼叫RootProvider建立服務,只是由子scope捕獲物件用於釋放Dispose
return RootProvider.GetService(ServiceIdentifier.FromServiceType(serviceType), this);
}
}
在ServiceProvider
的建構函式中,註冊了IServiceProvider
和IServiceScopeFactory
服務。兩者都是返回一個ServiceProviderEngineScope
物件,前者是返回當前scope
物件,後者是返回單例根scope
物件(Root
屬性)。這是理解建立scope物件流程的前提。
呼叫services.CreateScope()
建立scope物件流程:
- 1.呼叫
ServiceProvider
的CreateScope方法。services.CreateScope()
- 2.CreateScope方法就是獲取
根scope
並呼叫它自己的CreateScope方法。provider.GetRequiredService<IServiceScopeFactory>().CreateScope()
- 3.而
根scope
的CreateScope方法是呼叫ServiceProvider
的CreateScope方法。RootProvider.CreateScope()
- 4.在
ServiceProvider
的CreateScope方法中就是new一個物件。new ServiceProviderEngineScope(this, isRootScope: false)
兜兜轉轉,services.CreateScope()
最終是呼叫自己內部方法CreateScope
。程式碼和註釋已貼出,結合上下兩段程式碼看。
//這是在ServiceProvider的建構函式中註冊的服務
CallSiteFactory.Add(ServiceIdentifier.FromServiceType(typeof(IServiceProvider)), new ServiceProviderCallSite());
CallSiteFactory.Add(ServiceIdentifier.FromServiceType(typeof(IServiceScopeFactory)), new ConstantCallSite(typeof(IServiceScopeFactory), Root));
//我們建立Scope是呼叫的這個擴充套件方法,結合上面程式碼,你會發現CreateScope其實是呼叫的自己的同名方法
public static IServiceScope CreateScope(this IServiceProvider provider)
{
return provider.GetRequiredService<IServiceScopeFactory>().CreateScope();
}
//這裡是ServiceProvider的CreateScope方法,它“internal”修飾的,但最終呼叫的這裡
internal IServiceScope CreateScope()
{
// 建立Scope最終是呼叫的這裡,ServiceProvider會儲存在每個Scope裡,每個Scope都是同一個ServiceProvider
return new ServiceProviderEngineScope(this, isRootScope: false);
}
根scope和子scope
根scope
和子scope
透過IsRootScope
屬性區分,儲存在ServiceProvider
中的Root
屬性就是一個根scope
,其生命週期是全域性的,程式停止才會呼叫Dispose
方法。ASP.NET CORE中每次請求建立的scope
就是一個子scope
。透過兩個例子加深印象:
static void EngineScopeTest()
{
using var services = new ServiceCollection().BuildServiceProvider();
using var scope1 = services.CreateScope();
using var scope2 = scope1.ServiceProvider.CreateScope();
//返回當前scope
var rootScope = services.GetService<IServiceProvider>();
//返回根scope
var rootScope2 = services.GetService<IServiceScopeFactory>();
var currentScope = scope1.ServiceProvider.GetService<IServiceProvider>();
var rootScope3 = scope1.ServiceProvider.GetService<IServiceScopeFactory>();
var currentScope2 = scope2.ServiceProvider.GetService<IServiceProvider>();
var rootScope4 = scope2.ServiceProvider.GetService<IServiceScopeFactory>();
// 獲取根scope
var root = GetRoot(services);
//執行程式碼輸出4個 True
Console.WriteLine(rootScope == root && rootScope == (root as IServiceScope)!.ServiceProvider);
Console.WriteLine(rootScope == rootScope3 && rootScope == rootScope4);
Console.WriteLine(currentScope == scope1 && currentScope == scope1.ServiceProvider);
Console.WriteLine(currentScope2 == scope2 && currentScope2 == scope2.ServiceProvider);
}
static object GetRoot(ServiceProvider provider)
{ //透過表示式樹獲取根scope
var p = Expression.Parameter(typeof(ServiceProvider));
//相當於 provider.Root
var lambda = Expression.Lambda<Func<ServiceProvider, object>>(Expression.Property(p, "Root"), p);
return lambda.Compile()(provider);
}
呼叫GetService<IServiceProvider>()
返回的是當前scope
,呼叫GetService<IServiceScopeFactory>()
返回的是根scope
。就算透過子scope方法scope1.ServiceProvider.CreateScope()
建立scope,最終還是呼叫new ServiceProviderEngineScope(this, isRootScope: false)
建立scope,並且子scope
之間沒有層級關係,每個子scope
都儲存了同一份ServiceProvider
物件。
再透過以下例子驗證一下,子scope是否真的儲存了ServiceProvider
物件
static void EngineScopeWhitServiceProviderTest()
{
using var services = new ServiceCollection().BuildServiceProvider();
using var scope1 = services.CreateScope()!;
using var scope2 = scope1.ServiceProvider.CreateScope()!;
var rootScope = services.GetService<IServiceProvider>();
var currentScope = scope1.ServiceProvider.GetService<IServiceProvider>();
var currentScope2 = scope2.ServiceProvider.GetService<IServiceProvider>();
var provider = GetRootProvider(scope1);//獲取子scope儲存的ServiceProvider
var provider2 = GetRootProvider(scope2);//獲取子scope儲存的ServiceProvider
//獲取根scope的ServiceProvider
var root = (IServiceScope)GetRoot(services);
var provider3 = GetRootProvider(root);
Console.WriteLine(services == provider && services == provider2 && services == provider3);
Console.WriteLine(services != rootScope && services != currentScope && services != root.ServiceProvider);
}
static object GetRootProvider(IServiceScope scope)
{ //透過表示式樹獲取RootProvider
var p = Expression.Parameter(typeof(IServiceScope));
//相當於 scope.RootProvider
var lambda = Expression.Lambda<Func<IServiceScope, object>>(
Expression.Property(Expression.Convert(p, scope.GetType()), "RootProvider"), p);
return lambda.Compile()(scope);
}
驗證沒問題。根scope
和子scope
的幾個特點:
- 都會捕獲需要釋放
Dispose
的物件 - 所有
Singleton
物件的“銷燬”由根scope
管理 - 對於
Scoped
和Transient
的物件,誰關聯誰管理(建立物件時需要傳入一個關聯scope物件)- 注意:對於
Scoped
和Transient
的物件被ServiceProvider
建立時,它們的生命週期就變成了Singleton
,它們的Dispose
方法往往需要等程式停止時才會呼叫。雖然可以在建立容器時設定引數ValidateScopes
開啟校驗,但只會校驗Scoped
物件,它只會禁止ServiceProvider
中建立Scoped物件和Singleton
物件中注入Scoped
物件,對於Transient
物件不會校驗,所以Transient
物件的Dispose
方法要等程式停止時才會呼叫。
- 注意:對於
子scope
會快取生命週期為“Scoped”的例項,並儲存到ResolvedServices
屬性中。- 雖然後面會把建立物件的委託替換成了
Emit
的形式,但是生命週期為“Scoped”的例項已經儲存到ResolvedServices
屬性中,所以後面建立的“Scoped”的例項還是同一個。
- 雖然後面會把建立物件的委託替換成了
- 對於
根scope
關聯的Singleton
和Scoped
物件,建立出來後都會快取,只是儲存在不同地方(建立物件的委託中或ServiceCallSite
中)。
問:子scope
建立的Singleton
物件是由根scope
管理的,那子scope
是怎樣獲取根scope
物件的呢?
答:前面講過,子scope
會儲存ServiceProvider
物件,訪問它的Root
屬性就可以得到根scope
。像這樣scope1.RootProvider.Root
。這兩個成員的修飾符都是internal
,我們要想訪問,可以參考前面的程式碼GetRootProvider
和GetRoot
Dispose
物件被DI容器建立出來,如果有實現Dispose
介面,就會被ServiceProviderEngineScope
捕獲並放入物件List<object>? _disposables
中,並在DI容器本身被呼叫Dispose
方法時,會逆序呼叫_disposables
中每個物件的Dispose
方法( for (int i = _disposables.Count - 1; i >= 0; i--)
),也就是呼叫scope
的Dispose
方法就會呼叫它捕獲物件的Dispose
方法。
scope
的Dispose
方法呼叫的時機:
- 對於
根scope
,也就是ServiceProvider
的Root屬性,當ServiceProvider
的Dispose
方法被呼叫時,會呼叫Root.Dispose()
- 對於
子scope
需要自己手動呼叫Dispose
方法或者用using
。
透過一段程式碼來驗證一下scope
物件是否真的捕獲了物件:
static void DisposeTest()
{
using var services = new ServiceCollection()
.AddSingleton<ISingletonService, SingletonService2>()
.AddScoped<IScopedService, ScopedService2>()
.AddTransient<ITransientService, TransientService2>()
.BuildServiceProvider();
var singleton1 = services.GetService<ISingletonService>();
var scoped1 = services.GetService<IScopedService>();
var transient1 = services.GetService<ITransientService>();
//獲取被捕獲的物件
var rootDisposables = GetRootDisposables(services);
Console.WriteLine(rootDisposables.Any(o => o == singleton1)
&& rootDisposables.Any(o => o == scoped1)
&& rootDisposables.Any(o => o == transient1)
&& rootDisposables.Count == 3);
using (var scope1 = services.CreateScope())
{
var singleton2 = scope1.ServiceProvider.GetService<ISingletonService>();
var scoped2 = scope1.ServiceProvider.GetService<IScopedService>();
var transient2 = scope1.ServiceProvider.GetService<ITransientService>();
var singleton3 = scope1.ServiceProvider.GetService<ISingletonService>();
var scoped3 = scope1.ServiceProvider.GetService<IScopedService>();
var transient3 = scope1.ServiceProvider.GetService<ITransientService>();
//獲取被捕獲的物件
var scopeDisposables = GetScopeDisposables(scope1.ServiceProvider);
//Singleton儲存在Root Scope中
Console.WriteLine(scopeDisposables.Any(o => o == singleton2) == false
&& scopeDisposables.Any(o => o == singleton3) == false
&& scopeDisposables.Count == 3);//2個Transient物件,1個Scope物件
Console.WriteLine(scopeDisposables.Any(o => o == scoped2)
&& scopeDisposables.Any(o => o == transient2)
&& scopeDisposables.Any(o => o == scoped3)
&& scopeDisposables.Any(o => o == transient3));
}
}
static List<object> GetRootDisposables(ServiceProvider provider)
{
var providerParam = Expression.Parameter(typeof(ServiceProvider));
// provider.Root
var engineParam = Expression.Property(providerParam, "Root");
// provider.Root._disposables;
var dispoParam = Expression.Field(engineParam, "_disposables");
var lambda = Expression.Lambda<Func<ServiceProvider, List<object>>>(dispoParam, providerParam);
return lambda.Compile()(provider);
}
static List<object> GetScopeDisposables(IServiceProvider provider)
{
//這個provider就是ServiceProviderEngineScope
var providerParam = Expression.Parameter(typeof(IServiceProvider));
// provider as ServiceProviderEngineScope
var engineParam = Expression.Convert(providerParam, provider.GetType());
// (provider as ServiceProviderEngineScope)._disposables;
var dispoParam = Expression.Field(engineParam, "_disposables");
var lambda = Expression.Lambda<Func<IServiceProvider, List<object>>>(dispoParam, providerParam);
return lambda.Compile()(provider);
}
輸出3個True,沒問題。
ServiceProvider
最後再介紹一下,ServiceProvider
是如何利用ServiceProviderEngineScope
和ServiceProviderEngine
建立和管理物件的。類ServiceIdentifier是DI8.0的新功能
private sealed class ServiceAccessor
{
//型別的建立資訊和快取
public ServiceCallSite? CallSite { get; set; }
//來自ServiceProviderEngine的RealizeService方法
public Func<ServiceProviderEngineScope, object?>? RealizedService { get; set; }
}
//注意,RealizeService方法的引數和返回值就是ServiceAccessor類的成員
internal sealed class ILEmitServiceProviderEngine : ServiceProviderEngine
{
public override Func<ServiceProviderEngineScope, object?> RealizeService(ServiceCallSite callSite)
{
//...
}
}
public sealed class ServiceProvider : IServiceProvider, IKeyedServiceProvider, IDisposable, IAsyncDisposable
{
//虛擬碼,但最終是new DynamicServiceProviderEngine
internal ServiceProviderEngine _engine = new DynamicServiceProviderEngine(this);
//根scope 它的IsRootScope屬性為true
internal ServiceProviderEngineScope Root { get; }
//我們獲取服務最終都是呼叫這裡
internal object? GetService(ServiceIdentifier serviceIdentifier, ServiceProviderEngineScope serviceProviderEngineScope)
{
//_serviceAccessors是一個字典,可以理解為每一個服務標識對應一個建立物件的建立物件的委託
ServiceAccessor serviceAccessor = _serviceAccessors.GetOrAdd(serviceIdentifier, CreateServiceAccessor);
//呼叫 委託 獲取物件
object? result = serviceAccessor.RealizedService?.Invoke(serviceProviderEngineScope);
return result;
}
private ServiceAccessor CreateServiceAccessor(ServiceIdentifier serviceIdentifier)
{
//GetCallSite方法獲取型別的建立資訊,指明如何建立這個型別的例項,比如透過建構函式、工廠、建立陣列(IEnumerable<>服務)、常量等建立例項
ServiceCallSite? callSite = CallSiteFactory.GetCallSite(serviceIdentifier, new CallSiteChain());
if (callSite.Cache.Location == CallSiteResultCacheLocation.Root)
{
//Singleton物件,透過反射建立一次
object? value = CallSiteRuntimeResolver.Instance.Resolve(callSite, Root);
//Resolve方法裡,在物件建立出來後會儲存到callSite裡面,如果實現dispose介面還會被Root捕獲
return new ServiceAccessor { CallSite = callSite, RealizedService = scope => value };
}
//呼叫 ServiceProviderEngine 獲取一個 建立物件的委託
Func<ServiceProviderEngineScope, object?> realizedService = _engine.RealizeService(callSite);
return new ServiceAccessor { CallSite = callSite, RealizedService = realizedService };
}
}
程式碼只保留了核心邏輯。建立物件時,所有的GetService
方法最終都是呼叫這個方法GetService(ServiceIdentifier serviceIdentifier, ServiceProviderEngineScope serviceProviderEngineScope)
。該方法接收一個“服務標識”和一個“ServiceProviderEngineScope”物件,如果是透過services.GetService
呼叫,傳遞的就是根scope
;如果是透過scope1.ServiceProvider.GetService
呼叫,傳遞的就是當前子scope
物件。
GetService
方法先透過CreateServiceAccessor
獲取“服務標識”相關資訊(ServiceCallSite
)和建立物件的委託。ServiceCallSite
前面有介紹。Singleton
物件不會透過ServiceProviderEngine
的RealizeService
方法獲取建立物件的委託,而是直接透過反射建立物件並快取起來。Scoped
和Transient
物件透過 _engine.RealizeService(callSite)
獲取建立物件的委託。最後就是呼叫委託獲取物件。
總結
.NET依賴注入中獲取物件就是透過GetService方法,方法內容主要就兩步:
- 1.獲取建立物件的委託(透過
ServiceProviderEngine
) - 2.呼叫委託建立物件
如果建立出來的物件實現了IAsyncDisposable
或者IDisposable
介面,建立出來的物件會被ServiceProviderEngineScope
捕獲用於呼叫Dispose
方法。
如有疑問,歡迎評論交流。如有錯誤,歡迎批評指出。
寫文章是真累!以後儘量多寫文章,這樣也能放在簡歷上了。