手把手教你写DI_3_小白徒手支持 Singleton
和 Scoped
生命周期
在上一节:手把手教你写DI_2_小白徒手撸构造函数注入
浑身绷带的小白同学:我们继续开展我们的工作,大家都知道 Singleton
是什么,就是全局只有一个呗,我们就先从它开始,这个多简单,我们找个字典放这些对象就ok啦
public class ServiceProvider : IServiceProvider
{...private readonly ConcurrentDictionary
大神:我的刀呢?
小白同学:我错啦!!!
小白同学:好了,我们来说下 小白同学:虽然比较奇怪为啥百度百科强调的是名字,名字不过是我们方便自己对应以及找到变量/内存地址等的手段而已。不过不管啦,反正DI里面的 小白同学:作用域由于考虑到不是我们自己控制,这是有使用者自定的,所以我们需要提供一些抽象接口让用户可以使用。这里呢,我们就偷懒啦,抄袭一下别人的定义 小白同学:我们来实现它 小白同学:大家看,多简单,完美 大神:你问过我的青龙偃月刀了吗? 小白同学(尴尬): 哈哈,怎么可能写完了,我是开个玩笑,肯定要把服务定义给过去 青龙偃月刀:你希望你的生命周期也和这个 小白同学:为啥?我这不是实现了吗? 青龙偃月刀: 小白同学:我放进 青龙偃月刀:hehe, 小白同学:对,就是这样,才能保证是新的作用域呀 青龙偃月刀:hehe, 那新的 小白同学:对,就是这样,新的作用域创建的对象肯定和旧的作用域创建的对象肯定不一样 青龙偃月刀:hehe, 那 小白同学:啥? 青龙偃月刀:我真恨不得自己再把自己磨快点。 青龙偃月刀: 小白同学:对, 青龙偃月刀:那新的 小白同学:都是从缓存字典 青龙偃月刀:。。。。。。 这个字典你放哪呢? 小白同学:我放 青龙偃月刀:。。。。。。 那每一个新的 小白同学:吃惊.gif, 不愧是宝刀 小白同学:我换静态的 青龙偃月刀:那整个程序就只有一份了啊 小白同学:对呀,就是只要一份 青龙偃月刀:那一个程序里面多个DI容器呢? 小白同学:大吃一惊.gif,还能这么玩? 青龙偃月刀:不说其他,就说你单元测试一个DI容器能测试各种场景? 小白同学:尴尬.gif 我目前只写了一个 青龙偃月刀:...............你改吧 小白同学:哦 小白同学:我们就可以这样注册 青龙偃月刀:磨刀石呢?我要磨快点 小白同学:又咋了,我写的这么完美? 青龙偃月刀:你确定这样符合作用域的概念? 小白同学:怎么不符合了? 青龙偃月刀:你看看你是怎么写创建新的 小白同学:这样啊 青龙偃月刀:一个 小白同学:为啥啊?生命周期不是用户自己控制了吗? 青龙偃月刀:一个方法的作用域内,可以声明多个同名对象吗? 小白同学:不能呀 青龙偃月刀:那你允许一个 小白同学:他可以自己回收呗 青龙偃月刀:你让人家自己回收 !!!??? 那人家为什么不用 小白同学:你说的好有道理,我竟无言以对 小白同学:那我加缓存 小白同学:怎么样?完美吧? 青龙偃月刀:我劝你好好考虑一下,我的大刀已经饥渴难耐 小白同学:哪儿不完美?明明很beautiful 青龙偃月刀:再提示一下,用户是不是会这样用? 小白同学:对呀,可以完美应对呀 青龙偃月刀:。。。。。。。。。你的Dispose做了什么? 小白同学:emmmm 什么。。。 都没做? 青龙偃月刀:那用户 小白同学:emmmm。。。。。。 小白同学:好吧,既然有问题我们再改下 青龙偃月刀:........... 一个子作用域可以把 小白同学:啊。。。。。活到那么久很好啊。。。。哈,我知道怎么改 小白同学:真完美!!!!! 青龙偃月刀:呵呵,这样也能算完美?多少没做,还有多少问题没搞?你看人家做成这样子都算差的了 - https://github.com/fs7744/Nornspublic class ServiceProvider : IServiceProvider
{...private readonly ConcurrentDictionaryScoped
作用域,百度百科的解释是这样的: 作用域(scope),程序设计概念,通常来说,一段程序代码中所用到的名字并不总是有效/可用的,而限定这个名字的可用性的代码范围就是这个名字的作用域。
作用域的使用提高了程序逻辑的局部性,增强程序的可靠性,减少名字冲突。
对于对象而言(其他也是一样的),在main函数中,对象的作用域为他所在的最近的一对花括号内。在后花括号处析构函数被调用;全局的对象的作用域为声明之后的整个文件,析构函数在最后被调用。另外,临时产生的对象在使用完后立即会被析构。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)));ServiceProvider
由 IServiceScopeFactory
创建的都是新的吧?ServiceProvider
创建的对象也是新的吧?Singleton
不是全局唯一吗?Singleton
和作用域有什么关系?我不是有字典缓存了吗?ServiceProvider
是不是可以创建 三种不同生命周期的对象?Singleton
,Scoped
, Transient
ServiceProvider
创建的Singleton
对象呢?private readonly ConcurrentDictionary
里面拿呗ServiceProvider
类上啊ServiceProvider
是不是都有一个新的缓存字典?static ConcurrentDictionary
// 小白同学:在IServiceProvider接口上添加我们需要数据字段
public interface IServiceProvider
{Dictionary
}public class ServiceProvider : IServiceProvider
{public Dictionary
}public class ServiceScopeFactory : IServiceScopeFactory
{private readonly IServiceProvider provider;// 小白同学:这样我们可以直接取已有的providerpublic 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
}IServiceProvider a = IServiceScopeFactory.CreateScopeProvider();doSomethings(a);a.Dispose();
Dispose
什么?public class ServiceProvider : IServiceProvider
{private void Dispose(){// 小白同学:Dispose every thingforeach (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 scopedCachevar 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();}
}