一、关于Workflow-Core

  近期工作上有一个工作流的开发需求,自己基于面向对象和职责链模式捣鼓了一套小框架,后来在github上发现一个轻量级的工作流引擎轮子:Workflow-Core,看完其wiki之后决定放弃之前自己造的轮子,使用这个开源项目来改造,也就有了这一篇博文。

  

  Workflow-Core是一个基于.NET Standard的轻量级工作流引擎,其GitHub地址为:https://github.com/danielgerlag/workflow-core,目前有超过1200+个star。它提供了FluentAPI、多任务、持久化以及并行处理的功能,适合于小型工作流、责任链的需求开发。

  由于Workflow-Core支持工作流长期运行,因此Workflow-Core支持以下多种数据源格式的持久化,可以通过安装不同的Provider包来实现对应的持久化:

  • (默认提供,用于测试和开发)内存
  • MongoDB
  • MS SQL Server
  • MySql
  • Sqlite
  • Redis
  • PostgreSQL

  立刻上手把,Nuget上安装一把,目前最新版本2.0.0:

  1. PM> Install-Package WorkflowCore

二、Workflow-Core的基本使用

2.1 Hello World

  这里创建了一个.NET Core控制台应用程序,快速演示第一个Workflow-Core的Hello World,展示如何开始一个Workflow:

  (1)定义一个实现IWorkflow接口的Workflow:

  1. public class HelloWorldWorkflow : IWorkflow
  2. {
  3. public string Id => "HelloWorld";
  4.  
  5. public int Version => ;
  6.  
  7. public void Build(IWorkflowBuilder<object> builder)
  8. {
  9. builder
  10. .StartWith<HelloWorld>()
  11. .Then<ActiveWorld>()
  12. .Then<GoodbyeWorld>();
  13. }
  14. }

  这里定义了一个HelloWorldWorkflow,其版本号为1,它有3个步骤:HelloWorld、ActiveWorld和GoodbyeWorld,会依次执行。

  (2)定义三个继承自StepBody类的步骤类:

  1. public class HelloWorld : StepBody
  2. {
  3. public override ExecutionResult Run(IStepExecutionContext context)
  4. {
  5. Console.WriteLine("Hello World!");
  6. return ExecutionResult.Next();
  7. }
  8. }
  9.  
  10. public class ActiveWorld : StepBody
  11. {
  12. public override ExecutionResult Run(IStepExecutionContext context)
  13. {
  14. Console.WriteLine("I am activing in the World!");
  15. return ExecutionResult.Next();
  16. }
  17. }
  18.  
  19. public class GoodbyeWorld : StepBody
  20. {
  21. public override ExecutionResult Run(IStepExecutionContext context)
  22. {
  23. Console.WriteLine("Goodbye World!");
  24. return ExecutionResult.Next();
  25. }
  26. }

  (3)ServiceCollection中注入Workflow-Core相关组件

  1. private static IServiceProvider ConfigureServices()
  2. {
  3. IServiceCollection services = new ServiceCollection();
  4. services.AddLogging(); // WorkflowCore需要用到logging service
  5. services.AddWorkflow();
  6.  
  7. var serviceProvider = services.BuildServiceProvider();
  8.  
  9. return serviceProvider;
  10. }

  (4)在Program.cs的Main方法中获取到注入的host并执行工作流

  1. public static void Main(string[] args)
  2. {
  3. var serviceProvider = ConfigureServices();
  4. var host = serviceProvider.GetService<IWorkflowHost>();
  5. host.RegisterWorkflow<HelloWorldWorkflow>();
  6. host.Start();
  7.  
  8. // Demo1:Hello World
  9. host.StartWorkflow("HelloWorld");
  10.  
  11. Console.ReadKey();
  12. host.Stop();
  13. }

  这里传入的是Workflow的Id,Workflow-Core会根据Id去自动匹配最新版本的对应Workflow,运行结果如下:

  

2.2 If语句

  在工作流处理中,往往会有很多的条件判断,那么在Workflow-Core中也提供了直接的If功能,如下面这个IfStatementWorkflow所示:

  1. public class IfStatementWorkflow : IWorkflow<MyData>
  2. {
  3. public string Id => "if-sample";
  4.  
  5. public int Version => ;
  6.  
  7. public void Build(IWorkflowBuilder<MyData> builder)
  8. {
  9. builder
  10. .StartWith<SayHello>()
  11. .If(data => data.Counter < ).Do(then => then
  12. .StartWith<PrintMessage>()
  13. .Input(step => step.Message, data => "Outcome is less than 3")
  14. )
  15. .If(data => data.Counter < ).Do(then => then
  16. .StartWith<PrintMessage>()
  17. .Input(step => step.Message, data => "Outcome is less than 5")
  18. )
  19. .Then<SayGoodbye>();
  20. }
  21. }

  这个传递进来的MyData的定义如下:

  1. public class MyData
  2. {
  3. public int Counter { get; set; }
  4. }

  当传递进来的MyData的Counter属性<3 或 <5时会有不同的分支进行逻辑的处理。

2.3 MySQL持久化支持

  想要将工作流配置持久化到MySQL,只需以下两步:

  (1)通过Nuget安装MySQL Provider包:

  1. PM> Install-Package WorkflowCore.Persistence.MySQL

  (2)注入到ServiceCollection

  1. services.AddWorkflow(x => x.UseMySQL(@"Server=127.0.0.1;Database=workflow;User=root;Password=password;", true, true));

  一旦启动,你就会发现Workflow-Core自动帮你创建了很多表用于持久化工作流配置和实例。

  

2.4 计划任务和循环任务

  Workflow-Core还集成了计划任务和循环任务的功能:

  (1)计划任务:比如在工作流步骤中设置一个延迟5分钟执行的计划任务

  1. builder
  2. .StartWith(context => Console.WriteLine("Hello"))
  3. .Schedule(data => TimeSpan.FromSeconds()).Do(schedule => schedule
  4. .StartWith(context => Console.WriteLine("Doing scheduled tasks"))
  5. )
  6. .Then(context => Console.WriteLine("Doing normal tasks"));

  (2)循环任务:比如在工作流步骤中设置一个延迟5分钟进行的循环任务,知道Counter > 5才结束

  1. builder
  2. .StartWith(context => Console.WriteLine("Hello"))
  3. .Recur(data => TimeSpan.FromSeconds(), data => data.Counter > ).Do(recur => recur
  4. .StartWith(context => Console.WriteLine("Doing recurring task"))
  5. )
  6. .Then(context => Console.WriteLine("Carry on"));

2.5 Saga支持

  了解分布式事务方案的童鞋应该都知道Saga,在Workflow-Core中也有支持,这是一个十分有用的功能:

  (1)比如:在创建一个客户信息之后,将其推送到Salesforce和ERP,如果推送过程中发生了错误,那么就通过重试进行补偿,并且重试有时间间隔。

  1.   builder
  2. .StartWith<CreateCustomer>()
  3. .Then<PushToSalesforce>()
  4. .OnError(WorkflowErrorHandling.Retry, TimeSpan.FromMinutes())
  5. .Then<PushToERP>()
  6. .OnError(WorkflowErrorHandling.Retry, TimeSpan.FromMinutes());

  (2)又比如:当Task2发生异常时,Workflow-Core会帮助执行UndoTask2 和 UndoTask1 帮你回滚数据以恢复状态。

  1. builder
  2. .StartWith<LogStart>()
  3. .Saga(saga => saga
  4. .StartWith<Task1>()
  5. .CompensateWith<UndoTask1>()
  6. .Then<Task2>()
  7. .CompensateWith<UndoTask2>()
  8. .Then<Task3>()
  9. .CompensateWith<UndoTask3>()
  10. )
  11. .OnError(Models.WorkflowErrorHandling.Retry, TimeSpan.FromMinutes())
  12. .Then<LogEnd>();

  更多Saga示例,请参考:https://github.com/danielgerlag/workflow-core/tree/master/src/samples/WorkflowCore.Sample17

三、在ASP.NET Core中使用Workflow-Core

3.1 注入与初始化

  (1)注入:使用AddWorkflow()扩展方法

  1. public void ConfigureServices(IServiceCollection services)
  2. {
  3. services.AddWorkflow();
  4. services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
  5. }

  (2)初始化:

  1. public void Configure(IApplicationBuilder app, IHostingEnvironment env)
  2. {
  3. .......
  4. app.UseWorkflow();
  5. }

  扩展方法如下:

  1. public static class ConfigureExtensions
  2. {
  3. public static IApplicationBuilder UseWorkflow(this IApplicationBuilder app)
  4. {
  5. var host = app.ApplicationServices.GetService<IWorkflowHost>();
  6. host.RegisterWorkflow<EdcWorkflow>();
  7. host.RegisterWorkflow<EdcDataWorkflow, EdcData>();
  8. host.Start();
  9.  
  10. var appLifetime = app.ApplicationServices.GetService<IApplicationLifetime>();
  11. appLifetime.ApplicationStopping.Register(() =>
  12. {
  13. host.Stop();
  14. });
  15.  
  16. return app;
  17. }
  18. }

  这里需要注意的就是:将你要用到的所有Workflow都事先进行Register注册。

3.2 通过DI获取使用  

  在你想要用到的地方,无论是Controller还是Service,通过依赖注入获取到Host,并使用它:

  1. [Route("api/[controller]")]
  2. [ApiController]
  3. public class ValuesController : ControllerBase
  4. {
  5. private IWorkflowController _workflowService;
  6.  
  7. public ValuesController(IWorkflowController workflowService)
  8. {
  9. _workflowService = workflowService;
  10. }
  11.  
  12. // GET api/values
  13. [HttpGet]
  14. public async Task<IEnumerable<string>> Get()
  15. {
  16. await _workflowService.StartWorkflow("EdcWorkflow");
  17. return new string[] { "EdcWorkflow v1" };
  18. }
  19.  
  20. // GET api/values/5
  21. [HttpGet("{id}")]
  22. public async Task<string> Get(int id)
  23. {
  24. await _workflowService.StartWorkflow("EdcDataWorkflow", new EdcData() { Id = id });
  25. return "EdcDataWorkflow v1";
  26. }
  27. }

  这两个Workflow的定义如下:

  1. public class EdcWorkflow : IWorkflow
  2. {
  3. public string Id => "EdcWorkflow";
  4.  
  5. public int Version => ;
  6.  
  7. public void Build(IWorkflowBuilder<object> builder)
  8. {
  9. builder
  10. .StartWith<HelloWorld>()
  11. .Then<GoodbyeWorld>();
  12. }
  13. }
  14.  
  15. public class EdcDataWorkflow : IWorkflow<EdcData>
  16. {
  17. public string Id => "EdcDataWorkflow";
  18.  
  19. public int Version => ;
  20.  
  21. public void Build(IWorkflowBuilder<EdcData> builder)
  22. {
  23. builder
  24. .StartWith<HelloWorld>()
  25. .If(data => data.Id < ).Do(then => then
  26. .StartWith<PrintMessage>()
  27. .Input(step => step.Message, data => "Passed Id is less than 3")
  28. )
  29. .If(data => data.Id < ).Do(then => then
  30. .StartWith<PrintMessage>()
  31. .Input(step => step.Message, data => "Passed Id is less than 5")
  32. )
  33. .Then<GoodbyeWorld>();
  34. }
  35. }

  示例结果很简单:

  (1)api/values

  

  (2)api/values/1

  

四、小结

  Workflow-Core是一个适合.NET Core的优秀的轻量级工作流引擎,对于小型工作流和责任链类型的需求开发很适合,可以节约大量时间避免重复造轮子,将时间主要花在业务逻辑上面。当然,这里演示的示例只是众多功能特性中的一小部分,我只是选取了我用到的部分而已,大家有兴趣的话可以去GitHub上先给个star再仔细研究其wiki文档,应用到自己的项目中去。

  示例代码:https://github.com/EdisonChou/EDC.WorkflowCore.Sample

作者:周旭龙

出处:https://edisonchou.cnblogs.com

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。

一个适合于.NET Core的超轻量级工作流引擎:Workflow-Core的更多相关文章

  1. 开源工作流引擎 Workflow Core 的研究和使用教程

    目录 开源工作流引擎 Workflow Core 的研究和使用教程 一,工作流对象和使用前说明 二,IStepBuilder 节点 三,工作流节点的逻辑和操作 容器操作 普通节点 事件 条件体和循环体 ...

  2. 基于领域驱动设计(DDD)超轻量级快速开发架构

    smartadmin.core.urf 这个项目是基于asp.net core 3.1(最新)基础上参照领域驱动设计(DDD)的理念,并参考目前最为了流行的abp架构开发的一套轻量级的快速开发web ...

  3. 分享自己的超轻量级高性能ORM数据访问框架Deft

    Deft 简介 Deft是一个超轻量级高性能O/R mapping数据访问框架,简单易用,几分钟即可上手. Deft包含如下但不限于此的特点: 1.按照Transact-SQL的语法语义风格来设计,只 ...

  4. 超轻量级高性能ORM数据访问组件Deft,比dapper快20%以上

    超轻量级高性能ORM数据访问组件Deft,比dapper快20%以上 阅读目录 Deft简介 Deft 核心类介绍 Deft 3分钟即可上手使用 其他可选的配置参数 性能测试 Demo代码下载 回到顶 ...

  5. 基于Groovy+HttpRestful的超轻量级的接口测试用例配置的设计方案及DEMO实现

    目标 设计一个轻量级测试用例框架,接口测试编写者只需要编写测试用例相关的内容(入参及结果校验),不需要理会系统的实现,不需要写跟测试校验无关的内容. 思路 测试用例分析 一个用例由以下部分组成: (1 ...

  6. 聊聊如何设计千万级吞吐量的.Net Core网络通信!

    聊聊如何设计千万级吞吐量的.Net Core网络通信! 作者:大石头 时间:2018-10-26 晚上 20:00 地点:QQ群-1600800 内容:网络通信, 网络库使用方式 网络库设计理念,高性 ...

  7. 跨平台移动开发 Xuijs超轻量级的框架Style CSS属性用法

    PhoneGap里面推荐使用的超轻量级的框架 Style CSS属性用法 设置css属性:setstyle 通过ID设置css属性 x$('#top1').setStyle('color', '#DB ...

  8. 超轻量级网络SqueezeNet网络解读

    SqueezeNet网络模型非常小,但分类精度接近AlexNet. 这里复习一下卷积层参数的计算 输入通道ci,核尺寸k,输出通道co,参数个数为: 以AlexNet第一个卷积为例,参数量达到:3*1 ...

  9. 基于领域驱动设计(DDD)超轻量级快速开发架构(二)动态linq查询的实现方式

    -之动态查询,查询逻辑封装复用 基于领域驱动设计(DDD)超轻量级快速开发架构详细介绍请看 https://www.cnblogs.com/neozhu/p/13174234.html 需求 配合Ea ...

随机推荐

  1. Linux搭建Java环境(JDK+Tomcat+MySQL)

    目录 一.项目环境: 二.安装JDK1.8 三.安装Tomcat8.5 四.安装MySQL数据库 五.配置JAVA项目 一.项目环境: 开发环境 生产环境 测试环境 硬件环境: web服务器:cpu: ...

  2. 01 | 可见性、原子性和有序性问题:并发编程Bug的源头

    由于CPU.内存.I/O 设备的速度差异,为了合理利用 CPU 的高性能,平衡这三者的速度差异,计算机体系机构.操作系统.编译程序都做出以下处理: 1. CPU 增加了缓存,以均衡与内存的速度差异: ...

  3. django基础之day04,必知必会13条,双下划线查询,字段增删改查,对象的跨表查询,双下划线的跨表查询

    from django.test import TestCase # Create your tests here. import os import sys if __name__ == " ...

  4. k8s~跨namespace的service相互访问

    在k8s里,你可以通过服务名去访问相同namespace里的服务,然后服务可以解析到对应的pod,从而再由pod转到对应的容器里,我们可以认为这个过程有两个port的概念,service port 就 ...

  5. 微信小程序APP生命周期

    小程序APP生命周期需要先从app.js这个文件开始,App() 必须在 app.js 中调用,必须调用且只能调用一次,app.js中定义了一些应用的生命周期函数 onLaunch----当小程序初始 ...

  6. JS获取包含当前节点本身的代码内容(outerHtml)

    原生JS DOM的内置属性 outerHTML 可用来获取当前节点的html代码(包含当前节点),且此属性可使用jQuery的prop()获取 <div id="demo-test-0 ...

  7. Scrcpy用电脑控制Android手机(支持Windows/macOS/Linux)

    一.scrcpy简介 注意:拼写是scrcpy,非Python爬虫框架Scrapy. scrcpy可以通过adb调试的方式来将手机屏幕投到电脑上,并可以通过电脑控制您的Android设备.它可以通过U ...

  8. jquery-获取button的值

    <button id="test" name="test_name" value="test_value"></butto ...

  9. Ajax之处理不同格式的JSON数据

    JSON是一种网络中的数据格式,主要用于网络间的数据传输,它比XML格式的数据传输速度快,使用更广. 1.Ajax处理对象格式的JSON数据: <script src="../JS/j ...

  10. pycharm安装第三方包问题解决

    pycharm安装第三方包问题解决 pycharm是一个基于python的非常好用的集成开发环境,而python有许多非常不错的开源第三方库,这就需要将一些这样的第三方库导入到我们的项目中去了.然而, ...