热门标签 | 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



推荐阅读
  • 使用 Azure Service Principal 和 Microsoft Graph API 获取 AAD 用户列表
    本文介绍了一段通用代码示例,该代码不仅能够操作 Azure Active Directory (AAD),还可以通过 Azure Service Principal 的授权访问和管理 Azure 订阅资源。Azure 的架构可以分为两个层级:AAD 和 Subscription。 ... [详细]
  • 本文详细介绍了如何构建一个高效的UI管理系统,集中处理UI页面的打开、关闭、层级管理和页面跳转等问题。通过UIManager统一管理外部切换逻辑,实现功能逻辑分散化和代码复用,支持多人协作开发。 ... [详细]
  • 1.如何在运行状态查看源代码?查看函数的源代码,我们通常会使用IDE来完成。比如在PyCharm中,你可以Ctrl+鼠标点击进入函数的源代码。那如果没有IDE呢?当我们想使用一个函 ... [详细]
  • 深入解析Spring Cloud Ribbon负载均衡机制
    本文详细介绍了Spring Cloud中的Ribbon组件如何实现服务调用的负载均衡。通过分析其工作原理、源码结构及配置方式,帮助读者理解Ribbon在分布式系统中的重要作用。 ... [详细]
  • 实体映射最强工具类:MapStruct真香 ... [详细]
  • 本次考试于2016年10月25日上午7:50至11:15举行,主要涉及数学专题,特别是斐波那契数列的性质及其在编程中的应用。本文将详细解析考试中的题目,并提供解题思路和代码实现。 ... [详细]
  • 深入理解 .NET 中的中间件
    中间件是插入到应用程序请求处理管道中的组件,用于处理传入的HTTP请求和响应。它在ASP.NET Core中扮演着至关重要的角色,能够灵活地扩展和自定义应用程序的行为。 ... [详细]
  • 本文详细介绍了如何在Linux系统上安装和配置Smokeping,以实现对网络链路质量的实时监控。通过详细的步骤和必要的依赖包安装,确保用户能够顺利完成部署并优化其网络性能监控。 ... [详细]
  • DNN Community 和 Professional 版本的主要差异
    本文详细解析了 DotNetNuke (DNN) 的两种主要版本:Community 和 Professional。通过对比两者的功能和附加组件,帮助用户选择最适合其需求的版本。 ... [详细]
  • 本文详细介绍了 Apache Jena 库中的 Txn.executeWrite 方法,通过多个实际代码示例展示了其在不同场景下的应用,帮助开发者更好地理解和使用该方法。 ... [详细]
  • 本文详细介绍了Java中的访问器(getter)和修改器(setter),探讨了它们在保护数据完整性、增强代码可维护性方面的重要作用。通过具体示例,展示了如何正确使用这些方法来控制类属性的访问和更新。 ... [详细]
  • 在维护公司项目时,发现按下手机的某个物理按键后会激活相应的服务,并在屏幕上模拟点击特定坐标点。本文详细介绍了如何使用ADB Shell Input命令来模拟各种输入事件,包括滑动、按键和点击等。 ... [详细]
  • 本文详细探讨了VxWorks操作系统中双向链表和环形缓冲区的实现原理及使用方法,通过具体示例代码加深理解。 ... [详细]
  • 毕业设计:基于机器学习与深度学习的垃圾邮件(短信)分类算法实现
    本文详细介绍了如何使用机器学习和深度学习技术对垃圾邮件和短信进行分类。内容涵盖从数据集介绍、预处理、特征提取到模型训练与评估的完整流程,并提供了具体的代码示例和实验结果。 ... [详细]
  • 本文介绍了一种解决二元可满足性(2-SAT)问题的方法。通过具体实例,详细解释了如何构建模型、应用算法,并提供了编程实现的细节和优化建议。 ... [详细]
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社区 版权所有