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

dotnet通过依赖注入的Scoped给工作流注入相同的上下文信息

本文将来聊聊Microsoft.Extensions.DependencyInjection这个依赖注入框架的Scoped功能的一个应用,这个框架是默认ASP.NETCore的核心

本文将来聊聊 Microsoft.Extensions.DependencyInjection 这个依赖注入框架的 Scoped 功能的一个应用,这个框架是默认 ASP.NET Core 的核心库将会默认被引用。而其他 .NET 的应用如 WPF 或 Xamarin 等也可以使用这个库。因此本文标题就是 dotnet 而不是具体哪个框架 在开发的时候,咱会有一些复杂的逻辑需要多个类合作进行执行,而在使用多个类进行执行的时候,就涉及到上下文信息的传递。例如最简单的追踪 Id 的值,假定在多个类组成的多个步骤里面,因为存在多线程调用的问题,咱在定位问题的时候需要在日志里面输出当前步骤所使用的追踪 Id 是哪个,这样就运行进行并行多次任务同时执行,同时日志不会乱

尽管本文使用 Scoped 仅作为日志记录的功能没能发挥强大的日志库的作为,但是减弱日志库是为了提升 DependencyInjection 的强大,因此请小伙伴仅认为日志库和输出文本到控制台之间没有任何差别

如上图,假定有三个步骤,分别是 F1 和 F2 和 F3 三个步骤,此时有3个任务同时进来。而我期望能够在日志里面的相关输出能包含当前的步骤在执行的任务是哪一个

最简单的方法是在每个步骤的参数里面传递上任务的追踪 Id 值,此时就可以在每个步骤里面的输出添加追踪信息

这个方法存在什么问题?如果我想要多添加额外的参数,此时我需要改一条链。另外也没有发挥 Scoped 的功能

那么什么是 Scoped 的功能?在 Microsoft.Extensions.DependencyInjection 提供的对象注入里面提供了三个不同的方式,第一个是瞬时 Transient 模式,这个模式可以让每次获取实例的时候,拿到的都是全新的实例。第二个是 Singleton 单例,无论在哪里从这个容器获取到的都是相同的对象。而最后一个也是最复杂的就是 Scoped 范围模式,也是本文要来安利大家的功能

先不说 Scoped 的定义,先来聊聊他的作用。在相同的 using 范围内,嗯,这个 Scoped 是容器的状态,容器可以通过 CreateScope 方法进入 Scoped 范围,如下面代码

// IServiceProvider serviceProvider
using (var serviceScope = serviceProvider.CreateScope())
{

}

此时在一个相同的 serviceScope 实例创建的对象,如果这个对象在注入的时候标记了自己是 Scoped 范围,那么将会拿到相同的实例。而在标记了 Scoped 范围的对象在不同的 ServiceScope 实例创建的是不同的对象,如下面代码

// IServiceProvider serviceProvider
// Foo 是注册为 Scoped 的对象
// services.AddScoped();
using (var serviceScope1 = serviceProvider.CreateScope())
{
// 下面代码的 foo 和 foo1 是相同的对象
var foo = serviceScope.ServiceProvider.GetService();
var foo1 = serviceScope.ServiceProvider.GetService();
}
using (var serviceScope2 = serviceProvider.CreateScope())
{
// 下面的 foo2 和 foo 不是相同的对象
var foo2 = serviceScope.ServiceProvider.GetService();
}

因此假设将各个步骤加上步骤需要的上下文信息类都作为 Scoped 范围注入,那么此时在一次任务过程中,任务使用的步骤都在一个 Scoped 里面,如果此时的任务使用相同的类型的上下文信息类,那么此上下文信息将会是相同的对象。因此各个任务可以使用上下文信息共享信息

假设上下文信息类是 Info 类,里面只有使用一个信息就是 Id 这个信息

public class Info
{
public string Id { set; get; }
}

为了方便起见,我还是创建一个 ASP.NET Core 应用,因为这个应用框架默认部署好了依赖注入

在 Startup.cs 里面进行注册

public void ConfigureServices(IServiceCollection services)
{
services.AddScoped();
}

然后定义三个步骤的类

public class F1
{
public F1(ILogger logger, Info info, F2 f2)
{
_logger = logger;
Info = info;
F2 = f2;
}
public Info Info { get; }
public void Do()
{
_logger.LogInformation(Info.Id);
F2.Do();
}
private readonly ILogger _logger;
private F2 F2 { get; }
}
public class F2
{
public F2(ILogger logger, Info info)
{
_logger = logger;
Info = info;
}
public Info Info { get; }
public void Do()
{
_logger.LogInformation(Info.Id);
}
private readonly ILogger _logger;
}
public class F3
{
public F3(ILogger logger, Info info)
{
_logger = logger;
Info = info;
}
public Info Info { get; }
public void Do()
{
_logger.LogInformation(Info.Id);
}
private readonly ILogger _logger;
}

可以看到上面三个类都是只有一个 Do 方法,在 Do 方法里面调用日志记录上下文信息

在 Startup.cs 里面进行注册这三个步骤

public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddScoped();
services.AddScoped();
services.AddScoped();
services.AddScoped();
}

在使用的时候,默认控制器就是注册为 Scoped 的,因此在控制器里面无论是构造注入或者是使用容器获取都是在相同的 Scoped 里面

上面代码是 F1 步骤引用 F2 步骤,咱在构造将 F1 注入。而 F3 是独立步骤,咱通过容器获取

[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
public WeatherForecastController(ILogger logger, F1 f1, Info info,
IServiceProvider serviceProvider)
{
Info = info;
_logger = logger;
_f1 = f1;
_serviceProvider = serviceProvider;
using (var serviceScope = serviceProvider.CreateScope())
{
var foo = serviceScope.ServiceProvider.GetService();
var foo1 = serviceScope.ServiceProvider.GetService();
if (ReferenceEquals(foo, foo1))
{
}
}
}
public Info Info { get; }
[HttpGet]
public IActionResult Get()
{
Info.Id = DateTime.Now.ToString();
_logger.LogInformation(Info.Id);
_f1.Do();
var f3 = _serviceProvider.GetService();
f3.Do();
return Ok();
}
private readonly F1 _f1;
private readonly ILogger _logger;
private readonly IServiceProvider _serviceProvider;
}

执行代码可以看到 F1 和 F2 和 F3 的 Info 对象都是相同的对象,于是在 Info 对象设置的值可以在三个步骤使用

通过这个方法,在后续修改的时候,假如有一个信息是 F1 和 F3 都需要的,但是 F1 和 F3 是独立的,此时就可以再新建一个类用于存放此参数,然后将这个类注册为 Scoped 的。接着在 F1 和 F3 注入这个类,此时使用的对象就是相同的对象,因此参数也就能传递

有趣的是这个方法改动仅仅只是 F1 和 F3 两个类加上依赖注入构造,其他模块可以不动

本文代码放在 github 欢迎小伙伴访问

我搭建了自己的博客 https://blog.lindexi.com/ 欢迎大家访问,里面有很多新的博客。

如果在博客看到有任何不懂的,欢迎交流



推荐阅读
  • 深入理解Kafka服务端请求队列中请求的处理
    本文深入分析了Kafka服务端请求队列中请求的处理过程,详细介绍了请求的封装和放入请求队列的过程,以及处理请求的线程池的创建和容量设置。通过场景分析、图示说明和源码分析,帮助读者更好地理解Kafka服务端的工作原理。 ... [详细]
  • CF:3D City Model(小思维)问题解析和代码实现
    本文通过解析CF:3D City Model问题,介绍了问题的背景和要求,并给出了相应的代码实现。该问题涉及到在一个矩形的网格上建造城市的情景,每个网格单元可以作为建筑的基础,建筑由多个立方体叠加而成。文章详细讲解了问题的解决思路,并给出了相应的代码实现供读者参考。 ... [详细]
  • 基于Socket的多个客户端之间的聊天功能实现方法
    本文介绍了基于Socket的多个客户端之间实现聊天功能的方法,包括服务器端的实现和客户端的实现。服务器端通过每个用户的输出流向特定用户发送消息,而客户端通过输入流接收消息。同时,还介绍了相关的实体类和Socket的基本概念。 ... [详细]
  • 本文详细介绍了GetModuleFileName函数的用法,该函数可以用于获取当前模块所在的路径,方便进行文件操作和读取配置信息。文章通过示例代码和详细的解释,帮助读者理解和使用该函数。同时,还提供了相关的API函数声明和说明。 ... [详细]
  • Mac OS 升级到11.2.2 Eclipse打不开了,报错Failed to create the Java Virtual Machine
    本文介绍了在Mac OS升级到11.2.2版本后,使用Eclipse打开时出现报错Failed to create the Java Virtual Machine的问题,并提供了解决方法。 ... [详细]
  • 本文详细介绍了在ASP.NET中获取插入记录的ID的几种方法,包括使用SCOPE_IDENTITY()和IDENT_CURRENT()函数,以及通过ExecuteReader方法执行SQL语句获取ID的步骤。同时,还提供了使用这些方法的示例代码和注意事项。对于需要获取表中最后一个插入操作所产生的ID或马上使用刚插入的新记录ID的开发者来说,本文提供了一些有用的技巧和建议。 ... [详细]
  • 《数据结构》学习笔记3——串匹配算法性能评估
    本文主要讨论串匹配算法的性能评估,包括模式匹配、字符种类数量、算法复杂度等内容。通过借助C++中的头文件和库,可以实现对串的匹配操作。其中蛮力算法的复杂度为O(m*n),通过随机取出长度为m的子串作为模式P,在文本T中进行匹配,统计平均复杂度。对于成功和失败的匹配分别进行测试,分析其平均复杂度。详情请参考相关学习资源。 ... [详细]
  • 本文介绍了Java高并发程序设计中线程安全的概念与synchronized关键字的使用。通过一个计数器的例子,演示了多线程同时对变量进行累加操作时可能出现的问题。最终值会小于预期的原因是因为两个线程同时对变量进行写入时,其中一个线程的结果会覆盖另一个线程的结果。为了解决这个问题,可以使用synchronized关键字来保证线程安全。 ... [详细]
  • 在重复造轮子的情况下用ProxyServlet反向代理来减少工作量
    像不少公司内部不同团队都会自己研发自己工具产品,当各个产品逐渐成熟,到达了一定的发展瓶颈,同时每个产品都有着自己的入口,用户 ... [详细]
  • 本文详细介绍了Spring的JdbcTemplate的使用方法,包括执行存储过程、存储函数的call()方法,执行任何SQL语句的execute()方法,单个更新和批量更新的update()和batchUpdate()方法,以及单查和列表查询的query()和queryForXXX()方法。提供了经过测试的API供使用。 ... [详细]
  • 本文介绍了UVALive6575题目Odd and Even Zeroes的解法,使用了数位dp和找规律的方法。阶乘的定义和性质被介绍,并给出了一些例子。其中,部分阶乘的尾零个数为奇数,部分为偶数。 ... [详细]
  • flowable工作流 流程变量_信也科技工作流平台的技术实践
    1背景随着公司业务发展及内部业务流程诉求的增长,目前信息化系统不能够很好满足期望,主要体现如下:目前OA流程引擎无法满足企业特定业务流程需求,且移动端体 ... [详细]
  • C++字符字符串处理及字符集编码方案
    本文介绍了C++中字符字符串处理的问题,并详细解释了字符集编码方案,包括UNICODE、Windows apps采用的UTF-16编码、ASCII、SBCS和DBCS编码方案。同时说明了ANSI C标准和Windows中的字符/字符串数据类型实现。文章还提到了在编译时需要定义UNICODE宏以支持unicode编码,否则将使用windows code page编译。最后,给出了相关的头文件和数据类型定义。 ... [详细]
  • 本文介绍了Python爬虫技术基础篇面向对象高级编程(中)中的多重继承概念。通过继承,子类可以扩展父类的功能。文章以动物类层次的设计为例,讨论了按照不同分类方式设计类层次的复杂性和多重继承的优势。最后给出了哺乳动物和鸟类的设计示例,以及能跑、能飞、宠物类和非宠物类的增加对类数量的影响。 ... [详细]
  • 解决nginx启动报错epoll_wait() reported that client prematurely closed connection的方法
    本文介绍了解决nginx启动报错epoll_wait() reported that client prematurely closed connection的方法,包括检查location配置是否正确、pass_proxy是否需要加“/”等。同时,还介绍了修改nginx的error.log日志级别为debug,以便查看详细日志信息。 ... [详细]
author-avatar
mobiledu2502869017
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有