elsa-workflows - 使用 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)并且知道我们需要实现一个处理程序才能在其中包含液体表达式这两种模型的工作流程:
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",
"syntax": "Literal"
},
"to": {
"expression": "my.email@acme.co",
"syntax": "Literal"
},
"subject": {
"expression": "Workflow Testing",
"syntax": "Literal"
},
"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"
},
"name": "",
"title": "Email: Test",
"description": ""
},
"blocking": false,
"executed": false,
"faulted": false
}, {
"id": "2efcffa9-8e18-45cf-aac8-fcfdc8846df8",
"type": "ForEach",
"left": 867,
"top": 474,
"state": {
"collectionExpression": {
"expression": "{{ Input.Payload.ApproversList }}",
"syntax": "Liquid"
},
"iteratorName": "",
"name": "",
"title": "",
"description": ""
},
"blocking": false,
"executed": false,
"faulted": false
}, {
"id": "7966b931-f683-4b81-aad4-ad0f6c628191",
"type": "SendEmail",
"left": 1042,
"top": 675,
"state": {
"from": {
"expression": "my.email@acme.co",
"syntax": "Literal"
},
"to": {
"expression": "my.email@acme.co",
"syntax": "Literal"
},
"subject": {
"expression": "Looping #",
"syntax": "Literal"
},
"body": {
"expression": "Loop Details",
"syntax": "Literal"
},
"name": "",
"title": "",
"description": ""
},
"blocking": false,
"executed": false,
"faulted": false
}, {
"id": "5f246eda-271d-46ed-8efe-df0f26d542be",
"type": "SendEmail",
"left": 1163,
"top": 325,
"state": {
"name": "",
"from": {
"expression": "my.email@acme.co",
"syntax": "Literal"
},
"to": {
"expression": "my.email@acme.co",
"syntax": "Literal"
},
"subject": {
"expression": "Loop Over",
"syntax": "Literal"
},
"body": {
"expression": "Loop Finished",
"syntax": "Literal"
},
"title": "",
"description": ""
},
"blocking": false,
"executed": false,
"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",
"outcome": "Done"
}, {
"sourceActivityId": "ac7669d6-b7e6-4139-825e-5f2b9c1dbdb8",
"destinationActivityId": "2efcffa9-8e18-45cf-aac8-fcfdc8846df8",
"outcome": "Done"
}, {
"sourceActivityId": "2efcffa9-8e18-45cf-aac8-fcfdc8846df8",
"destinationActivityId": "7966b931-f683-4b81-aad4-ad0f6c628191",
"outcome": "Iterate"
}, {
"sourceActivityId": "7966b931-f683-4b81-aad4-ad0f6c628191",
"destinationActivityId": "2efcffa9-8e18-45cf-aac8-fcfdc8846df8",
"outcome": "Done"
}
]
}
这是我的结果:
- 信号:工作
- 第一封电子邮件: 作品:
ForEach 失败,我在调试中发现了这个:
失败:Elsa.Expressions.WorkflowExpressionEvaluator[0] 评估 JavaScript 表达式“{{ Input.Payload.ApproversList }}”时出错。消息:未定义输入 ReferenceError:未定义输入失败:Elsa.Services.ActivityInvoker[0] 调用工作流 de8e12d4645e4480abccbbe562b48448 Elsa.Exceptions.WorkflowException 的活动 2efcffa9-8e18-45cf-aac8-fcfdc8846df8 时出错:评估 JavaScript 表达式时出错“ {{ Input.Payload.ApproversList }}”。消息:未定义输入 ---> ReferenceError:未定义输入 --- 内部异常堆栈跟踪结束 --- 在 Elsa.Expressions.WorkflowExpressionEvaluator.EvaluateAsync(IWorkflowExpression expression, Type type, WorkflowExecutionContext workflowExecutionContext, CancellationToken cancelToken) 在Elsa.Extensions.WorkflowExpressionEvaluatorExtensions。
1 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, Func
2 调用动作)失败:Elsa.Services.WorkflowInvoker[0] DbUpdateException 从 Elsa.WorkflowEventHandlers.PersistenceWorkflowEventHandler 引发的 IWorkflowEventHandler Microsoft.EntityFrameworkCore.DbUpdateException:更新条目时出错。有关详细信息,请参阅内部异常。---> Newtonsoft.Json.JsonSerializationException:为“Jint.Engine”类型的属性“Engine”检测到自引用循环。路径'Exception.InnerException.Error.Engine.Global'。
我需要迭代 a BudgetReleaseRequestApproverViewModel
,发送电子邮件,等待操作,重复,但我无法弄清楚循环。
解决方案
这个答案基于我在GitHub 问题上提供的评论,这是对 OP 问题的重复。为了完整起见,我提供以下内容。
尝试使用ForEachinput
活动的函数(确保所选语法是 JavaScript):
input('PayLoad').ApproverList
这将获得名为 的输入"PayLoad"
。
我不知道Liquid是否应该工作。从用户体验的角度来看,我们应该确保它这样做,或者如果没有,甚至不允许该选项。
推荐阅读
- javascript - 在字符串的每一层大括号上插入字符串
- python - 如果进程终止,请执行某些操作
- python - 如何使 pg.PlotItem.removeItem() 仅通过名称识别 PlotDataItems?
- javascript - 如何在 Vuejs 中使用 img 源绑定?
- opencv - 每张图像推理的 YOLOv3 分辨率和执行时间
- python - 当使用 Openpyxl 的行上的单元格中有字符串值时,如何突出显示 Excel 行?
- javascript - 为什么我的变量的值没有显示在我的反应组件的渲染中?
- python - `elif` 无法识别
- python - 使用 Beautiful Soup Python 抓取网页表
- python - 基于python分配用户ID的有效方法