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 属性用于唯一标识一个活动或连接。
  • sourcetarget 属性用于定义连接的源活动和目标活动。
  • sourcePorttargetPort 属性用于定义连接的源端口和目标端口。

JSON 结构与 C# 数据模型非常相似。主要的区别在于我们使用了 JavaScript 表达式而非 C# 的 Lambda 表达式,并且设计器创建了一个使用其 Root 属性包裹 Flowchart 对象的 Workflow 对象。

总结

在本章中,我们了解了 Elsa 工作流的核心概念。我们已经看到工作流是由活动组成的,这些活动可以相互连接。同时,我们也了解到工作流可以通过编程方式或设计器来定义。

在接下来的章节中,我们将探索更多概念。每当你看到工作流或活动的 C# 表示时,请记住通过设计器创建的 JSON 表示形式非常相似。主要不同之处在于设计器使用 JavaScript 表达式而不是 C# 的 Lambda 表达式,并且设计器会创建一个使用 Root 属性包裹 Flowchart 对象的 Workflow 对象。

在本文档中