前言
????自從.Net Core引入IOC相關的體系之后,關于它的討論就從來沒有停止過,因為它是.Net Core體系的底層框架,你只要使用了.Net Core的時候就必然會用到它。當然關于使用它的過程中產生的問題也從來沒停止過。我對待問題的態度向來都是,如果你踩到了坑,說明你還沒有足夠了解它,所以我們對它認知的突破,很多時候是遇到了問題并解決了問題。今天的話題呢,也是一個群友在研究.Net Core作用域的過程中產生的疑問,博主呢對這個問題也很有興趣,就借此機會探究了一下,把自己研究結果分享給大家。
簡單演示
在日常的開發中使用CreateScope()
或CreateAsyncScope()
的場景可能沒有那么多,但是在ASP.NET Core底層的話這是核心設計,在上篇文章<解決ASP.NET Core在Task中使用IServiceProvider的問題>[1]中提到過,ASP.NET Core會為每次請求創建一個Scope
,也就是咱們這次提到的作用域。使用的方法有很簡單,本質就是IServiceProvider
的一個擴展方法。咱們今天主要說的就是ServiceLifetime.Scoped
這個比較特殊的生命周期,在Scope內是如何工作的,原始點的寫法其實就是
IServiceCollection?services?=?new?ServiceCollection();
services.AddScoped<Person>(provider?=>?new()?{?Id?=?1,?Name?=?"yi念之間",?Sex?=?"Man"?});IServiceProvider?serviceProvider?=?services.BuildServiceProvider();
using?(IServiceScope?serviceScope?=?serviceProvider.CreateScope())
{var?personOne?=?serviceScope.ServiceProvider.GetService<Person>();Console.WriteLine(person.Name);
}
如果在ASP.NET Core框架里那玩法就多了,只要有IServiceProvide
的地方都可以使用CreateScope()
或CreateAsyncScope()
方法,簡單演示一下,但是如果感覺自己把握不住的話還是提前自己試驗一下
[HttpGet]
public?async?Task<object>?JudgeScope([FromServices]IServiceProvider?scopeProvider)
{using?IServiceScope?scope?=?HttpContext.RequestServices.CreateScope();Person?person?=?scope.ServiceProvider.GetService<Person>();await?using?(AsyncServiceScope?scope2?=?scopeProvider.CreateAsyncScope()){Person?person2?=?scope2.ServiceProvider.GetService<Person>();}return?person;
}
源碼探究
通過上面的示例,我們可以看到其實關于IServiceScope
的操作部分就是三個核心。
??通過
CreateScope()
或CreateAsyncScope()
方法創建服務作用域。??使用
GetService
相關的方法創建需要的對象實例。??用完了作用域之后通過使用
Dispose()
或者DisposeAsync()
方法(using的方式同理)釋放作用域。
先說AsyncServiceScope
為了怕大家心里有疑慮,因為使用CreateScope()
方法創建出來的是IServiceScope
實例,使用CreateAsyncScope
方法創建的是AsyncServiceScope
實例。咱們這里先來說一下AsyncServiceScope
和IServiceScope
的關系,看了之后大家就不用惦記它了,先來看一下CreateAsyncScope()
方法的定義[點擊查看源碼👈[2]]
public?static?AsyncServiceScope?CreateAsyncScope(this?IServiceProvider?provider)
{return?new?AsyncServiceScope(provider.CreateScope());
}
方法就是返回AsyncServiceScope
實例,接下來來看一下這個類的定義[點擊查看源碼👈[3]]
public?readonly?struct?AsyncServiceScope?:?IServiceScope,?IAsyncDisposable
{private?readonly?IServiceScope?_serviceScope;public?AsyncServiceScope(IServiceScope?serviceScope){//AsyncServiceScope也是IServiceScope實例構建起來的_serviceScope?=?serviceScope????throw?new?ArgumentNullException(nameof(serviceScope));}//ServiceProvider也是直接在IServiceScope實例中直接獲取的public?IServiceProvider?ServiceProvider?=>?_serviceScope.ServiceProvider;//同步釋放public?void?Dispose(){_serviceScope.Dispose();}//異步釋放public?ValueTask?DisposeAsync(){//因為IAsyncDisposable的ServiceProvider能繼續創建作用域//使用CreateScope或CreateAsyncScope方法if?(_serviceScope?is?IAsyncDisposable?ad){return?ad.DisposeAsync();}_serviceScope.Dispose();return?default;}
}
通過源碼我們可以看到AsyncServiceScope
本身是包裝了IServiceScope
實例,它本身也是實現了IServiceScope
接口并且同時IAsyncDisposable
接口以便可以異步調用釋放。相信大家都知道,實現了IDispose
接口可以使用using IServiceScope scope = HttpContext.RequestServices.CreateScope()
的方式,它編譯完之后其實是
IServiceScope?scope?=?HttpContext.RequestServices.CreateScope();
try
{//具體操作
}
finally
{scope.Dispose();
}
實現了IAsyncDisposable
接口可以使用await using (AsyncServiceScope scope2 = scopeProvider.CreateAsyncScope())
的方式,它編譯完的代碼則是
AsyncServiceScope?scope2?=?scopeProvider.CreateAsyncScope();
try
{//具體操作
}
finally
{await?scope2.DisposeAsync();
}
打消了這個疑慮,相信大家對它們的關系有了了解,本質就是包裝了一下IServiceScope
實例。
由創建開始
接下來我們可以專心的看一下IServiceScope
相關的實現,IServiceScope
的創建則是來自IServiceProvider
的擴展方法CreateScope()
,首先看下它的定義[點擊查看源碼👈[4]]
public?static?IServiceScope?CreateScope(this?IServiceProvider?provider)
{return?provider.GetRequiredService<IServiceScopeFactory>().CreateScope();
}
好吧,短短的一行代碼,我們可以得到兩個比較重要的信息
??首先獲取到的
IServiceScopeFactory
實例,看過上篇文章的可以知道,默認情況通過IServiceScopeFactory
實例獲取的是根容器
的實例。??其次
IServiceProvider
的CreateScope擴展方法,本質是調用的IServiceScopeFactory
的CreateScope
方法。
接下來我們就看看下IServiceScopeFactory默認實現類中關于CreateScope()
方法的定義,在ServiceProviderEngineScope
類中[點擊查看源碼👈[5]]
internal?ServiceProvider?RootProvider?{?get;?}
public?IServiceScope?CreateScope()?=>?RootProvider.CreateScope();
這里毫無疑問了RootProvider
屬性里的實例都是來自根容器,而CreateScope()
方法則是調用的ServiceProvider
的CreateScope()
方法。看下ServiceProvider
類的CreateScope方法定義 [點擊查看源碼👈[6]]
private?bool?_disposed;
internal?IServiceScope?CreateScope()
{//判斷當前ServiceProvider是否被釋放if?(_disposed){//如果已經釋放則直接拋出異常ThrowHelper.ThrowObjectDisposedException();}//創建ServiceProviderEngineScope實例return?new?ServiceProviderEngineScope(this,?isRootScope:?false);
}
通過上面的代碼我們可以看到CreateScope()
方法,本質是創建了一個ServiceProviderEngineScope
方法實例。通過創建的這一行代碼,好巧不巧又可以得到兩個重要的信息。
??一是ServiceProviderEngineScope構造函數的第一個參數,傳遞的是當前的ServiceProvider實例。
??二是ServiceProviderEngineScope構造函數的第二個參數叫
isRootScope
值給的是false
,說明當前ServiceProviderEngineScope實例不是根作用域,也就是我們說的子作用域。
大致看一下ServiceProviderEngineScope構造函數的實現[點擊查看源碼👈[7]]
internal?sealed?class?ServiceProviderEngineScope?:?IServiceScope,?IServiceProvider,?IAsyncDisposable,?IServiceScopeFactory
{internal?Dictionary<ServiceCacheKey,?object>?ResolvedServices?{?get;?}internal?object?Sync?=>?ResolvedServices;internal?ServiceProvider?RootProvider?{?get;?}public?bool?IsRootScope?{?get;?}//IServiceProvider的ServiceProvider屬性則是賦值的當前實例public?IServiceProvider?ServiceProvider?=>?this;public?ServiceProviderEngineScope(ServiceProvider?provider,?bool?isRootScope){//用來存儲當前作用域管理的對象實例ResolvedServices?=?new?Dictionary<ServiceCacheKey,?object>();//創建當前實例的根容器RootProvider?=?provider;//標識當前作用域是否是根容器IsRootScope?=?isRootScope;}
}
下大致看一下,因為接下來會涉及到ServiceProviderEngineScope
這個類。到目前為止涉及到了兩個比較重要的類ServiceProvider
和ServiceProviderEngineScope
,它們都是實現了IServiceProvider
接口。看起來有點亂的樣子,不過我們可以姑且這么理解。ServiceProvider是IServiceProvider的直系實現類,作為IServiceProvider根容器的實現。ServiceProviderEngineScope是用于,通過IServiceProvider創建作用域時表現出來的實例,也就是非根容器的實例。
探究獲取方法
關于上面的介紹,我們其實探究了一點serviceProvider.CreateScope()
,接下來我們就需要看一下關于獲取的相關操作,也就是GetService
方法相關,它的使用形式是serviceScope.ServiceProvider.GetService<T>()
。上面我們提到過ServiceProviderEngineScope
的ServiceProvider
屬性實例則是當前ServiceProviderEngineScope的實例,所以我們直接去看ServiceProviderEngineScope
的GetService
方法[點擊查看源碼👈[8]]
internal?sealed?class?ServiceProviderEngineScope?:?IServiceScope,?IServiceProvider,?IAsyncDisposable,?IServiceScopeFactory
{private?bool?_disposed;internal?ServiceProvider?RootProvider?{?get;?}public?object?GetService(Type?serviceType){//判斷當前實例是否釋放if?(_disposed){//如果已經釋放則直接拋出異常ThrowHelper.ThrowObjectDisposedException();}return?RootProvider.GetService(serviceType,?this);}
}
看著挺亂的,各種跳轉各種調用。不過本質只設計到兩個類ServiceProvider
和ServiceProviderEngineScope
,先說明一下,省的大家看著整蒙圈了。通過最后一句代碼,我們又能得到兩個比較重要的信息。
? ServiceProviderEngineScope的GetService方法,本質是在調用RootProvider的GetService方法。通過前面咱們的源碼分析可以知道
RootProvider
屬性的值是ServiceProvider
實例也就是代表的根容器。??調用RootProvider的GetService方法的時候傳遞了當前ServiceProviderEngineScope實例。
接下來就可以直接找到ServiceProvider的GetService方法了,看一下里面的具體相關實現[點擊查看源碼👈[9]]
public?sealed?class?ServiceProvider?:?IServiceProvider,?IDisposable,?IAsyncDisposable
{private?bool?_disposed;private?ConcurrentDictionary<Type,?Func<ServiceProviderEngineScope,?object>>?_realizedServices;private?readonly?Func<Type,?Func<ServiceProviderEngineScope,?object>>?_createServiceAccessor;internal?ServiceProviderEngine?_engine;internal?ServiceProvider(){_createServiceAccessor?=?CreateServiceAccessor;_realizedServices?=?new?ConcurrentDictionary<Type,?Func<ServiceProviderEngineScope,?object>>();}internal?object?GetService(Type?serviceType,?ServiceProviderEngineScope?serviceProviderEngineScope){//判斷當前實例是否釋放if?(_disposed){ThrowHelper.ThrowObjectDisposedException();}//緩存獲取服務實例委托的字典,值為要獲取實例的類型,值是創建實例的委托//_createServiceAccessor本質是CreateServiceAccessor方法委托Func<ServiceProviderEngineScope,?object>?realizedService?=?_realizedServices.GetOrAdd(serviceType,?_createServiceAccessor);OnResolve(serviceType,?serviceProviderEngineScope);DependencyInjectionEventSource.Log.ServiceResolved(this,?serviceType);//執行realizedService委托,傳遞的是ServiceProviderEngineScope實例var?result?=?realizedService.Invoke(serviceProviderEngineScope);System.Diagnostics.Debug.Assert(result?is?null?||?CallSiteFactory.IsService(serviceType));return?result;}private?Func<ServiceProviderEngineScope,?object>?CreateServiceAccessor(Type?serviceType){//獲取ServiceCallSite,其實就是獲取要解析對象的實例相關信息ServiceCallSite?callSite?=?CallSiteFactory.GetCallSite(serviceType,?new?CallSiteChain());if?(callSite?!=?null){DependencyInjectionEventSource.Log.CallSiteBuilt(this,?serviceType,?callSite);OnCreate(callSite);//咱們當前討論的是Scope周期對象的問題,這個方法描述的是生命周期為ServiceLifetime.Singleton的情況,可以跳過這個邏輯//如果是單例情況,則直接在根容器中直接去操作對象實例,和當前的ServiceProviderEngineScope無關if?(callSite.Cache.Location?==?CallSiteResultCacheLocation.Root){object?value?=?CallSiteRuntimeResolver.Instance.Resolve(callSite,?Root);return?scope?=>?value;}//解析ServiceCallSite里的信息return?_engine.RealizeService(callSite);}return?_?=>?null;}
}
這里我們看下CallSiteFactory.GetCallSite
方法,先來說一下這個方法是做啥的。我們要獲取某個類型的實例(可以理解為我們演示示例里的Person類),但是我們注冊類相關的信息的時候(比如上面的services.AddScoped<Person>(provider => new() { Id = 1, Name = "yi念之間", Sex = "Man" })
)涉及到幾種方式,比如AddScoped<T>
和Add<T>(Func<IServiceProvider,object>)
,我們需要知道創建類型實例的時候使用哪種方式(比如我們的Person是使用委托的這種方式),ServiceCallSite正是存儲的類型和如何創建這個類型的工廠相關的信息。我們來看一下GetCallSite
方法的核心操作[點擊查看源碼👈[10]]
private?readonly?ConcurrentDictionary<ServiceCacheKey,?ServiceCallSite>?_callSiteCache?=?new?ConcurrentDictionary<ServiceCacheKey,?ServiceCallSite>();private?ServiceCallSite?TryCreateExact(ServiceDescriptor?descriptor,?Type?serviceType,?CallSiteChain?callSiteChain,?int?slot)
{if?(serviceType?==?descriptor.ServiceType){//要獲取的類型會被包裝成ServiceCacheKeyServiceCacheKey?callSiteKey?=?new?ServiceCacheKey(serviceType,?slot);//在緩存中獲取ServiceCallSite實例,可以理解為設計模式中的享元模式if?(_callSiteCache.TryGetValue(callSiteKey,?out?ServiceCallSite?serviceCallSite)){return?serviceCallSite;}ServiceCallSite?callSite;//根據ServiceDescriptor.Lifetime包裝ResultCachevar?lifetime?=?new?ResultCache(descriptor.Lifetime,?serviceType,?slot);//ServiceDescriptor就是我們添加到IServiceCollection的最終形式//我們注冊服務的時候本質就是在IServiceCollection里添加ServiceDescriptor實例//AddScope<T>()這種形式if?(descriptor.ImplementationInstance?!=?null){callSite?=?new?ConstantCallSite(descriptor.ServiceType,?descriptor.ImplementationInstance);}//AddScope(Func<IServiceProvider,object>)形式else?if?(descriptor.ImplementationFactory?!=?null){callSite?=?new?FactoryCallSite(lifetime,?descriptor.ServiceType,?descriptor.ImplementationFactory);}//AddScope<T,TImpl>()形式else?if?(descriptor.ImplementationType?!=?null){callSite?=?CreateConstructorCallSite(lifetime,?descriptor.ServiceType,?descriptor.ImplementationType,?callSiteChain);}else{throw?new?InvalidOperationException(SR.InvalidServiceDescriptor);}//將創建的ServiceCallSite緩存起來return?_callSiteCache[callSiteKey]?=?callSite;}return?null;
}
而解析ServiceCallSite
實例的方法RealizeService(ServiceCallSite)
則是在ServiceProviderEngine
類中,看一下相關實現[點擊查看源碼👈[11]]
public?override?Func<ServiceProviderEngineScope,?object>?RealizeService(ServiceCallSite?callSite)
{int?callCount?=?0;return?scope?=>{//核心代碼是Resolve方法,這里的scope則是ServiceProviderEngineScope//即我們上面通過CreateScope()創建的實例var?result?=?CallSiteRuntimeResolver.Instance.Resolve(callSite,?scope);if?(Interlocked.Increment(ref?callCount)?==?2){_?=?ThreadPool.UnsafeQueueUserWorkItem(_?=>{try{_serviceProvider.ReplaceServiceAccessor(callSite,?base.RealizeService(callSite));}catch?(Exception?ex){//省略掉非核心代碼}},null);}return?result;};
}
上面我們看到的RealizeService()
方法返回的是一個委托,而調用這個委托的地方則是上面源碼中看到的realizedService.Invoke(serviceProviderEngineScope)
,核心操作在CallSiteRuntimeResolver.Instance.Resolve()
方法,Resolve方法的核心邏輯在VisitCallSite()
方法,看一下它的實現方式[點擊查看源碼👈[12]]
protected?virtual?TResult?VisitCallSite(ServiceCallSite?callSite,?TArgument?argument)
{if?(!_stackGuard.TryEnterOnCurrentStack()){return?_stackGuard.RunOnEmptyStack((c,?a)?=>?VisitCallSite(c,?a),?callSite,?argument);}switch?(callSite.Cache.Location){//ServiceLifetime.Singleton單例情況case?CallSiteResultCacheLocation.Root:return?VisitRootCache(callSite,?argument);//ServiceLifetime.Scoped作用域情況,也就是咱們關注的情況case?CallSiteResultCacheLocation.Scope:return?VisitScopeCache(callSite,?argument);//ServiceLifetime.Transient瞬時情況case?CallSiteResultCacheLocation.Dispose:return?VisitDisposeCache(callSite,?argument);case?CallSiteResultCacheLocation.None:return?VisitNoCache(callSite,?argument);default:throw?new?ArgumentOutOfRangeException();}
}
因為我們關注的是CallSiteResultCacheLocation.Scope
這種情況所以我們重點關注的是VisitScopeCache()
這段方法邏輯,CallSiteRuntimeResolver的VisitCache()
方法[點擊查看源碼👈[13]]
protected?override?object?VisitScopeCache(ServiceCallSite?callSite,?RuntimeResolverContext?context)
{//咱們關注的是Scope的情況,所以重點在VisitCache方法return?context.Scope.IsRootScope??VisitRootCache(callSite,?context)?:VisitCache(callSite,?context,?context.Scope,?RuntimeResolverLock.Scope);
}//這里的serviceProviderEngine參數就是我們傳遞進來的ServiceProviderEngineScope實例
private?object?VisitCache(ServiceCallSite?callSite,?RuntimeResolverContext?context,?ServiceProviderEngineScope?serviceProviderEngine,?RuntimeResolverLock?lockType)
{bool?lockTaken?=?false;//獲取ServiceProviderEngineScope的Sync屬性object?sync?=?serviceProviderEngine.Sync;//獲取ServiceProviderEngineScope的ResolvedServices屬性Dictionary<ServiceCacheKey,?object>?resolvedServices?=?serviceProviderEngine.ResolvedServices;//加鎖if?((context.AcquiredLocks?&?lockType)?==?0){Monitor.Enter(sync,?ref?lockTaken);}try{//判斷ServiceProviderEngineScope的ResolvedServices的屬性里是否包含該類型實例//當前作用域內只有一個實例,所以緩存起來if?(resolvedServices.TryGetValue(callSite.Cache.Key,?out?object?resolved)){return?resolved;}//當前Scope沒創建過實例的話則創建resolved?=?VisitCallSiteMain(callSite,?new?RuntimeResolverContext{Scope?=?serviceProviderEngine,AcquiredLocks?=?context.AcquiredLocks?|?lockType});//判斷當前類型實例是否是IDispose想實例serviceProviderEngine.CaptureDisposable(resolved);//給ServiceProviderEngineScope的ResolvedServices的屬性添加緩存實例resolvedServices.Add(callSite.Cache.Key,?resolved);return?resolved;}finally{if?(lockTaken){Monitor.Exit(sync);}}
}protected?virtual?TResult?VisitCallSiteMain(ServiceCallSite?callSite,?TArgument?argument)
{//比如我們上面的services.AddScoped<Person>(provider?=>?new()?{?Id?=?1,?Name?=?"yi念之間",?Sex?=?"Man"?})//對應的Kind則是CallSiteKind.Factoryswitch?(callSite.Kind){case?CallSiteKind.Factory://調用了VisitFactory方法return?VisitFactory((FactoryCallSite)callSite,?argument);case??CallSiteKind.IEnumerable:return?VisitIEnumerable((IEnumerableCallSite)callSite,?argument);case?CallSiteKind.Constructor:return?VisitConstructor((ConstructorCallSite)callSite,?argument);case?CallSiteKind.Constant:return?VisitConstant((ConstantCallSite)callSite,?argument);case?CallSiteKind.ServiceProvider:return?VisitServiceProvider((ServiceProviderCallSite)callSite,?argument);default:throw?new?NotSupportedException(SR.Format(SR.CallSiteTypeNotSupported,?callSite.GetType()));}
}protected?override?object?VisitFactory(FactoryCallSite?factoryCallSite,?RuntimeResolverContext?context)
{//調用我們注冊的services.AddScoped<Person>(provider?=>?new()?{?Id?=?1,?Name?=?"yi念之間",?Sex?=?"Man"?})//FactoryCallSite的Factory即是provider?=>?new()?{?Id?=?1,?Name?=?"yi念之間",?Sex?=?"Man"?}//context.Scope則是我們通過CreateScope創建的實例//返回的結果就是調用當前委托得到的實例即我們實例中的Person實例return?factoryCallSite.Factory(context.Scope);
}
回過頭來說一下咱們上面展示的代碼,被調用執行的地方就是GetService
方法里的realizedService.Invoke(serviceProviderEngineScope)
的這段代碼。上面的執行邏輯里涉及到了ServiceProviderEngineScope
里的幾個操作,比如ResolvedServices
屬性和CaptureDisposable()
方法,看一下相關的代碼
internal?sealed?class?ServiceProviderEngineScope?:?IServiceScope,?IServiceProvider,?IAsyncDisposable,?IServiceScopeFactory
{internal?IList<object>?Disposables?=>?_disposables????(IList<object>)Array.Empty<object>();private?bool?_disposed;private?List<object>?_disposables;internal?Dictionary<ServiceCacheKey,?object>?ResolvedServices?{?get;?}public?ServiceProviderEngineScope(ServiceProvider?provider,?bool?isRootScope){ResolvedServices?=?new?Dictionary<ServiceCacheKey,?object>();}internal?object?CaptureDisposable(object?service){//判斷實例是否實現了IDisposable或IAsyncDisposable接口,因為這種需要在當前作用域是否的時候一起釋放if?(ReferenceEquals(this,?service)?||?!(service?is?IDisposable?||?service?is?IAsyncDisposable)){return?service;}bool?disposed?=?false;lock?(Sync){//判斷當前作用域是否釋放if?(_disposed){disposed?=?true;}else{???//如果滿足則添加到_disposables待釋放集合,以便作用域釋放的時候一起釋放_disposables???=?new?List<object>();_disposables.Add(service);}}//如果當前作用域已經被釋放則直接釋放當前實例if?(disposed){//前提是服務實例是實現IDisposable或IAsyncDisposable接口的if?(service?is?IDisposable?disposable){disposable.Dispose();}else{Task.Run(()?=>?((IAsyncDisposable)service).DisposeAsync().AsTask()).GetAwaiter().GetResult();}ThrowHelper.ThrowObjectDisposedException();}return?service;}
}
上面關于ServiceProviderEngineScope
類中涉及到GetService()
方法的相關邏輯已經展示的差不多了,涉及到的比較多,而且看著比較亂。不過如果理解了思路還是比較清晰的,咱們來做一下一個大概的總結。
??首先,需要獲取ServiceCallSite,在方法
GetCallSite()
中,其實就是獲取要解析對象的實例相關信息。我們需要知道創建類型實例的時候使用哪種方式(比如我們的Person是使用委托的這種方式),其中也包括該對象創建的類型、創建工廠、生命周期類型。??然后,得到ServiceCallSite實例之后,我們就可以通過實例創建的信息去創建信息,在方法
RealizeService()
里。根據不同類型創建方式和生命周期,判斷如何創建對象,即對象存放位置。??最后,如果是單例模式,則在根容器中解析這個對象,位置當然也是存儲在根容器中,全局唯一。如果是瞬時模式,則直接返回創建的對象實例,不進行任何存儲,但是需要判斷實例是否實現了IDisposable或IAsyncDisposable接口,如果是則加入當前ServiceProviderEngineScope實例的_disposables集合。如果是Scope模式就比較特殊了,因為Scope需要在當前ServiceProviderEngineScope中存儲保證當前作用域唯一,則需要添加到ResolvedServices屬性的字典里,同時也需要判斷是否需要添加到_disposables集合里。
這就可以解釋ServiceProviderEngineScope針對不同生命周期的存儲方式了,單例的情況創建和存儲都是在根容器中,瞬時的情況下則每次都創建新的實例且不進行存儲,Scope的情況下則是存儲在當前的ResolvedServices中享元起來可以在當前作用域內重復使用。
關于結束釋放
前面咱們看了下關于作用域創建,在做用戶獲取對象的相關邏輯。接下來我們來看一下三件套的最后一個步驟,釋放邏輯相關的。這個邏輯比較簡單,上面咱們或多或少的也說過了一點,釋放分為同步釋放和異步釋放兩種情況,咱們看一下同步釋放的相關實現[點擊查看源碼👈[14]]
internal?Dictionary<ServiceCacheKey,?object>?ResolvedServices?{?get;?}
internal?object?Sync?=>?ResolvedServices;
private?bool?_disposed;
private?List<object>?_disposables;
public?void?Dispose()
{List<object>?toDispose?=?BeginDispose();if?(toDispose?!=?null){for?(int?i?=?toDispose.Count?-?1;?i?>=?0;?i--){???//模仿棧模式,最后創建的最先釋放if?(toDispose[i]?is?IDisposable?disposable){//釋放的正式實現了IDisposable接口的對象disposable.Dispose();}else{throw?new?InvalidOperationException(SR.Format(SR.AsyncDisposableServiceDispose,?TypeNameHelper.GetTypeDisplayName(toDispose[i])));}}}
}private?List<object>?BeginDispose()
{//本質就是鎖住當前存儲對象的集合,不允許進行任何操作lock?(Sync){//如果已經釋放過了則直接返回if?(_disposed){return?null;}DependencyInjectionEventSource.Log.ScopeDisposed(RootProvider.GetHashCode(),?ResolvedServices.Count,?_disposables?.Count????0);//先把釋放標識設置了_disposed?=?true;}//判斷是否是根容器釋放if?(IsRootScope?&&?!RootProvider.IsDisposed()){RootProvider.Dispose();}return?_disposables;
}
其實主要邏輯就是循環釋放_disposables
里的所有對象,也就是實現了IDisposable
接口的對象。接下來咱們再來看一下異步釋放的相關邏輯。
public?ValueTask?DisposeAsync()
{List<object>?toDispose?=?BeginDispose();if?(toDispose?!=?null){try{for?(int?i?=?toDispose.Count?-?1;?i?>=?0;?i--){object?disposable?=?toDispose[i];//判斷是否是實現了IAsyncDisposable接口的對象if?(disposable?is?IAsyncDisposable?asyncDisposable){//獲取DisposeAsync方法返回值也就是ValueTaskValueTask?vt?=?asyncDisposable.DisposeAsync();if?(!vt.IsCompletedSuccessfully){return?Await(i,?vt,?toDispose);}//阻塞等待DisposeAsync執行完成vt.GetAwaiter().GetResult();}else{((IDisposable)disposable).Dispose();}}}catch?(Exception?ex){return?new?ValueTask(Task.FromException(ex));}}return?default;static?async?ValueTask?Await(int?i,?ValueTask?vt,?List<object>?toDispose){//等待DisposeAsync方法里的邏輯執行完成await?vt.ConfigureAwait(false);i--;for?(;?i?>=?0;?i--){object?disposable?=?toDispose[i];if?(disposable?is?IAsyncDisposable?asyncDisposable){//等待DisposeAsync執行完成await?asyncDisposable.DisposeAsync().ConfigureAwait(false);}else{((IDisposable)disposable).Dispose();}}}
}
其實核心邏輯和同步釋放是一致的,只是IAsyncDisposable
接口中的DisposeAsync()
方法返回的異步相關的ValueTask所以需要進行一些等待相關的操作。不過本質都是循環釋放_disposables
里的數據,而這些數據正是當前作用域里里實現了IDisposable或IAsyncDisposable接口的實例。
使用
CreateScope()
或GetService()
方法的時候,都會判斷當前作用域是否釋放,而這個標識正是在Dispose()
或DisposeAsync()
置為true
的。我們上面文章中的那個異常的引發點也正是這里,也正是因為作用域被釋放了表示為置為true
了,所以GetService才會直接拋出異常。
群友問題解答
關于ServiceProviderEngineScope
重要的相關實現,咱們上面已經大致的講解過了。其實探索它的原動力就是因為群友遇到的一些關于這方面的疑問,如果了解了它的實現的話便能輕松的解除心中的疑問,還原一下當時有疑問的代碼
IServiceCollection?services?=?new?ServiceCollection();
services.AddScoped<Person>(provider?=>?new()?{?Id?=?1,?Name?=?"yi念之間",?Sex?=?"Man"?});IServiceProvider?serviceProvider?=?services.BuildServiceProvider();
Person?person?=?null;
using?(IServiceScope?serviceScope?=?serviceProvider.CreateScope())
{person?=?serviceScope.ServiceProvider.GetService<Person>();Console.WriteLine(person.Name);
}
if?(person?==?null)
{Console.WriteLine("Person被回收了");
}
代碼大致描述的就是當時的這么一個場景,這里毫無疑問哈,完全判斷不出來Person是否已經被回收。通過上面的源碼咱們就可以知道,無論是掉用ServiceProviderEngineScope
的是Dispose()
或DisposeAsync()
方法(using上面咱們說過了就是語法糖),其實都是調用了當前作用域內實現了IDisposable
或IAsyncDisposable
接口的實例里的Dispose()
或DisposeAsync()
方法。
??即使當前實例實現了
IDisposable
或IAsyncDisposable
接口,且調用了實例內的Dispose()
或DisposeAsync()
方法,也不意味著當前對象已經被釋放了,因為我們用Dispose方法里多半是這個對象里引用的非托管代碼的釋放工作,并不意味這當前對象被釋放了。??
IServiceScope
實現類ServiceProviderEngineScope
里ResolvedServices屬性享元的實例,也就是生命周期為ServiceLifetime.Scoped
的實例。這些實例何時被回收是取決于兩點,一是當前實例的訪問是否超出當前作用域,二是當前對象是否有被引用。上面的示例中IServiceScope
實例雖然已經超出作用了(因為在using括號之外了),但是Person外出的棧里還引用著ResolvedServices字典里Person對象的實例,所以GC
即垃圾回收機制并不會回收這個實例,因為它還在被引用。那就意味著它不能被釋放,也就不存在Person實例被回收這么一說了。
所以,上面的問題說起來就是IServiceScope主要解決的是對象取的問題,也就是我用我的字典屬性保留了需要保留的對象實例,可以釋放被聲明可以釋放的操作(比如非托管資源的釋放)。但是作用域本身的回收和內部管理的對象的回收是交給GC來負責的。
細想一下就會明白了,托管對象的回收本身就是垃圾回收處理的,就和你自己寫單例模式或者直接new一個對象實例的時候,你也沒考慮對象的回收問題,因為垃圾回收機制已經幫你處理了。
總結
????在.Net Core體系中IOC一直是核心模塊,且關于Scope的作用域的問題,一直會有人產生疑問,想更深刻的了解一下還是得多拿一些時間研究一下。有些知識不是靠一時半會的學就能牢牢地掌握,需要日常不斷的積累和不斷的解決問題,才能掌握的更多。因為設計到的源碼比較多,而且不熟悉的話可能不是很好理解,所以還需要平時的積累,積累的越多能解決的問題越多,才能避免入坑。好了大致總結一下
??當我們使用
CreateScope()
或CreateAsyncScope()
創建出ServiceProviderEngineScope
或AsyncServiceScope
實例的時候,即我們通常描述的作用域。這個實例里包含了ResolvedServices
屬性和Disposables
屬性,分別保存當前作用域內即生命周期為ServiceLifetime.Scoped
實例和實現了IDisposable
或IAsyncDisposable
接口的實例。??使用
GetService()
方法在當前作用域內獲取實例的時候,會根據服務注冊時使用的生命周期判斷是否加入到當前作用域里享元的實例。其中單例來自于根容器,瞬時的每次都需要創建新的實例所以不需要保存,只有生命周期為ServiceLifetime.Scoped
才保存。瞬時的和Scope的對象創建出來的時候都會判斷是否實現了IDisposable
或IAsyncDisposable
接口,如果是則加入到Disposables
屬性的集合里用于釋放。??當前作用域被釋放的時候,即調用
IServiceScope
實例Dispose()
相關方法的時候,會遍歷Disposables
集合里的對象進行Dispose相關方法調用,并不是回收托管到當前作用域內的對象,因為對象何時被回收取決于GC
即垃圾回收機制。
好了到這里差不多,歡迎大家多多交流。寒冬已至,希望大家都有御寒的方法。分享一下看到過的一句話。你能得到的最牢靠的一定得是依靠你自身實力建立起來的,而不是你所處的平臺建立起來的,因為依賴平臺建立起來的,離開這個平臺會打折。
引用鏈接
[1]
?<解決ASP.NET Core在Task中使用IServiceProvider的問題>:?https://www.cnblogs.com/wucy/p/16566495.html[2]
?點擊查看源碼👈:?https://github.com/dotnet/runtime/blob/v6.0.10/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/src/ServiceProviderServiceExtensions.cs#L134[3]
?點擊查看源碼👈:?https://github.com/dotnet/runtime/blob/v6.0.10/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/src/AsyncServiceScope.cs[4]
?點擊查看源碼👈:?https://github.com/dotnet/runtime/blob/v6.0.10/src/libraries/Microsoft.Extensions.DependencyInjection.Abstractions/src/ServiceProviderServiceExtensions.cs#L124[5]
?點擊查看源碼👈:?https://github.com/dotnet/runtime/blob/v6.0.10/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ServiceProviderEngineScope.cs#L49[6]
?點擊查看源碼👈:?https://github.com/dotnet/runtime/blob/v6.0.10/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceProvider.cs#L182[7]
?點擊查看源碼👈:?https://github.com/dotnet/runtime/blob/v6.0.10/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ServiceProviderEngineScope.cs#L19[8]
?點擊查看源碼👈:?https://github.com/dotnet/runtime/blob/v6.0.10/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ServiceProviderEngineScope.cs#L37[9]
?點擊查看源碼👈:?https://github.com/dotnet/runtime/blob/v6.0.10/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceProvider.cs#L120[10]
?點擊查看源碼👈:?https://github.com/dotnet/runtime/blob/v6.0.10/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteFactory.cs#L313[11]
?點擊查看源碼👈:?https://github.com/dotnet/runtime/blob/v6.0.10/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/DynamicServiceProviderEngine.cs#L19[12]
?點擊查看源碼👈:?https://github.com/dotnet/runtime/blob/v6.0.10/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteVisitor.cs#L17[13]
?點擊查看源碼👈:?https://github.com/dotnet/runtime/blob/v6.0.10/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteRuntimeResolver.cs#L111[14]
?點擊查看源碼👈:?https://github.com/dotnet/runtime/blob/v6.0.10/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ServiceProviderEngineScope.cs#L92