工作流引擎之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 ...
随机推荐
- Spring的事务控制-基于注解的方式
模拟转账操作,即Jone减少500,tom增加500 如果有疑问请访问spring事务控制-基于xml方式 1.创建数据表 2.创建Account实体类 public class Account { ...
- Spring MVC 工作原理和流程、注解
Spring MVC 是实现MVC设计模式的企业级开发框架,是Spring框架的一个子模块,无需整合,开发起来更加便捷. MVC设计模式 MVC是一种设计模式,它将应用程序分为 Controller. ...
- socket套接字模块及黏包现象
一.socket套接字模块 socket概念 socket层 理解socket Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口.在设计模式中,Socket其实就是一个门面模 ...
- go interface{}使用
先上代码 func In(haystack []interface{}, needle interface{}) (bool, error) { sVal := reflect.ValueOf(hay ...
- docker进阶_dockerswarm
DockerSwarm Docker Swarm简介 Docker Swarm的功能 Docker Swarm包含两个方面:docker安全集群,以及一个微服务应用引擎 集群方面,swarm将 ...
- Java中日期格式化的实现算法
package com.study.test; import java.io.Serializable; import java.text.SimpleDateFormat; import java. ...
- shell语法习题练习进阶版
第4章 shell语法深度习题练习 4.1 使用if,case,函数的方法将服务改成system(centos6) 4.1.1 if方法 4.1.1.1 system实现 4.1.1.1.1 编写代码 ...
- javascript中的宏任务和微任务(二)
js事件轮询执行顺序总结: 1)所有的同步任务都在主线程上执行,行成一个执行栈. 2)除了主线程之外,还存在一个任务列队,只要异步任务有了运行结果,就在任务列队中植入一个时间标记. 3)主线程完成所有 ...
- CVPR 2022数据集汇总|包含目标检测、多模态等方向
前言 本文收集汇总了目前CVPR 2022已放出的一些数据集资源. 转载自极市平台 欢迎关注公众号CV技术指南,专注于计算机视觉的技术总结.最新技术跟踪.经典论文解读.CV招聘信息. M5Produc ...
- .NET宝藏API之:IHostedService,后台任务执行
我们在项目开发的过程中可能会遇到类似后台定时任务的需求,比如消息队列的消费者. 按照.NetF时的开发习惯首先想到的肯定是Windows Service,拜托,都什么年代了还用Windows服务(小声 ...