使用 Elsa 工作流 ForEach 循环活动

问题描述

我的工作流程是在这样的信号上触发的:

public async Task<IActionResult> StartApprovalProcess([FromBody] long requestId)
{
    if (!ModelState.IsValid)
        return BadRequest(ModelState);

    // Get data object
    var payload = await _mainService.GetBudgetReleaseRequestPayload(requestId);

    var input = new Variables();

    input.Setvariable("Payload",payload);

    // Signal the workflow to start
    await _workflowInvoker.TriggerSignalAsync("StartApprovalPhase",input);

    return Ok("BRR registered");
}

这是我的有效载荷类:

public class BudgetReleaseRequestApprovalPhasePayloadModel
{
    public BudgetReleaseRequestApprovalPhasePayloadModel(BudgetReleaseRequestApprovalPhasePayloadDto model)
    {
        Id                 = model.Id;
        Description        = model.Description;
        Amount             = model.Amount;
        RequesterId        = model.RequesterId;
        SubmissionDate     = model.SubmissionDate;
        CostCenterName     = model.CostCenterName;
        ExpenseTypeName    = model.ExpenseTypeName;
        RequestTypeName    = model.RequestTypeName;
        AccountCode        = model.AccountCode;
        AccountName        = model.AccountName;
        BpsReferenceNumber = model.BpsReferenceNumber;

        ApproversList = new List<BudgetReleaseRequestApproverviewmodel>();

        foreach (var budgetReleaseRequestApprover in model.ApproversList)
        {
            ApproversList.Add(new BudgetReleaseRequestApproverviewmodel(budgetReleaseRequestApprover));
        }
    }

    public long     Id                 { get; set; }
    public string   Description        { get; set; }
    public decimal  Amount             { get; set; }
    public string   RequesterId        { get; set; }
    public DateTime SubmissionDate     { get; set; }
    public string   CostCenterName     { get; set; }
    public string   ExpenseTypeName    { get; set; }
    public string   RequestTypeName    { get; set; }
    public string   AccountCode        { get; set; }
    public string   AccountName        { get; set; }
    public string   BpsReferenceNumber { get; set; }

    public string AmountFormatted   => $"{Amount:N2} AED";
    public string DateFormatted     => $"{SubmissionDate:dd-MMM-yyyy}";
    public string CostCenterandType => $"{CostCenterName}/{ExpenseTypeName}";
    public string AccountDetail     => $"{AccountCode} - {AccountName}";
    public int    ApproversCount    => ApproversList.Count;

    public IList<BudgetReleaseRequestApproverviewmodel> ApproversList { get; set; }
}

这里是作为集合的类:

public class BudgetReleaseRequestApproverviewmodel
{
    public BudgetReleaseRequestApproverviewmodel(BudgetReleaseRequestApprover model)
    {
        RequestId         = model.RequestId;
        RequestApproverId = model.RequestApproverId;
        ApproverId        = model.ApproverId;
        RequesterId       = model.RequesterId;
        ApproverSequence  = model.ApproverSequence;
        ActionId          = model.ActionId;
        RequestActionId   = model.RequestActionId;
    }

    public long   RequestId         { get; set; }
    public byte   RequestApproverId { get; set; }
    public string ApproverId        { get; set; }
    public string RequesterId       { get; set; }
    public byte   ApproverSequence  { get; set; }
    public Guid?  ActionId          { get; set; }
    public byte?  RequestActionId   { get; set; }

}

我遵循了主要指南 (https://sipkeschoorstra.medium.com/building-workflow-driven-net-core-applications-with-elsa-139523aa4c50),并且知道我们需要实现一个处理程序,以便在这两种模型的工作流程中使用 Liquid 表达式:

public class LiquidConfigurationHandler : INotificationHandler<EvaluatingLiquidExpression>
{
    public Task Handle(EvaluatingLiquidExpression notification,CancellationToken cancellationToken)
    {
        var context = notification.TemplateContext;
        context.MemberAccessstrategy.Register<BudgetReleaseRequestApprovalPhasePayloadModel>();
        context.MemberAccessstrategy.Register<BudgetReleaseRequestApproverviewmodel>();

        return Task.CompletedTask;
    }
}

这是我的测试工作流程:

{
    "activities": [{
            "id": "abc63216-76e7-42b2-ab7b-5cdb6bbc3ed9","type": "Signaled","left": 122,"top": 365,"state": {
                "signal": {
                    "expression": "StartApprovalPhase","Syntax": "Literal"
                },"name": "","title": "Signal: Start Approval Phase","description": "Trigger the workflow when this signal is received."
            },"blocking": false,"executed": false,"faulted": false
        },{
            "id": "ac7669d6-b7e6-4139-825e-5f2b9c1dbdb8","type": "SendEmail","left": 553,"top": 379,"state": {
                "from": {
                    "expression": "my.email@acme.co","to": {
                    "expression": "my.email@acme.co","subject": {
                    "expression": "Workflow Testing","body": {
                    "expression": "<p>BRR #{{ Input.Payload.Id }}</p>\r\n<p>Name: {{ Input.Payload.Description }}</p>\r\n<p>Amount: {{ Input.Payload.AmountFormatted }}</p>\r\n<p>Date: {{ Input.Payload.DateFormatted }}</p>\r\n<br />\r\n<p>Approvers: {{ Input.Payload.ApproversCount }}</p>","Syntax": "Liquid"
                },"title": "Email: Test","description": ""
            },{
            "id": "2efcffa9-8e18-45cf-aac8-fcfdc8846df8","type": "ForEach","left": 867,"top": 474,"state": {
                "collectionExpression": {
                    "expression": "{{ Input.Payload.ApproversList }}","iteratorName": "","title": "",{
            "id": "7966b931-f683-4b81-aad4-ad0f6c628191","left": 1042,"top": 675,"subject": {
                    "expression": "Looping #","body": {
                    "expression": "Loop Details",{
            "id": "5f246eda-271d-46ed-8efe-df0f26d542be","left": 1163,"top": 325,"state": {
                "name": "","from": {
                    "expression": "my.email@acme.co","subject": {
                    "expression": "Loop Over","body": {
                    "expression": "Loop Finished","faulted": false
        }
    ],"connections": [{
            "sourceActivityId": "2efcffa9-8e18-45cf-aac8-fcfdc8846df8","destinationActivityId": "5f246eda-271d-46ed-8efe-df0f26d542be","outcome": "Done"
        },{
            "sourceActivityId": "abc63216-76e7-42b2-ab7b-5cdb6bbc3ed9","destinationActivityId": "ac7669d6-b7e6-4139-825e-5f2b9c1dbdb8",{
            "sourceActivityId": "ac7669d6-b7e6-4139-825e-5f2b9c1dbdb8","destinationActivityId": "2efcffa9-8e18-45cf-aac8-fcfdc8846df8",{
            "sourceActivityId": "2efcffa9-8e18-45cf-aac8-fcfdc8846df8","destinationActivityId": "7966b931-f683-4b81-aad4-ad0f6c628191","outcome": "Iterate"
        },{
            "sourceActivityId": "7966b931-f683-4b81-aad4-ad0f6c628191","outcome": "Done"
        }
    ]
}

视觉:

Elsa-workflow

这是我的结果:

  • 信号:有效
  • 第一封电子邮件:作品:

Email-Sample

  • ForEach 失败,我在调试中发现:

    失败:Elsa.Expressions.WorkflowExpressionEvaluator[0] 评估 JavaScript 表达式“{{ Input.Payload.ApproversList }}”时出错。消息:输入未定义 参考错误:输入未定义 失败:Elsa.Services.ActivityInvoker[0] 调用工作流 de8e12d4645e4480abccbbe562b48448 的活动 2efcffa9-8e18-45cf-aac8-fcfdc8846df8 时出错 Elsa.Exceptions.WorkflowException:评估 JavaScript 表达式“{{ Input.Payload.ApproversList }}”时出错。消息:输入未定义 ---> ReferenceError:输入未定义 --- 内部异常堆栈跟踪结束 --- 在 Elsa.Expressions.WorkflowExpressionEvaluator.EvaluateAsync(IWorkflowExpression 表达式,类型类型,WorkflowExecutionContext 工作流执行上下文,CancellationToken 取消令牌) 在 Elsa.Extensions.WorkflowExpressionEvaluatorExtensions.EvaluateAsync[T](IWorkflowExpressionEvaluator 评估器,IWorkflowExpression1 expression,WorkflowExecutionContext workflowExecutionContext,CancellationToken cancellationToken) at Elsa.Activities.ControlFlow.Activities.ForEach.OnExecuteAsync(WorkflowExecutionContext context,CancellationToken cancellationToken) at Elsa.Services.ActivityInvoker.InvokeAsync(WorkflowExecutionContext workflowContext,IActivity activity,Func2 invokeAction)

    失败:Elsa.Services.WorkflowInvoker[0] dbupdateException 从 Elsa.WorkflowEventHandlers.PersistenceWorkflowEventHandler 抛出的 IWorkflowEventHandler Microsoft.EntityFrameworkCore.dbupdateException:更新条目时出错。有关详细信息,请参阅内部异常。 ---> Newtonsoft.Json.JsonSerializationException:检测到类型为“Jint.Engine”的属性“Engine”的自引用循环。路径“Exception.InnerException.Error.Engine.Global”。

我需要迭代 BudgetReleaseRequestApproverviewmodel、发送电子邮件、等待操作、重复,但我无法弄清楚循环。

解决方法

此答案基于我在 GitHub issue 上提供的评论,这是 OP 问题的重复。为了完整起见,我提供以下内容。

尝试对 ForEach 活动使用 input 函数(确保所选语法为 JavaScript):

input('PayLoad').ApproverList

这将获得名为 "PayLoad" 的输入。

我不知道 Liquid 是否应该起作用。从 UX 的角度来看,我们应该要么确保它有,要么在没有的情况下甚至不允许该选项。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...