手把手教你写DI_3_小白徒手支持 `Singleton` 和 `Scoped` 生命周期
手把手教你写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();
}
}
小白同学:真完美!!!!!
青龙偃月刀:呵呵,这样也能算完美?多少没做,还有多少问题没搞?你看人家做成这样子都算差的了 - https://github.com/fs7744/Norns
手把手教你写DI_3_小白徒手支持 `Singleton` 和 `Scoped` 生命周期的更多相关文章
- 手把手教你写DI_2_小白徒手撸构造函数注入
小白徒手撸构造函数注入 在上一节:手把手教你写DI_1_DI框架有什么? 我们已经知道我们要撸哪些东西了 那么我们开始动工吧,这里呢,我们找小白同学来表演下 小白同学 :我们先定义一下我们的广告招聘纸 ...
- 手把手教你写DI_1_DI框架有什么?
DI框架有什么? 在上一节:手把手教你写DI_0_DI是什么? 我们已经理解DI是什么 接下来我们就徒手撸一撸,玩个支持构造函数注入的DI出来 首先我们回顾一下 构造函数注入 的代码形式, 大概长这模 ...
- 网络编程懒人入门(八):手把手教你写基于TCP的Socket长连接
本文原作者:“水晶虾饺”,原文由“玉刚说”写作平台提供写作赞助,原文版权归“玉刚说”微信公众号所有,即时通讯网收录时有改动. 1.引言 好多小白初次接触即时通讯(比如:IM或者消息推送应用)时,总是不 ...
- 只有20行Javascript代码!手把手教你写一个页面模板引擎
http://www.toobug.net/article/how_to_design_front_end_template_engine.html http://barretlee.com/webs ...
- [原创]手把手教你写网络爬虫(5):PhantomJS实战
手把手教你写网络爬虫(5) 作者:拓海 摘要:从零开始写爬虫,初学者的速成指南! 封面: 大家好!从今天开始,我要与大家一起打造一个属于我们自己的分布式爬虫平台,同时也会对涉及到的技术进行详细介绍.大 ...
- Android开发之手把手教你写ButterKnife框架(三)
欢迎转载,转载请标明出处: http://blog.csdn.net/johnny901114/article/details/52672188 本文出自:[余志强的博客] 一.概述 上一篇博客讲了, ...
- Android开发之手把手教你写ButterKnife框架(二)
欢迎转载,转载请标明出处: http://blog.csdn.net/johnny901114/article/details/52664112 本文出自:[余志强的博客] 上一篇博客Android开 ...
- 手把手教你写基于C++ Winsock的图片下载的网络爬虫
手把手教你写基于C++ Winsock的图片下载的网络爬虫 先来说一下主要的技术点: 1. 输入起始网址,使用ssacnf函数解析出主机号和路径(仅处理http协议网址) 2. 使用socket套接字 ...
- 手把手教你写DI_0_DI是什么?
DI是什么? Dependency Injection 常常简称为:DI. 它是实现控制反转(Inversion of Control – IoC)的一个模式. fowler 大大大神 "几 ...
随机推荐
- TP5 RCE
Thinkphp5 RCE 复现 环境: win10+wamp+thinkphp5.1.29 下载地址 源码分析 程序首先跳转到 public目录下的index.php,然后执行 thinkphp/l ...
- 基于FFmpeg的Dxva2硬解码及Direct3D显示(一)
目录 前言 名词解释 代码实现逻辑 前言 关于视频软解码的资料网上比较多了,但是关于硬解可供参考的资料非常之有限,虽然总得来说软解和硬解的基本逻辑一样,但是实现细节上的差别还是比较多的.虽然目前功能已 ...
- 数据库Sharding的基本思想和切分策略(转)
一.基本思想 Sharding的基本思想就要把一个数据库切分成多个部分放到不同的数据库(server)上,从而缓解单一数据库的性能问题.不太严格的讲,对于海量数据的数据库,如果是因为表多而数据多,这时 ...
- VS2017新建MVC+ORM中的LinqDb访问数据库项目
1.前提概述 ORM对象关系映射(Object-Relational Mapping)是一种程序技术,用于实现面向对象编程语言里不同类型系统的数据之间的转换.从效果上说,它其实是创建了一个可在编程语言 ...
- python程序基础
高级程序设计语言包括Python.C/C++.Java等 低级程序设计语言包括汇编语言和机器语言 Python是一种解释型语言,但为了提高运行效率,Python程序在 执行一次之后会自动生成扩展名 ...
- Mac磁盘清理工具——CleanMyMac
许多刚从Windows系统转向Mac系统怀抱的用户,一开始难免不习惯,因为Mac系统没有像Windows一样的C盘.D盘,分盘分区明显.因此这也带来了一些问题,关于Mac的磁盘的清理问题,怎么进行清理 ...
- 通过城市联动实时将地址显示到text中
<div class="form-group field-supplier-sort <?php if($model->getErrors('province_id') | ...
- jmeter接口测试多数据组合登陆场景
一.安装好Java运行环境 百度下载JDK并且配置JAVA环境的教程一搜一大把,这里我就不详说了 二.运行JMETER 打开安装目录的bin文件中的jmeter.bat文件 三.添加程序 1.添加线程 ...
- E - Knapsack 2 题解(超大01背包)
题目链接 题目大意 给你一n(n<=100)个物品,物品价值最大为1e3,物品体积最多为1e9,背包最大为1e9 题目思路 如果按照平常的背包来算那么时间复杂度直接O(1e11) 这个你观察就发 ...
- 【Makefile】5-Makefile变量的基础
目录 前言 概念 Chapter 5:变量的基础 5.1 变量的基础 * 空格的定义 ** 一些赋值 一些特殊的符号 5.2 变量中的变量 * 5.3 变量高级用法 变量值替换 把变量的值再当成变量 ...