热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

手把手教你写DI_3_小白徒手支持`Singleton`和`Scoped`生命周期

手把手教你写DI_3_小白徒手支持Singleton和Scoped生命周期在上一节:手把手教你写DI_2_小白徒手撸构造函数注入浑身绷带的小白同学:我们

手把手教你写DI_3_小白徒手支持 SingletonScoped 生命周期

在上一节:手把手教你写DI_2_小白徒手撸构造函数注入

浑身绷带的小白同学:我们继续开展我们的工作,大家都知道 Singleton是什么,就是全局只有一个呗,我们就先从它开始,这个多简单,我们找个字典放这些对象就ok啦

public class ServiceProvider : IServiceProvider
{...private readonly ConcurrentDictionary singletonCache = new ConcurrentDictionary();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 }

大神:我的刀呢?

小白同学:我错啦!!!

public class ServiceProvider : IServiceProvider
{...private readonly ConcurrentDictionary singletonCache = new ConcurrentDictionary();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

小白同学:好了,我们来说下 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, ServiceProviderIServiceScopeFactory 创建的都是新的吧?

小白同学:对,就是这样,才能保证是新的作用域呀

青龙偃月刀:hehe, 那新的 ServiceProvider 创建的对象也是新的吧?

小白同学:对,就是这样,新的作用域创建的对象肯定和旧的作用域创建的对象肯定不一样

青龙偃月刀:hehe, 那Singleton不是全局唯一吗?

小白同学:啥?Singleton和作用域有什么关系?我不是有字典缓存了吗?

青龙偃月刀:我真恨不得自己再把自己磨快点。

青龙偃月刀:ServiceProvider 是不是可以创建 三种不同生命周期的对象?

小白同学:对,Singleton ,Scoped, Transient

青龙偃月刀:那新的ServiceProvider创建的Singleton对象呢?

小白同学:都是从缓存字典private readonly ConcurrentDictionary singletonCache 里面拿呗

青龙偃月刀:。。。。。。 这个字典你放哪呢?

小白同学:我放ServiceProvider类上啊

青龙偃月刀:。。。。。。 那每一个新的ServiceProvider是不是都有一个新的缓存字典?

小白同学:吃惊.gif, 不愧是宝刀

小白同学:我换静态的 static ConcurrentDictionary singletonCache

青龙偃月刀:那整个程序就只有一份了啊

小白同学:对呀,就是只要一份

青龙偃月刀:那一个程序里面多个DI容器呢?

小白同学:大吃一惊.gif,还能这么玩?

青龙偃月刀:不说其他,就说你单元测试一个DI容器能测试各种场景?

小白同学:尴尬.gif 我目前只写了一个

青龙偃月刀:...............你改吧

小白同学:哦

// 小白同学:在IServiceProvider接口上添加我们需要数据字段
public interface IServiceProvider
{Dictionary Services {get;}ConcurrentDictionary SingletonCache {get;}
}public class ServiceProvider : IServiceProvider
{public Dictionary Services {get;}public ConcurrentDictionary SingletonCache {get;}// 小白同学:复用对应的缓存public ServiceProvider(IServiceProvider provider){Services = provider.Services;SingletonCache = provider.SingletonCache;}
}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 scopedCache = new ConcurrentDictionary();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 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();}
}

小白同学:真完美!!!!!

青龙偃月刀:呵呵,这样也能算完美?多少没做,还有多少问题没搞?你看人家做成这样子都算差的了 - https://github.com/fs7744/Norns

转:https://www.cnblogs.com/fs7744/p/9931141.html



推荐阅读
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • ZSI.generate.Wsdl2PythonError: unsupported local simpleType restriction ... [详细]
  • 本文介绍了如何使用C#制作Java+Mysql+Tomcat环境安装程序,实现一键式安装。通过将JDK、Mysql、Tomcat三者制作成一个安装包,解决了客户在安装软件时的复杂配置和繁琐问题,便于管理软件版本和系统集成。具体步骤包括配置JDK环境变量和安装Mysql服务,其中使用了MySQL Server 5.5社区版和my.ini文件。安装方法为通过命令行将目录转到mysql的bin目录下,执行mysqld --install MySQL5命令。 ... [详细]
  • 本文介绍了iOS数据库Sqlite的SQL语句分类和常见约束关键字。SQL语句分为DDL、DML和DQL三种类型,其中DDL语句用于定义、删除和修改数据表,关键字包括create、drop和alter。常见约束关键字包括if not exists、if exists、primary key、autoincrement、not null和default。此外,还介绍了常见的数据库数据类型,包括integer、text和real。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 本文介绍了C#中生成随机数的三种方法,并分析了其中存在的问题。首先介绍了使用Random类生成随机数的默认方法,但在高并发情况下可能会出现重复的情况。接着通过循环生成了一系列随机数,进一步突显了这个问题。文章指出,随机数生成在任何编程语言中都是必备的功能,但Random类生成的随机数并不可靠。最后,提出了需要寻找其他可靠的随机数生成方法的建议。 ... [详细]
  • PHP中的单例模式与静态变量的区别及使用方法
    本文介绍了PHP中的单例模式与静态变量的区别及使用方法。在PHP中,静态变量的存活周期仅仅是每次PHP的会话周期,与Java、C++不同。静态变量在PHP中的作用域仅限于当前文件内,在函数或类中可以传递变量。本文还通过示例代码解释了静态变量在函数和类中的使用方法,并说明了静态变量的生命周期与结构体的生命周期相关联。同时,本文还介绍了静态变量在类中的使用方法,并通过示例代码展示了如何在类中使用静态变量。 ... [详细]
  • 怀疑是每次都在新建文件,具体代码如下 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • 计算机存储系统的层次结构及其优势
    本文介绍了计算机存储系统的层次结构,包括高速缓存、主存储器和辅助存储器三个层次。通过分层存储数据可以提高程序的执行效率。计算机存储系统的层次结构将各种不同存储容量、存取速度和价格的存储器有机组合成整体,形成可寻址存储空间比主存储器空间大得多的存储整体。由于辅助存储器容量大、价格低,使得整体存储系统的平均价格降低。同时,高速缓存的存取速度可以和CPU的工作速度相匹配,进一步提高程序执行效率。 ... [详细]
  • 本文讨论了在openwrt-17.01版本中,mt7628设备上初始化启动时eth0的mac地址总是随机生成的问题。每次随机生成的eth0的mac地址都会写到/sys/class/net/eth0/address目录下,而openwrt-17.01原版的SDK会根据随机生成的eth0的mac地址再生成eth0.1、eth0.2等,生成后的mac地址会保存在/etc/config/network下。 ... [详细]
author-avatar
迷茫_信徒_476
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有