引子

工作流(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入门系列教程之一 初始化项目并创建第一个工作流的更多相关文章

  1. Angular2入门系列教程7-HTTP(一)-使用Angular2自带的http进行网络请求

    上一篇:Angular2入门系列教程6-路由(二)-使用多层级路由并在在路由中传递复杂参数 感觉这篇不是很好写,因为涉及到网络请求,如果采用真实的网络请求,这个例子大家拿到手估计还要自己写一个web ...

  2. Angular2入门系列教程6-路由(二)-使用多层级路由并在在路由中传递复杂参数

    上一篇:Angular2入门系列教程5-路由(一)-使用简单的路由并在在路由中传递参数 之前介绍了简单的路由以及传参,这篇文章我们将要学习复杂一些的路由以及传递其他附加参数.一个好的路由系统可以使我们 ...

  3. Angular2入门系列教程5-路由(一)-使用简单的路由并在在路由中传递参数

    上一篇:Angular2入门系列教程-服务 上一篇文章我们将Angular2的数据服务分离出来,学习了Angular2的依赖注入,这篇文章我们将要学习Angualr2的路由 为了编写样式方便,我们这篇 ...

  4. Angular2入门系列教程4-服务

    上一篇文章 Angular2入门系列教程-多个组件,主从关系 在编程中,我们通常会将数据提供单独分离出来,以免在编写程序的过程中反复复制粘贴数据请求的代码 Angular2中提供了依赖注入的概念,使得 ...

  5. ASP.NET MVC 入门系列教程

    ASP.NET MVC 入门系列教程 博客园ASP.NET MVC 技术专题 http://kb.cnblogs.com/zt/mvc/ 一个居于ASP.NET MVC Beta的系列入门文章,有朋友 ...

  6. Qt快速入门系列教程目录

    Qt快速入门系列教程目录

  7. Android视频录制从不入门到入门系列教程(一)————简介

    一.WHY Android SDK提供了MediaRecorder帮助开发者进行视频的录制,不过这个类很鸡肋,实际项目中应该很少用到它,最大的原因我觉得莫过于其输出的视频分辨率太有限了,满足不了项目的 ...

  8. Android视频录制从不入门到入门系列教程(三)————视频方向

    运行Android视频录制从不入门到入门系列教程(二)————显示视频图像中的Demo后,我们应该能发现视频的方向是错误的. 由于Android中,Camera给我们的视频图片的原始方向是下图这个样子 ...

  9. 数据挖掘入门系列教程(二)之分类问题OneR算法

    数据挖掘入门系列教程(二)之分类问题OneR算法 数据挖掘入门系列博客:https://www.cnblogs.com/xiaohuiduan/category/1661541.html 项目地址:G ...

随机推荐

  1. spring-注入集合对象

    1.创建Stu类 package com.spring.collections; import java.util.Arrays; import java.util.List; import java ...

  2. 解决一次calico异常情况,pod之间访问pod ip不通

    k8s 集群采用二进制安装,cni网络插件用calico通讯问题描述:发现有些pod不是很正常例如: ht13.node正常系统采样 [root@ht6 ~]# cat /etc/redhat-rel ...

  3. Hyperledger Fabric组织的动态添加和删除

    前言 在Fabric定制联盟链网络工程实践中,我们虚拟了一个工作室的联盟链网络需求,并根据此需求分析了整个网络的架构且已经完成了一个简单 fabric 网络模型.本文将在其基础上,在 mychanne ...

  4. Problem N: 输出回字形

    这个题如果用for循环直接做的话恐怕得做上几个小时吧,加上一点小技巧,用坐标法来写这个题.就像下面这样: 坐标原点不在矩形的角上,而在矩形的中心处,这只是算是一个技巧,理解起来好理解而已.

  5. Python 一网打尽<排序算法>之先从玩转冒泡排序开始

    1. 前言 所谓排序,就是把一个数据群体按个体数据的特征按从大到小或从小到大的顺序存放. 排序在应用开发中很常见,如对商品按价格.人气.购买数量--显示. 初学编程者,刚开始接触的第一个稍微有点难理解 ...

  6. 使用C#制作九九

    效果图如下 源码如下: using System; using System.Collections.Generic; using System.Linq; using System.Web; usi ...

  7. bootStrap简要和学习笔记

    bootStrap简要和学习笔记前端在学些了html.css.JavaScript三件套后,我们感觉前端变数太多了,需要创造力来设计一些可能经常使用的界面啊.按钮样式啊等,也就有了一些前端的框架,那何 ...

  8. 数仓建模—建模工具PdMan(CHINER)介绍

    数据仓库系列文章(持续更新) 数仓架构发展史 数仓建模方法论 数仓建模分层理论 数仓建模-宽表的设计 数仓建模-指标体系 数据仓库之拉链表 数仓-数据集成 数仓-数据集市 数仓-商业智能系统 数仓-埋 ...

  9. MKL与VS2019配置方法

    VS2019配置oneAPI并调用MKL库 oneAPI oneAPI是一个跨架构的编程工具,旨在简化跨GPU.CPU.FPGA和AI加速器之间的编程,可以与英特尔自身设备,或其他厂商的芯片配合使用, ...

  10. 普罗米修斯!Ubuntu下prometheus监控软件安装使用

    *Prometheus* 是一个开源的服务监控系统和时间序列数据库 官方网站:prometheus.io 一.安装prometheus cd /usr/local/        #进入安装目录 wg ...