如何修复Elsa3工作流中SwitchCase表达式序列化错误?
问题场景
使用Elsa 3.1.3版本定义包含条件判断的Switch活动时,触发以下序列化错误:
System.NotSupportedException: 不支持'System.Func
2[[Elsa.Expressions.Models.ExpressionExecutionContext, Elsa.Expressions, Version=3.1.3.0, Culture=neutral, PublicKeyToken=null],[System.Threading.Tasks.ValueTask1[[System.Object, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]], System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]'实例的序列化和反序列化。
引发错误的工作流构建代码:
protected override void Build(IWorkflowBuilder builder) { var transactionCommandVariable = new Variable<IFlowCommand<TransactionPayload>>(); builder.Root = new Sequence { Activities = { new ReceiveCommandActivity(loggerFactory.CreateLogger<ReceiveCommandActivity>()) { Name = "ReceiveCommand", CanStartWorkflow = true, Result = new(transactionCommandVariable) // 将变量设置为活动的输出。 }, new Switch() { Cases = { new SwitchCase() { Label = "create transaction", Condition = Expression.DelegateExpression(ctx => transactionCommandVariable.Get(ctx) is FlowCommand<CreateTransactionPayload>), Activity = new Sequence { Activities = { new ValidateCreateTransactionCommandActivity( assetRepository, walletRepository, txValidator, loggerFactory.CreateLogger<ValidateCreateTransactionCommandActivity>()) { Command = new(transactionCommandVariable), }, new CreateTransactionActivity( txnRepository, mapper, eventPublisher, config, loggerFactory.CreateLogger<CreateTransactionActivity>()) { Command = new(transactionCommandVariable), } } } },
错误抛出位置的序列化代码:
public class JsonIgnoreCompositeRootConverter : JsonConverter<IActivity> { public override IActivity Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { throw new NotImplementedException(); } [UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "<Pending>")] public override void Write(Utf8JsonWriter writer, IActivity? value, JsonSerializerOptions options) { writer.WriteStartObject(); PropertyInfo[] obj = value?.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public) ?? Array.Empty<PropertyInfo>(); JsonSerializerOptions options2 = new JsonSerializerOptions(options); PropertyInfo[] array = obj; foreach (PropertyInfo propertyInfo in array) { if (propertyInfo.GetCustomAttribute<JsonIgnoreAttribute>() == null && propertyInfo.GetCustomAttribute<JsonIgnoreCompositeRootAttribute>() == null) { string propertyName = options.PropertyNamingPolicy?.ConvertName(propertyInfo.Name) ?? propertyInfo.Name; writer.WritePropertyName(propertyName); object value2 = propertyInfo.GetValue(value); if (value2 == null) { writer.WriteNullValue(); } else { // ERROR JsonSerializer.Serialize(writer, value2, options2); } } } writer.WriteEndObject(); } }
核心原因
DelegateExpression包含匿名委托实例,无法被JSON序列化,而Elsa工作流定义需要持久化存储,因此所有活动属性必须是可序列化类型。
解决方案
方案1:使用CSharpExpression替代DelegateExpression
将条件改为可序列化的CSharp表达式,直接引用工作流变量并检查类型:
protected override void Build(IWorkflowBuilder builder) { // 显式指定变量名称,确保表达式中可以正确引用 var transactionCommandVariable = new Variable<IFlowCommand<TransactionPayload>> { Name = "TransactionCommand" }; builder.Root = new Sequence { Activities = { // ... ReceiveCommandActivity 代码保持不变 new Switch() { Cases = { new SwitchCase() { Label = "create transaction", // 使用CSharpExpression编写类型检查逻辑 Condition = Expression.CSharpExpression( "TransactionCommand is FlowCommand<CreateTransactionPayload>" ), Activity = new Sequence { // ... 原有活动代码保持不变 } }, } } } }; }
方案2:自定义可序列化表达式(进阶)
如果需要更复杂的判断逻辑,可以实现IExpression接口并确保类型支持JSON序列化,但对于简单的类型检查,方案1已足够满足需求。
额外注意事项
- 确保CSharpExpression中引用的变量名称与工作流变量的
Name属性一致 - 如果类型不在Elsa默认引用的命名空间中,需要在表达式中使用完整的类型命名空间(例如
MyNamespace.FlowCommand<MyNamespace.CreateTransactionPayload>)
内容的提问来源于stack exchange,提问作者RomyZero

