手把手教你寫DI_3_小白徒手支援 `Singleton` 和 `Sc
手把手教你寫DI_3_小白徒手支援 Singleton
和 Scoped
生命週期
在上一節:
渾身繃帶的小白同學:我們繼續開展我們的工作,大家都知道 Singleton
是什麼,就是全域性只有一個唄,我們就先從它開始,這個多簡單,我們找個字典放這些物件就ok啦
public class ServiceProvider : IServiceProvider{ ... private readonly ConcurrentDictionary<Type, object> singletonCache = new ConcurrentDictionary<Type, object>(); public object GetService(Type serviceType) { case Lifetime.Singleton: singletonCache.GetOrAdd(serviceType, x => { if(defintion is DelegateServiceDefintion defi) { return defi.ImplementationFactory(this); } else { ConstructorInfo constructor = cache.GetOrAdd(serviceType, i => { var d = defintion as TypeServiceDefintion; var implementationType = serviceType.IsConstructedGenericType ? d.ImplementationType.MakeGenericType(serviceType.GenericTypeArguments) : d.ImplementationType; return implementationType.GetConstructors().FirstOrDefault(i => i.IsPublic); }); ar ps = constructor.GetParameters(); var args = new object[ps.Length]; for (int j = 0; j < ps.Length; j++) { var p = ps[j]; args[j] = i.GetService(p.ParameterType); // 小白同學: 獲取引數值 } return constructor.Invoke(args); // 小白同學: 建立; } }); .... case Lifetime.Transient: ..... .... } }
大神:我的刀呢?
小白同學:我錯啦!!!
public class ServiceProvider : IServiceProvider{ ... private readonly ConcurrentDictionary<Type, object> singletonCache = new ConcurrentDictionary<Type, object>(); public object GetService(Type serviceType) { case Lifetime.Singleton: return singletonCache.GetOrAdd(serviceType, x => CreateObj(x)); case Lifetime.Scoped: return CreateObj(x); case Lifetime.Transient: return CreateObj(x); .... } public object CreateObj(Type serviceType) { if(defintion is DelegateServiceDefintion defi) { return defi.ImplementationFactory(this); } else { ConstructorInfo constructor = cache.GetOrAdd(serviceType, i => { var d = defintion as TypeServiceDefintion; var implementationType = serviceType.IsConstructedGenericType ? d.ImplementationType.MakeGenericType(serviceType.GenericTypeArguments) : d.ImplementationType; return implementationType.GetConstructors().FirstOrDefault(i => i.IsPublic); }); ar ps = constructor.GetParameters(); var args = new object[ps.Length]; for (int j = 0; j < ps.Length; j++) { var p = ps[j]; args[j] = i.GetService(p.ParameterType); // 小白同學: 獲取引數值 } return constructor.Invoke(args); // 小白同學: 建立; } } }
小白同學:好了,我們來說下 Scoped
作用域,百度百科的解釋是這樣的: 作用域(scope),程式設計概念,通常來說,一段程式程式碼中所用到的名字並不總是有效/可用的,而限定這個名字的可用性的程式碼範圍就是這個名字的作用域。
作用域的使用提高了程式邏輯的區域性性,增強程式的可靠性,減少名字衝突。
對於物件而言(其他也是一樣的),在main函式中,物件的作用域為他所在的最近的一對花括號內。在後花括號處解構函式被呼叫;全域性的物件的作用域為宣告之後的整個檔案,解構函式在最後被呼叫。另外,臨時產生的物件在使用完後立即會被析構。
小白同學:雖然比較奇怪為啥百度百科強調的是名字,名字不過是我們方便自己對應以及找到變數/記憶體地址等的手段而已。不過不管啦,反正DI裡面的Scoped
概念和這段解釋有點點相似,是為DI提供將物件生命週期控制在自定義的範圍內部的一個手段,比如我們保證http 一次請求的生命週期內,一些比如context之類的處理,我們就可以用這樣的作用域概念處理,
小白同學:作用域由於考慮到不是我們自己控制,這是有使用者自定的,所以我們需要提供一些抽象介面讓使用者可以使用。這裡呢,我們就偷懶啦,抄襲一下別人的定義
public interface IServiceScopeFactory{ IServiceProvider CreateScopeProvider(); }
小白同學:我們來實現它
public class ServiceScopeFactory : IServiceScopeFactory{ public IServiceProvider CreateScopeProvider() { return new ServiceProvider(); } }
小白同學:大家看,多簡單,完美
大神:你問過我的青龍偃月刀了嗎?
小白同學(尷尬): 哈哈,怎麼可能寫完了,我是開個玩笑,肯定要把服務定義給過去
public class ServiceScopeFactory : IServiceScopeFactory{ private readonly IServiceDefintions services; public ServiceScopeFactory(IServiceDefintions services) { this.services = services; } public IServiceProvider CreateScopeProvider() { return new ServiceProvider(services); } }
青龍偃月刀:你希望你的生命週期也和這個ServiceScopeFactory
一樣無處安放嗎?
小白同學:為啥?我這不是實現了嗎?
青龍偃月刀:ServiceScopeFactory
使用者從哪裡拿?
小白同學:我放進ServiceDefintions
呀,
var a = new ServiceDefintions(); a.Add(new DelegateServiceDefintion(typeof(IServiceScopeFactory),typeof(ServiceScopeFactory),Lifetime.Transient, i => new ServiceScopeFactory(a)));
青龍偃月刀:hehe, ServiceProvider
由 IServiceScopeFactory
建立的都是新的吧?
小白同學:對,就是這樣,才能保證是新的作用域呀
青龍偃月刀:hehe, 那新的 ServiceProvider
建立的物件也是新的吧?
小白同學:對,就是這樣,新的作用域建立的物件肯定和舊的作用域建立的物件肯定不一樣
青龍偃月刀:hehe, 那Singleton
不是全域性唯一嗎?
小白同學:啥?Singleton
和作用域有什麼關係?我不是有字典快取了嗎?
青龍偃月刀:我真恨不得自己再把自己磨快點。
青龍偃月刀:ServiceProvider
是不是可以建立 三種不同生命週期的物件?
小白同學:對,Singleton
,Scoped
, Transient
青龍偃月刀:那新的ServiceProvider
建立的Singleton
物件呢?
小白同學:都是從快取字典private readonly ConcurrentDictionary<Type, object> singletonCache
裡面拿唄
青龍偃月刀:。。。。。。 這個字典你放哪呢?
小白同學:我放ServiceProvider
類上啊
青龍偃月刀:。。。。。。 那每一個新的ServiceProvider
是不是都有一個新的快取字典?
小白同學:吃驚.gif, 不愧是寶刀
小白同學:我換靜態的 static ConcurrentDictionary<Type, object> singletonCache
青龍偃月刀:那整個程式就只有一份了啊
小白同學:對呀,就是隻要一份
青龍偃月刀:那一個程式裡面多個DI容器呢?
小白同學:大吃一驚.gif,還能這麼玩?
青龍偃月刀:不說其他,就說你單元測試一個DI容器能測試各種場景?
小白同學:尷尬.gif 我目前只寫了一個
青龍偃月刀:...............你改吧
小白同學:哦
// 小白同學:在IServiceProvider介面上新增我們需要資料欄位public interface IServiceProvider{ Dictionary<Type, ServiceDefintion> Services {get;} ConcurrentDictionary<Type, object> SingletonCache {get;} }public class ServiceProvider : IServiceProvider{ public Dictionary<Type, ServiceDefintion> Services {get;} public ConcurrentDictionary<Type, object> SingletonCache {get;} // 小白同學:複用對應的快取 public ServiceProvider(IServiceProvider provider) { Services = provider.Services; SingletonCache = provider.SingletonCache; } }public class ServiceScopeFactory : IServiceScopeFactory{ private readonly IServiceProvider provider; // 小白同學:這樣我們可以直接取已有的provider public ServiceScopeFactory(IServiceProvider provider) { this.provider = provider; } public IServiceProvider CreateScopeProvider() { // 小白同學:有了存在的provider,我們就能複用對應的快取 return new ServiceProvider(provider); } }
小白同學:我們就可以這樣註冊ServiceScopeFactory
了
var a = new ServiceDefintions(); a.Add(new DelegateServiceDefintion(typeof(IServiceScopeFactory),typeof(ServiceScopeFactory),Lifetime.Transient, i => new ServiceScopeFactory(i)));
青龍偃月刀:磨刀石呢?我要磨快點
小白同學:又咋了,我寫的這麼完美?
青龍偃月刀:你確定這樣符合作用域的概念?
小白同學:怎麼不符合了?SingletonCache
都只有一個了,每個ServiceProvider
都是建立新的Scoped
生命週期物件
青龍偃月刀:你看看你是怎麼寫建立新的Scoped
生命週期物件的?
小白同學:這樣啊
case Lifetime.Scoped: return CreateObj(x);
青龍偃月刀:一個Scoped
生命週期內,一個ServiceType對應生成物件不該唯一嗎?
小白同學:為啥啊?生命週期不是使用者自己控制了嗎?
青龍偃月刀:一個方法的作用域內,可以宣告多個同名物件嗎?
小白同學:不能呀
青龍偃月刀:那你允許一個Scoped
作用域內,可以生成相同ServiceType,實際不同的物件?
小白同學:他可以自己回收唄
青龍偃月刀:你讓人家自己回收 !!!??? 那人家為什麼不用Transient
,你這樣和Transient
有什麼區別?
小白同學:你說的好有道理,我竟無言以對
小白同學:那我加快取
public class ServiceProvider : IServiceProvider{ private ConcurrentDictionary<Type, object> scopedCache = new ConcurrentDictionary<Type, object>(); public object CreateObj(Type serviceType) { case Lifetime.Scoped: return scopedCache.GetOrAdd(serviceType, x => CreateObj(x)); } }
小白同學:怎麼樣?完美吧?
青龍偃月刀:我勸你好好考慮一下,我的大刀已經飢渴難耐
小白同學:哪兒不完美?明明很beautiful
青龍偃月刀:再提示一下,使用者是不是會這樣用?
IServiceProvider a = IServiceScopeFactory.CreateScopeProvider(); doSomethings(a); a.Dispose();
小白同學:對呀,可以完美應對呀
青龍偃月刀:。。。。。。。。。你的Dispose做了什麼?
小白同學:emmmm 什麼。。。 都沒做?
青龍偃月刀:那使用者Dispose
什麼?
小白同學:emmmm。。。。。。
小白同學:好吧,既然有問題我們再改下
public class ServiceProvider : IServiceProvider{ private void Dispose() { // 小白同學:Dispose every thing foreach (var item in SingletonCache.Union(scopedCache)) { var disposable = item as IDisposable; disposable?.Dispose(); } scopedCache.Clear(); SingletonCache.Clear(); } }
青龍偃月刀:........... 一個子作用域可以把SingletonCache
Dispose 了?難道活到98歲不好嗎?
小白同學:啊。。。。。活到那麼久很好啊。。。。哈,我知道怎麼改
public class ServiceProvider : IServiceProvider{ public IServiceProvider Root { get; } public ServiceProvider(IServiceProvider provider) { Services = provider.Services; SingletonCache = provider.SingletonCache; Root = provider.Root; } private void Dispose() { // 小白同學:only Root can Dispose every thing // others can onlu disposable scopedCache var disposables = (Root == this ? SingletonCache.Union(scopedCache) : scopedCache) .Where(x => x.Value != this); foreach (var scoped in disposables) { var disposable = scoped.Value as IDisposable; disposable?.Dispose(); } scopedCache.Clear(); if (Root == this) SingletonCache.Clear(); } }
小白同學:真完美!!!!!
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/4550/viewspace-2816769/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 手把手教你寫DI_2_小白徒手擼建構函式注入函式
- 手把手教你寫VueRouterVue
- 手把手教你寫蛇蛇大作戰
- 手把手教你寫設計方案
- 手把手教你寫熱修復(HOTFIX)
- 手把手教你寫 Socket 長連線
- Flutter實戰:手把手教你寫Flutter PluginFlutterPlugin
- 手把手教你寫一個SpringMVC框架SpringMVC框架
- 手把手教你寫一個react validatorReact
- 手把手教你寫vue裁切預覽元件Vue元件
- 手把手教你寫Router框架入門篇框架
- 手把手教你撰寫本科畢業論文
- 手把手教你寫一個java的orm(三)JavaORM
- 手把手教你寫一個java的orm(一)JavaORM
- 手把手教你寫一個java的orm(五)JavaORM
- 超貼心的,手把手教你寫爬蟲爬蟲
- 手把手教你寫Android 貪吃蛇 遊戲Android遊戲
- 徒手教你製作運維監控大屏運維
- 手把手教你打造支援手勢放大縮小的ImageViewView
- 10種常見OOM分析——手把手教你寫bugOOM
- 手把手教你如何寫事件處理的程式碼事件
- 手把手教你寫一個Java的orm框架(4)JavaORM框架
- 手把手教你寫一個Java的orm框架(3)JavaORM框架
- 手把手教你寫一個Java的orm框架(2)JavaORM框架
- 手把手教你寫一個Java的orm框架(1)JavaORM框架
- 手把手教你寫一個 VSCode 外掛VSCode
- 手把手教你使用nodejs編寫cli(命令列)NodeJS命令列
- 手把手教你配置git和git倉庫Git
- 手把手教你做一個超寫實爆炸特效特效
- 手把手教你寫一個微信小程式日曆元件微信小程式元件
- Vite 實戰:手把手教你寫一個 Vite 外掛Vite
- 手把手教你寫一個簡易的微前端框架前端框架
- java8 手把手教你學會寫lambda表示式Java
- 手把手教你編寫入門級redis客戶端Redis客戶端
- 手把手教你寫網路爬蟲(7):URL去重爬蟲
- 手把手教你寫網路爬蟲(4):Scrapy入門爬蟲
- 手把手教你搭建AlphaZero(使用Python和Keras)PythonKeras
- Python爬蟲:手把手教你寫迷你爬蟲架構Python爬蟲架構