elsa 程序化工作流与设计工作流
本质上,Elsa 执行的是 IActivity 对象。这些对象通过编程方式创建或使用设计器。当使用设计器创建时,工作流定义以 JSON 形式存储,并在运行时用于重建实际的 Workflow 对象(实现 IActivity )。
让我们看看这两种方法之间一些更重要的差异和相似之处。
程序化工作流
程序化工作流通过实例化 IActivity 对象并设置它们的属性来创建。例如,以下代码创建了一个提示用户输入姓名并打印到控制台的工作流:
// 定义一个工作流变量来捕获ReadLine活动的输出。
var nameVariable = new Variable<string>();
// 定义一个简单的顺序工作流:
var workflow = new Sequence
{
// 注册名称变量。
Variables = { nameVariable },
// 设置要运行的活动序列。
Activities =
{
new WriteLine("请输入您的名字:"),
new ReadLine(nameVariable),
new WriteLine(context => $"很高兴认识你,{nameVariable.Get(context)}!")
}
};
输出:
请输入您的名字:
Elsa
很高兴认识你,Elsa!
值得注意的是,我们使用了 Sequence 活动来定义一个顺序工作流。除了 Sequence 之外,Elsa 还支持 Flowchart 活动,允许您通过声明活动之间的连接来将工作流定义为活动图。以下是使用 Flowchart 重新实现先前工作流的示例:
// 定义一个工作流变量来捕获ReadLine活动的输出。
var nameVariable = new Variable<string>();
// 定义要放入流程图的活动:
var writeLine1 = new WriteLine("请输入您的名字:");
var writeLine2 = new ReadLine(nameVariable);
var writeLine3 = new WriteLine(context => $"很高兴认识你,{nameVariable.Get(context)}!");
// 定义一个流程图工作流:
var workflow = new Flowchart
{
// 注册名称变量。
Variables = { nameVariable },
// 添加活动。
Activities =
{
writeLine1,
writeLine2,
writeLine3
},
// 设置活动之间的连接。
Connections =
{
new Connection(writeLine1, writeLine2),
new Connection(writeLine2, writeLine3)
}
};
尽管实现不同,但输出将相同:
请输入您的名字:
Elsa
很高兴认识你,Elsa!
设计器工作流
使用设计器时,通过将活动拖放到画布上并连接它们来定义工作流。底层数据模型与程序化工作流使用的模型相同,并使用 Flowchart 活动。换句话说,使用设计器创建工作流时,您创建的工作流其 Root 属性设置为 Flowchart 活动。
使用设计器创建工作流时,输入值使用脚本表达式定义,而不是 C# Lambda 语句。虽然您可以在设计器中使用 C# 脚本表达式,但不同之处在于 C# 脚本表达式在运行时评估,而程序化工作流中的 Lambda 表达式在构建时编译。
让我们看一个使用 JavaScript 表达式的例子。
以下工作流是使用设计器创建的:
接下来,我们逐一查看每个活动:
活动 1
这是一个 WriteLine 活动。其 Text 属性设置为 请输入您的名字: 。
活动 2
这是一个 ReadLine 活动。该活动没有输入,但其 Output 属性设置为名为 Name 的工作流变量。
此变量在工作流的 Variables 设置中定义:
活动 3
这是一个 WriteLine 活动。其 Text 属性设置为 JavaScript 表达式:Nice to meet you, ${getName()}!\。
从 JavaScript 访问工作流变量
要从 JavaScript 访问工作流变量,您可以使用以下命名约定:
get{nameOfVariable}()。例如,要访问Name变量,您可以使用getName()。
JSON 表示
让我们从设计器导出工作流并查看其 JSON 结构(已格式化并简化以便阅读):
{
"name": "很高兴认识你",
"variables": [
{
"id": "d6d10d5433e646b9bc02f1d05efeb584",
"name": "Name",
"typeName": "String"
}
],
"root": {
"type": "Elsa.Flowchart",
"id": "Flowchart1",
"start": "WriteLine1",
"activities": [
{
"text": {
"typeName": "String",
"expression": {
"type": "Literal",
"value": "请输入您的名字:"
},
"memoryReference": {
"id": "WriteLine1:input-1"
}
},
"id": "WriteLine1",
"type": "Elsa.WriteLine"
},
{
"result": {
"typeName": "String",
"memoryReference": {
"id": "d6d10d5433e646b9bc02f1d05efeb584"
}
},
"id": "ReadLine1",
"type": "Elsa.ReadLine"
},
{
"text": {
"typeName": "String",
"expression": {
"type": "JavaScript",
"value": "`很高兴认识你,${getName()}!`"
},
"memoryReference": {
"id": "WriteLine2:input-1"
}
},
"id": "WriteLine2",
"type": "Elsa.WriteLine"
}
],
"connections": [
{
"source": "WriteLine1",
"target": "ReadLine1",
"sourcePort": "Done",
"targetPort": "In"
},
{
"source": "ReadLine1",
"target": "WriteLine2",
"sourcePort": "Done",
"targetPort": "In"
}
]
}
}
几个关键点需要注意:
variables属性包含了工作流变量。root属性包含了工作流的根活动,它是一个Flowchart活动。activities属性包含了构成工作流的活动。connections属性包含了活动之间的连接关系。memoryReference属性用于引用工作流变量。expression属性用于定义 JavaScript 表达式。typeName属性用于定义属性的类型。type属性用于定义活动的类型。id属性用于唯一标识一个活动或连接。source和target属性用于定义连接的源活动和目标活动。sourcePort和targetPort属性用于定义连接的源端口和目标端口。
JSON 结构与 C# 数据模型非常相似。主要的区别在于我们使用了 JavaScript 表达式而非 C# 的 Lambda 表达式,并且设计器创建了一个使用其 Root 属性包裹 Flowchart 对象的 Workflow 对象。
总结
在本章中,我们了解了 Elsa 工作流的核心概念。我们已经看到工作流是由活动组成的,这些活动可以相互连接。同时,我们也了解到工作流可以通过编程方式或设计器来定义。
在接下来的章节中,我们将探索更多概念。每当你看到工作流或活动的 C# 表示时,请记住通过设计器创建的 JSON 表示形式非常相似。主要不同之处在于设计器使用 JavaScript 表达式而不是 C# 的 Lambda 表达式,并且设计器会创建一个使用 Root 属性包裹 Flowchart 对象的 Workflow 对象。
抠丁客





