dotnet 推荐 LightWorkFlowManager 轻量的工作过程管理库
本文将和大家推荐我团队开源的 LightWorkFlowManager 轻量的工作过程管理库,适合任何需要执行工作过程的应用逻辑,可以方便将多个工作过程拼凑起来,且自动集成重试和失败处理,以及日志和上报功能
这个 LightWorkFlowManager 轻量的工作过程管理库是我所在的团队在 GitHub 上使用最友好的 MIT 协议开源的库,请看 https://github.com/dotnet-campus/LightWorkFlowManager
这个 LightWorkFlowManager 轻量的工作过程管理库现在已经在我团队的正式产品应用上运行有半年了,相对来说比较稳定。使用过程中如果有任何问题,都欢迎在 GitHub 上新建 Issues 进行反馈
世界上有许多现有的工作过程管理,那为什么还重新造了一个?没啥原因,自己造的用的顺手。这个 LightWorkFlowManager 库在功能上和其他的工作过程管理库相比要缺失许多,但胜在轻量。且 LightWorkFlowManager 最重要的卖点就是对调试友好,适合用在本身复杂度就比较好的逻辑情况下,可以有效降低引入工作过程管理模块本身带来的额外复杂度
许多现有的功能齐全的工作过程管理库都是在 ASP.NET Core 上使用的。然而我这里的需求是在客户端应用框架上使用,且大部分情况下是在用户的设备上运行的,许多服务依赖本身是不存在的。尽管许多现有的功能齐全的工作过程管理库都可以通过配置的形式将其切换到单机内运行,然而这样做将在大部分时候不是库的推荐用法,用起来没有那么顺。为此我所在的团队开发了 LightWorkFlowManager 轻量的工作过程管理库,这个库可以很方便在客户端框架上运行,比如在 WPF 应用程序、在 WinForms 应用程序、在 MAUI 应用程序上运行。同时 LightWorkFlowManager 也兼顾 ASP.NET Core 服务本身,特别是依赖注入部分,可以和默认的服务的依赖注入相连接
我将举一个例子来告诉大家这个 LightWorkFlowManager 库所适用的情况。假定我拿到了一个开发任务,这个任务是做 PPT 解析,取出 PPT 文件里面的图片。细分这个任务将包括以下三个步骤:步骤一,获取 PPT 文件。步骤二,调用某个现有帮助方法获取到 PPT 文件里面的图片。步骤三,对拿到的 PPT 文件里面的图片文件进行处理
在步骤一里面,有多个方式获取到 PPT 文件,比如有些是从网上 CDN 服务器上下载的,有些是从磁盘路径获取的,有些是用户拖入的。这些不同的输入获取方式来自于各个业务的不同入口
在步骤三里面,又根据不同的业务情况,需要分为将图片文件进行上传到后台服务器,或者是有些业务需要将这些图片展示到界面上等等
从以上的描述可以看到,我所拿到的是一个可以散落在各个业务里面,看似存在许多不同,却又在逻辑上相似的需求。逻辑上的相似也就是可以强行归类步骤进行约束,逻辑上的重复在于有许多逻辑是完全可复用的,比如记录日志和重试机制。重试机制几乎可以用在以上三个步骤里面,无论是开发新手还是富有经验的开发者都知道,无论是从网上下载内容还是上传文件都需要经过不稳定的网络传输,这个时候因为网络传输失败而执行重试逻辑是十分常见的处理逻辑
假定没有 LightWorkFlowManager 库的辅助,一个可能的实现设计大概如下:设计一个统一的入口,通过入口传入参数判断执行不同的步骤处理逻辑,如下图

通过上图可以很简单就看出来问题,整个的逻辑复杂度将会比较高,原本的业务入口就存在多个,接着进入到 PPT 解析任务里面的统一入口,在 PPT 解析任务里面的统一入口再根据参数分发逻辑到不同的处理逻辑上。整个逻辑的圈复杂度将比较高,同时对于后续如果在步骤一或步骤三再添加更多的业务需求,将会让整个逻辑更加复杂起来。另一方面,如此复杂的逻辑,将会导致代码阅读和调试的困难性,比如难以非常明确的知道整个的调用链,调用链可能过于复杂,比如难以在调试的时候进行重复执行
在有 LightWorkFlowManager 库的辅助下,可以修改一下代码的逻辑设计,也就是不再进行统一的入口的分发,而是让各自的业务串联对应的逻辑,如此可以理顺一条逻辑,保持 PPT 解析任务模块的简单。同时业务方也可以更加清楚 PPT 解析任务的细节

可以看到修改之后的代码,可以让每个逻辑都是顺下来的,不存在逻辑的圈。可以将原本就比较复杂的逻辑,降低其逻辑复杂度。一个串联的顺的逻辑,将对代码阅读以及调试更加友好。且在 LightWorkFlowManager 库的辅助之下,可以尽量减少重复的代码逻辑,比如减少重复的记录日志和失败重试逻辑
在了解 LightWorkFlowManager 库的适用设计之后,接下来将和大家介绍具体的使用方法
按照 dotnet 的惯例,先安装 NuGet 库,请看 https://www.nuget.org/packages/dotnetCampus.LightWorkFlowManager
在开始介绍使用方法之前,需要先介绍一下 LightWorkFlowManager 里面的概念。在 LightWorkFlowManager 里面定义了 MessageWorkerManager 类型,将其作为工作器的管理器,用途就是将各个工作器串联起来,且运行起来,这个 MessageWorkerManager 就是 LightWorkFlowManager 的入口类型
在 LightWorkFlowManager 里面定义了工作器的基础类型就是 MessageWorker 类型,所有的业务上的开发者自定义的工作器都需要直接或间接继承 MessageWorker 类型
了解基础概念之后,接下来就开始编写一个简单的逻辑作为例子
使用方法
1、 创建 MessageWorkerManager 对象。创建 MessageWorkerManager 工作器管理器即可作为承载工作器的框架,请为每次单独的任务创建独立的 MessageWorkerManager 对象
// 每个任务一个 TaskId 号
string taskId = Guid.NewGuid().ToString();
// 相同类型的任务采用相同的名字,比如这是做 PPT 解析的任务
string taskName = "PPT 解析";
// 提供容器
IServiceScope serviceScope = serviceProvider.CreateScope();
var workerManager = new MessageWorkerManager(taskId, taskName, serviceScope);
2、 定义 Worker 工作器。以下例子代码定义了模拟业务代码的 FooWorker 工作器,可以在 FooWorker 重写的 DoInnerAsync 方法里面编写执行的业务代码。以下代码的 InputType 和 OutputType 分别是工作器的输入和输出类型,每个工作器的输出对象都会自动加入到 MessageWorkerManager 的过程上下文缓存里面
record InputType();
record OutputType();
class FooWorker : MessageWorker<InputType, OutputType>
{
protected override ValueTask<WorkerResult<OutputType>> DoInnerAsync(InputType input)
{
...
}
}
3、 执行 Worker 工作器。 完成类型的定义之后,接下来演示如何通过 MessageWorkerManager 将 FooWorker 运行起来
var result = await workerManager
.GetWorker<FooWorker>()
.RunAsync();
以上代码也可以简写为以下代码
var result = await workerManager.RunWorker<FooWorker>();
以上代码运行的前提是先注入 FooWorker 所需的 InputType 类型的对象。下文将告诉注入工作器参数部分的更多细节
机制和功能
工作器参数
工作器参数通过 MessageWorkerManager 工作器管理的 IWorkerContext 上下文读取。每个工作器的可返回值类型都会自动被设置到 IWorkerContext 上下文里面。如此即可自动实现上一个 Worker 的输出作为下一个 Worker 的输入
在每个工作器里面,都可以通过 SetContext 设置上下文信息
在开始执行工作器时,还可以手动设置输入参数,如以下例子
// 例子1:先获取工作器,再赋值给到工作器的执行方法
await Manager
.GetWorker<FooWorker>()
.RunAsync(new InputType());
// 例子2: 通过 SetContext 进行设置参数,再执行工作器
await Manager
.SetContext(new InputType())
.RunWorker<FooWorker>();
如果有些工作器之间的输入和输出参数需要进行转换,也可以 SetContext 传入转换委托进行参数转换
// 以下例子将当前上下文里的 Foo1Type 类型转换为 FooWorker 需要的 Foo2Type 参数
await Manager
.SetContext((Foo1Type foo1) => ConvertFoo1ToFoo2(foo1))
.RunWorker<FooWorker>();
异常中断和重试
每个 Worker 都可以返回 WorkerResult 类型的返回值,可以在返回值里面告知框架层是否当前的 Worker 执行成功。在执行失败时,可以赋值错误码,方便定位调试或输出。在执行失败时,可以返回告知框架层是否需要重试,默认情况下框架都会自动进行重试
中断后续的工作器执行有两个方法:
方法1: 通过返回状态为失败的 WorkerResult 返回值。一旦工作管理器的状态为 IsFail 状态,将会阻止所有的没有标记 CanRunWhenFail 为 true 的工作器的执行。换句话说就是除了哪些不断成功或失败状态都要执行的 Worker 工作器之外,其他的工作器都将不会执行,包括 SetContext 里面的委托转换也不会执行
方法2: 通过抛出异常的方式,通过 dotnet 里面的异常可以让后续逻辑炸掉不再执行
以上两个方法都是框架推荐使用的。框架设计的偏好是如果在重视性能的情况下,尽量使用方法1的方式中断执行。如果是在复杂的业务逻辑,有大量业务逻辑穿插在工作过程之外,则可以方便通过方法2进行中断
在 Worker 里执行其他 Worker 工作器
在一个 Worker 里面,可以执行其他的 Worker 工作器,如此可以比较自由的实现分支逻辑,套娃决定执行工作器
例子如下:
假定有一个 Worker2 工作器,定义如下:
class Worker2 : MessageWorker<InputType, OutputType>
{
protected override ValueTask<WorkerResult<OutputType>> DoInnerAsync(InputType input)
{
return SuccessTask(new OutputType());
}
}
record InputType();
record OutputType();
有另一个 Worker1 工作器,可以在 Worker1 里面执行 Worker2 工作器:
class Worker1 : MessageWorkerBase
{
public override async ValueTask<WorkerResult> Do(IWorkerContext context)
{
await Manager
.GetWorker<Worker2>()
.RunAsync(new InputType());
return WorkerResult.Success();
}
}
委托工作器
有一些非常小且轻的逻辑,也想加入到工作过程里面,但不想为此定义一个单独的工作器。可以试试委托工作器,如以下代码例子
var delegateMessageWorker = new DelegateMessageWorker(_ =>
{
// 在这里编写委托工作器的业务内容
});
var result = await messageWorkerManager.RunWorker(delegateMessageWorker);
如果连 DelegateMessageWorker 都不想创建,那也可以直接使用 MessageWorkerManager 的 RunWorker 方法传入委托,如以下代码例子
await messageWorkerManager.RunWorker((IWorkerContext context) =>
{
// 在这里编写委托工作器的业务内容
});
dotnet 推荐 LightWorkFlowManager 轻量的工作过程管理库的更多相关文章
- flutter最简单轻量便捷的路由管理方案NavRouter
大家好,我是CrazyQ1,今天给大家推荐一个路由管理方案,用的非常不错的,叫nav_router. 项目地址是:https://github.com/fluttercandies/nav_route ...
- Tippy.js – 轻量的 Javascript Tooltip 工具库
工具提示(Tooltip)在网站中的一个小功能,但却有很重要的作用,常用于显示一些温馨的提示信息.如果网站中的工具提示功能做得非常有创意的话能够加深用户对网站印象.Tippy.js 是一款帮助你快速创 ...
- 免费申请使用IBM Cloud Lite(轻量套餐) 续
之前尝试申请了IBM的轻量套餐,过程很简单,操作起来也比较方便,就是能够用到的地方不多,虽说几乎是无限流量且永久免费,我能做的也只是做个小网站 免费用户默认的是轻量应用服务,如果需要功能更多更全的应用 ...
- 推荐一个简单、轻量、功能非常强大的C#/ASP.NET定时任务执行管理器组件–FluentScheduler定时器
在C#WINFORM或者是ASP.NET的WEB应用程序中,根据各种定时任务的需求,比如:每天的数据统计,每小时刷新系统缓存等等,这个时候我们得应用到定时器这个东东. .NET Framework有自 ...
- 推荐轻量友好的.NET测试断言工具Shouldly
Shouldly是一个轻量的断言(Assertion)框架,用于补充.NET框架下的测试工具.Shouldly将焦点放在当断言失败时如何简单精准的给出很好的错误信息. Shouldly在GitHub的 ...
- 推荐一款轻量小众却高效免费开源windows热键脚本语言Autohotkey
写在前面的话 Autohotkey是一款轻量小众但高效免费开源的windows热键脚本语言,游戏操纵.鼠标操作.键盘快捷重定义,快捷短语等等,只有你想不到,没有它做不到,神器中的神器呀,相见恨晚. 安 ...
- Vue.js:轻量高效的前端组件化方案
转发一篇尤老师对vue.js的介绍,了解vue.js的来龙去脉.不过现在已经是2.0了,也有添加一些新的东西,当然有些东西也改了. Vue.js:轻量高效的前端组件化方案 Vue.js 是我在2014 ...
- Vue.js:轻量高效的前端组件化方案(转载)
摘要:Vue.js通过简洁的API提供高效的数据绑定和灵活的组件系统.在前端纷繁复杂的生态中,Vue.js有幸受到一定程度的关注,目前在GitHub上已经有5000+的star.本文将从各方面对Vue ...
- OWIN轻量型框架介绍
OWIN轻量型框架介绍 阅读目录 引言 框架的特色 如何启动 各项功能 静态路由的3种写法 伪静态路由的支持 处理Form表单提交的文件 流式处理Post请求的数据 多种请求类型自动识别 响应处理 请 ...
- 【转】Vue.js:轻量高效的前端组件化方案
摘要:Vue.js通过简洁的API提供高效的数据绑定和灵活的组件系统.在前端纷繁复杂的生态中,Vue.js有幸受到一定程度的关注,目前在GitHub上已经有5000+的star.本文将从各方面对Vue ...
随机推荐
- 基于proteus的555的门铃计数电路
基于proteus的555的门铃计数电路 1.实验原理 555定时器可以作为单稳态触发器完成计数所需的时钟.门铃工作时,需要进行一次计数.计数器使用前面使用的4026就可以将结果直接显示在数码管上. ...
- python爬虫配置随机请求头headers伪装User-Agent
python爬虫随机headers伪装fake_useragent fake_useragent 库 调用方法ua.random可以随机返回一个headers(User-Agent) from fak ...
- xpath解析爱奇艺电影网页数据
1 url='https://list.iqiyi.com/www/1/-------------11-1-1-iqiyi--.html' 2 headers={ 3 'User-Agent':'Mo ...
- 官宣!禅道与极狐(GitLab)达成深度合作,携手推进开源开放DevOps生态发展
近日,禅道与著名编程开源开发平台极狐(GitLab)公司签署战略合作,双方将重点探索适用于中国用户DevOps全生命周期解决方案,并将在开源培训和教育.云服务解决方案等多个领域深度合作,共同助力国内D ...
- Vim 速查表 做记录 便于记忆
Vim 命令速查表 简体中文 • English 简介:Vim 命令速查表,注释化 vimrc 配置文件,经典 Vim 键盘图,实用 Vim 书籍,Markdown 格式,目录化检索,系统化学习,体系 ...
- #SPFA#洛谷 4042 [AHOI2014/JSOI2014] 骑士游戏
题目 分析 如果我想普通攻击1,那么必须干掉所有产生的其它怪兽,这不由得可以用一个不等式来表示, \(普攻+\sum need<法攻\) 但是所需要消灭的怪兽同样可以这样进行,所以它可能具有后效 ...
- PWA 实践/应用(Google Workbox)
桌面端 PWA 应用: 移动端添加到桌面: 1 什么是 PWA PWA(Progressive Web App - 渐进式网页应用)是一种理念,由 Google Chrome 在 2015 年提出.P ...
- Git安装和配置教程:Windows/Mac/Linux三平台详细图文教程,带你一次性搞定Git环境
Git是一款免费.开源的分布式版本控制系统,广泛应用于软件开发领域.随着开源和云计算的发展,Git已经成为了开发者必备的工具之一.本文将为大家介绍Git在Windows.Mac和Linux三个平台上的 ...
- Git 13 暂存代码
如果当前分支的功能还没开发完,无法提交代码. 但出现了紧急情况(比如线上BUG),需要立即切换到其他分支进行开发. 此时可以先暂存当前分支代码,等切换回当前分支的时候再恢复. 1.暂存当前分支变更: ...
- 【FAQ】视频编辑服务常见问题及解答
Q1问题描述 1. 访问贴纸等素材的时候提示"网络异常,请重试"怎么办? 2. 使用AI能力时,提示"errorCode:20124 errorMsg:Method no ...