工作流引擎之Elsa入门系列教程之一 初始化项目并创建第一个工作流
引子
工作流(Workflow)是对工作流程及其各操作步骤之间业务规则的抽象、概括描述。
为了实现某个业务目标,需要多方参与、按预定规则提交数据时,就可以用到工作流。
通过流程引擎,我们按照流程图,编排一系列的步骤,让数据可以按照一定的规则,一定的顺序,提交给一定的负责人进行处理,实现带有时间轴的数据协作。
目前dotnet平台主流工作流引擎有两个:
轻量级嵌入式工作流引擎。它支持多种持久化方式和并发提供程序,以允许多节点群集,可以编码或者使用json、xml编排工作流。
这个引擎功能比较简单,但不适合处理长期工作流(定时任务类型的),随着执行的次数越来越多,处理速度会越来越慢。
Workflow slow when the count of the execution point more and more #1028
PersistedWorkflow ExecutionPointers exponentially increase in workflow loop. #1030
而且它是异步的,通过webapi启动流程后不能实时返回此次流程中step返回的数据,官方更新速度也不太理想,所以不选择此工作流引擎。
Elsa Core 是一个工作流库,可在任何 .NET Core 应用程序中执行工作流。可以使用代码和可视化工作流设计器来定义工作流。(功能更加全面,附带可视化流程设计器与流程监控页面)
本系列文章选择使用Elsa作为流程引擎,准备介绍此流程引擎的使用与扩展,如何与Abp框架一起使用,集成swagger,一步一步实现一个Demo。
快速开始
我们用vs2022创建一个空的ASP.NET Core Web应用,作为工作流核心服务,包含仪表盘与流程API。
一步一步添加依赖与配置,并启动。后续慢慢改造。
初始化项目
创建一个名为ElsaCore.Server的新项目
dotnet new web -n "ElsaCore.Server"
进入项目文件夹中为项目安装包
cd ElsaCore.Server
dotnet add package Elsa
dotnet add package Elsa.Activities.Http
dotnet add package Elsa.Activities.Timers
dotnet add package Elsa.Activities.UserTask
dotnet add package Elsa.Activities.Temporal.Quartz
dotnet add package Elsa.Persistence.EntityFramework.SqlServer
dotnet add package Elsa.Server.Api
dotnet add package Elsa.Designer.Components.Web
dotnet add package Microsoft.EntityFrameworkCore.Tools
添加ef tools用于初始化数据库
Elsa.Activities.Temporal.Quartz可以换成Elsa.Activities.Temporal.Hangfire,后续会讲解集成Hangfire和仪表盘。
上面的Activities是Elsa提供的几个活动实现,Http就是通过webapi接口形式的、Timers提供定时任务功能、UserTask提供了用户审批的功能,后续会详细解释,并且还有好多其他的Activities,我们还可以自己实现一个新的。
修改Program.cs
using Elsa;
using Elsa.Persistence.EntityFramework.Core.Extensions;
using Elsa.Activities.UserTask.Extensions;
using Elsa.Persistence.EntityFramework.SqlServer;
var builder = WebApplication.CreateBuilder(args);
// Elsa services.
var elsaSection = builder.Configuration.GetSection("Elsa");
builder.Services.AddElsa(elsa => elsa
.UseEntityFrameworkPersistence(ef => ef.UseSqlServer(builder.Configuration.GetConnectionString("Default"), typeof(Program)))
.AddConsoleActivities()
.AddJavaScriptActivities()
.AddUserTaskActivities()
.AddHttpActivities(elsaSection.GetSection("Server").Bind)
.AddQuartzTemporalActivities()
.AddWorkflowsFrom<Program>()
)
// Elsa API endpoints.
.AddElsaApiEndpoints()
// For Dashboard.
.AddRazorPages();
var app = builder.Build();
app.UseStaticFiles()// For Dashboard.
.UseHttpActivities()
.UseRouting()
.UseEndpoints(endpoints =>
{
// Elsa API Endpoints are implemented as regular ASP.NET Core API controllers.
endpoints.MapControllers();
// For Dashboard
endpoints.MapFallbackToPage("/_Host");
});
app.Run();
添加appsettings.json配置
BaseUrl的端口号要和launchSettings.json中的一致
"ConnectionStrings": {
"Default": "Server=(LocalDb)\\MSSQLLocalDB;Database=ElsaServer;Trusted_Connection=True"
},
"Elsa": {
"Server": {
"BaseUrl": "https://localhost:5001"
}
}
修改launchSettings.json
把launchSettings中的iis profiles删除,端口号改为5001
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "https://localhost:5001",
"sslPort": 5001
}
},
"profiles": {
"ElsaCore.Server": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "https://localhost:5001",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
初始化数据库
首先生成一次项目,然后执行
dotnet ef migrations add init
会自动创建Migrations目录。

然后更新数据库,执行
dotnet ef database update
此时打开SQL Server对象资源管理器可以看到数据库已经初始化完毕。
创建页面
新建目录Pages,创建在该目录下创建一个_Host.cshtml。
@page "/"
@{
var serverUrl = $"{Request.Scheme}://{Request.Host}";
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>Elsa Workflows</title>
<link rel="icon" type="image/png" sizes="32x32" href="/_content/Elsa.Designer.Components.Web/elsa-workflows-studio/assets/images/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/_content/Elsa.Designer.Components.Web/elsa-workflows-studio/assets/images/favicon-16x16.png">
<link rel="stylesheet" href="/_content/Elsa.Designer.Components.Web/elsa-workflows-studio/assets/fonts/inter/inter.css">
<link rel="stylesheet" href="/_content/Elsa.Designer.Components.Web/elsa-workflows-studio/elsa-workflows-studio.css">
<script src="/_content/Elsa.Designer.Components.Web/monaco-editor/min/vs/loader.js"></script>
<script type="module" src="/_content/Elsa.Designer.Components.Web/elsa-workflows-studio/elsa-workflows-studio.esm.js"></script>
</head>
<body>
<elsa-studio-root server-url="@serverUrl" monaco-lib-path="_content/Elsa.Designer.Components.Web/monaco-editor/min">
<elsa-studio-dashboard></elsa-studio-dashboard>
</elsa-studio-root>
</body>
</html>
启动项目
运行该项目,打开浏览器访问https://localhost:5001/,页面如下所示:

第一个HTTP Endpoint工作流
我们先定义一个简单的工作流,后续会实现启动参数与返回特定格式数据的流程。
定义工作流的方式有两种,使用设计器和代码。设计器定义的好处是可以在运行时动态添加与修改流程,并且是直接在流程图上进行修改,但是只能使用已注册的Activity,如果业务需要自定义Activity,则还是需要先写一些代码。
通过流程设计器定义
新建流程
选择菜单中的Workflow Definitions,进入工作流定义页,点击Create Workflow创建一个新的工作流。

点击Start,然后选择Http里面的HTTP Endpoint创建一个接口用来做为流程的入口。

设置参数并保存
- Path:
/design/hello-world - Methods: GET
接下来设置该接口的返回值。在流程的Done节点下点加号,选择HTTP里面的HTTPResponse,设置参数并保存:
- Content:
<h1>Hello World! </h1><p>这是通过设计器实现的流程</p> - Content Type:
text/html - Status Code:
OK

设置流程名称,点击右上角的设置按钮,设置Name为hello-world-design,Display Name为hello-world by design

点击右下角的publish发布流程。此时返回到Workflow Definitions中可以看到刚刚定义好的流程。

启动流程
因为hello-world-design这个流程是由HTTP Endpoint作为起点,所以我们可以通过接口来启动该流程。
访问hello-world-design可以看到如下效果

此时我们点击Workflow Instances可以看到刚刚执行的工作流实例,点击进入可以看到流程执行的详细过程。


使用代码定义
我们通过代码的方式实现上述流程。
新建流程
新建一个Workflows目录用于存放工作流。
创建一个类名为:HelloWorldWorkflow,并实现IWorkflow接口。具体代码如下:
using Elsa.Builders;
using Elsa.Activities.Http;
namespace ElsaCore.Server.Workflows
{
public class HelloWorldWorkflow : IWorkflow
{
public void Build(IWorkflowBuilder builder)
{
builder.HttpEndpoint(setup =>
{
setup.WithMethod(HttpMethod.Get.Method).WithPath("/code/hello-world");
})
.Then<WriteHttpResponse>(setup =>
{
setup.WithContentType("text/html")
.WithContent("<h1>Hello World! </h1><p>这是通过代码实现的流程</p>")
.WithStatusCode(System.Net.HttpStatusCode.OK);
});
}
}
}
因为我们在Program.cs中配置Elsa的时候使用了AddWorkflowsFrom<Program>(),所以会自动扫描目标类所在的程序集下所有实现IWorkflow接口的工作流自动注册。
否则需要调用AddWorkflow<HelloWorldWorkflow>()手动注册流程。
查看流程
启动项目并点击Workflow Registry可以看到我们刚刚创建的流程

点进去可以看到流程图,但因为是代码实现的所以是只读。

启动流程
访问https://localhost:5001/code/hello-world即可。

小结
本次我们创建了一个新项目,引入了一些Elsa相关的包,完成了工作流服务+图形化工作流仪表盘。创建了一个简单的工作流,但是这样是远远不够的,我们需要更加复杂的工作流,比如自定义参数、不同参数返回不同结果,模拟一些真实的业务场景,慢慢熟悉此框架,应用到真实的业务场景中,将在后续文章中体现,未完待续...
工作流引擎之Elsa入门系列教程之一 初始化项目并创建第一个工作流的更多相关文章
- Angular2入门系列教程7-HTTP(一)-使用Angular2自带的http进行网络请求
上一篇:Angular2入门系列教程6-路由(二)-使用多层级路由并在在路由中传递复杂参数 感觉这篇不是很好写,因为涉及到网络请求,如果采用真实的网络请求,这个例子大家拿到手估计还要自己写一个web ...
- Angular2入门系列教程6-路由(二)-使用多层级路由并在在路由中传递复杂参数
上一篇:Angular2入门系列教程5-路由(一)-使用简单的路由并在在路由中传递参数 之前介绍了简单的路由以及传参,这篇文章我们将要学习复杂一些的路由以及传递其他附加参数.一个好的路由系统可以使我们 ...
- Angular2入门系列教程5-路由(一)-使用简单的路由并在在路由中传递参数
上一篇:Angular2入门系列教程-服务 上一篇文章我们将Angular2的数据服务分离出来,学习了Angular2的依赖注入,这篇文章我们将要学习Angualr2的路由 为了编写样式方便,我们这篇 ...
- Angular2入门系列教程4-服务
上一篇文章 Angular2入门系列教程-多个组件,主从关系 在编程中,我们通常会将数据提供单独分离出来,以免在编写程序的过程中反复复制粘贴数据请求的代码 Angular2中提供了依赖注入的概念,使得 ...
- ASP.NET MVC 入门系列教程
ASP.NET MVC 入门系列教程 博客园ASP.NET MVC 技术专题 http://kb.cnblogs.com/zt/mvc/ 一个居于ASP.NET MVC Beta的系列入门文章,有朋友 ...
- Qt快速入门系列教程目录
Qt快速入门系列教程目录
- Android视频录制从不入门到入门系列教程(一)————简介
一.WHY Android SDK提供了MediaRecorder帮助开发者进行视频的录制,不过这个类很鸡肋,实际项目中应该很少用到它,最大的原因我觉得莫过于其输出的视频分辨率太有限了,满足不了项目的 ...
- Android视频录制从不入门到入门系列教程(三)————视频方向
运行Android视频录制从不入门到入门系列教程(二)————显示视频图像中的Demo后,我们应该能发现视频的方向是错误的. 由于Android中,Camera给我们的视频图片的原始方向是下图这个样子 ...
- 数据挖掘入门系列教程(二)之分类问题OneR算法
数据挖掘入门系列教程(二)之分类问题OneR算法 数据挖掘入门系列博客:https://www.cnblogs.com/xiaohuiduan/category/1661541.html 项目地址:G ...
随机推荐
- c++对c的拓展_指针的引用
套用引用公式:Type & ref =val; 假设:type 类型为int * 由公式得 int * & ref = val; // int * *const ref=&va ...
- 1、【Python运维脚本】Python 按时间删除和清空文件
删除和清空文件,用shell的话一条命令就够了,Python要一堆命令. 但是为了学习Python,所以用于实战,就得这么干了. Python 按时间删除和清空文件 #!/usr/bin/python ...
- k8s 初始化环境(1)
概念 k8s/kubernetes 容器化部署 解决容器编排问题,kubernetes为容器编排软件的佼佼者 kubernets为一组服务器集群 功能 自我修复 一个容器崩溃,另一个容器起来 弹性伸缩 ...
- ZABBIX Proxy容器启动的配置过程
ZABBIX Proxy容器启动的配置过程 环境介绍 版本 zabbix6 zabbix server 与 zabbix proxy 非同一台主机,zabbix proxy为主动方式提交给server ...
- Grafana插件Plugin中文汉化
示例Github地址 汉化三方插件 前面说过汉化Grafana的工作.目前在7.2.1上面,大部分已经完成.细节继续完善. 今天考虑在第三方插件上做一些汉化.点到插件一看全是英文感觉很突出.领导看到了 ...
- 【Azure API 管理】解决API Management添加AAD Group时遇见的 Failed to query Azure Active Directory graph due to error 错误
问题描述 为APIM添加AAD Group时候,等待很长很长的时间,结果添加失败.错误消息为: Write Groups ValidationError :Failed to query Azure ...
- 2021.08.10 Euler函数总结
2021.08.10 Euler函数总结 知识: 记 φ(n) 表示在 [1,n] 中与 n互质的数的个数. 1.p为质数,则 \[φ(p^l)=p^l-p=p^{l-1}(p-1) \] 注:每p个 ...
- @Transactional的使用与失效
@Transactinonal 注解在方法抛出RuntimeException类及其子类时.Error类及其子类时会回滚当前事务,使sql不提交: 只能作用于public的方法:写在类上时,代表给该类 ...
- 【译】defer-panic-and-recover
Go 有通用的控制流程:if,for,switch,goto.它也有go语句用于让代码运行在单独的协程.这里我将讨论一些不常见的问题:defer,panic 和 recover. defer语句将函数 ...
- python 本地配置文件库 Dynaconf 简介
[前言] 在项目中经常会遇到以下几种需要用到配置文件的场景: 相同的配置参数用在不同的代码中,如果需要调整,则需要手动将各个使用到的地方都相应调整. 密码等信息不想硬编码在项目文件中. 配置文件的格式 ...