网站推广话术与技巧,网站开发合同变更,崇明网站开发,小说小程序搭建前言自从.Net Core引入IOC相关的体系之后#xff0c;关于它的讨论就从来没有停止过#xff0c;因为它是.Net Core体系的底层框架#xff0c;你只要使用了.Net Core的时候就必然会用到它。当然关于使用它的过程中产生的问题也从来没停止过。我对待问题的态度向来都是#xf…前言 自从.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.AddScopedPerson(provider new() { Id 1, Name yi念之间, Sex Man });IServiceProvider serviceProvider services.BuildServiceProvider();
using (IServiceScope serviceScope serviceProvider.CreateScope())
{var personOne serviceScope.ServiceProvider.GetServicePerson();Console.WriteLine(person.Name);
}如果在ASP.NET Core框架里那玩法就多了只要有IServiceProvide的地方都可以使用CreateScope()或CreateAsyncScope()方法简单演示一下但是如果感觉自己把握不住的话还是提前自己试验一下[HttpGet]
public async Taskobject JudgeScope([FromServices]IServiceProvider scopeProvider)
{using IServiceScope scope HttpContext.RequestServices.CreateScope();Person person scope.ServiceProvider.GetServicePerson();await using (AsyncServiceScope scope2 scopeProvider.CreateAsyncScope()){Person person2 scope2.ServiceProvider.GetServicePerson();}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.GetRequiredServiceIServiceScopeFactory().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 DictionaryServiceCacheKey, 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 DictionaryServiceCacheKey, object();//创建当前实例的根容器RootProvider provider;//标识当前作用域是否是根容器IsRootScope isRootScope;}
}下大致看一下因为接下来会涉及到ServiceProviderEngineScope这个类。到目前为止涉及到了两个比较重要的类ServiceProvider和ServiceProviderEngineScope它们都是实现了IServiceProvider接口。看起来有点乱的样子不过我们可以姑且这么理解。ServiceProvider是IServiceProvider的直系实现类作为IServiceProvider根容器的实现。ServiceProviderEngineScope是用于通过IServiceProvider创建作用域时表现出来的实例也就是非根容器的实例。探究获取方法关于上面的介绍我们其实探究了一点serviceProvider.CreateScope(),接下来我们就需要看一下关于获取的相关操作也就是GetService方法相关它的使用形式是serviceScope.ServiceProvider.GetServiceT()。上面我们提到过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 ConcurrentDictionaryType, FuncServiceProviderEngineScope, object _realizedServices;private readonly FuncType, FuncServiceProviderEngineScope, object _createServiceAccessor;internal ServiceProviderEngine _engine;internal ServiceProvider(){_createServiceAccessor CreateServiceAccessor;_realizedServices new ConcurrentDictionaryType, FuncServiceProviderEngineScope, object();}internal object GetService(Type serviceType, ServiceProviderEngineScope serviceProviderEngineScope){//判断当前实例是否释放if (_disposed){ThrowHelper.ThrowObjectDisposedException();}//缓存获取服务实例委托的字典值为要获取实例的类型值是创建实例的委托//_createServiceAccessor本质是CreateServiceAccessor方法委托FuncServiceProviderEngineScope, 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 FuncServiceProviderEngineScope, 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.AddScopedPerson(provider new() { Id 1, Name yi念之间, Sex Man }))涉及到几种方式比如AddScopedT和AddT(FuncIServiceProvider,object)我们需要知道创建类型实例的时候使用哪种方式(比如我们的Person是使用委托的这种方式)ServiceCallSite正是存储的类型和如何创建这个类型的工厂相关的信息。我们来看一下GetCallSite方法的核心操作[点击查看源码[10]]private readonly ConcurrentDictionaryServiceCacheKey, ServiceCallSite _callSiteCache new ConcurrentDictionaryServiceCacheKey, 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实例//AddScopeT()这种形式if (descriptor.ImplementationInstance ! null){callSite new ConstantCallSite(descriptor.ServiceType, descriptor.ImplementationInstance);}//AddScope(FuncIServiceProvider,object)形式else if (descriptor.ImplementationFactory ! null){callSite new FactoryCallSite(lifetime, descriptor.ServiceType, descriptor.ImplementationFactory);}//AddScopeT,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 FuncServiceProviderEngineScope, 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属性DictionaryServiceCacheKey, 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.AddScopedPerson(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.AddScopedPerson(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 IListobject Disposables _disposables ?? (IListobject)Array.Emptyobject();private bool _disposed;private Listobject _disposables;internal DictionaryServiceCacheKey, object ResolvedServices { get; }public ServiceProviderEngineScope(ServiceProvider provider, bool isRootScope){ResolvedServices new DictionaryServiceCacheKey, 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 Listobject();_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 DictionaryServiceCacheKey, object ResolvedServices { get; }
internal object Sync ResolvedServices;
private bool _disposed;
private Listobject _disposables;
public void Dispose()
{Listobject 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 Listobject 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()
{Listobject 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, Listobject 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.AddScopedPerson(provider new() { Id 1, Name yi念之间, Sex Man });IServiceProvider serviceProvider services.BuildServiceProvider();
Person person null;
using (IServiceScope serviceScope serviceProvider.CreateScope())
{person serviceScope.ServiceProvider.GetServicePerson();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