elsa-core
elsa-core copied to clipboard
Elsa v3: Custom Activities are Broken
Attempted to add the simple Sum activity from the example in the docs but it fails:
Code:
public class Sum : CodeActivity<int>
{
public Sum(Variable<int> a, Variable<int> b, Variable<int> result)
{
A = new(a);
B = new(b);
Result = new(result);
}
public Input<int> A { get; set; } = default!;
public Input<int> B { get; set; } = default!;
protected override void Execute(ActivityExecutionContext context)
{
var a = A.Get(context);
var b = B.Get(context);
var result = a + b;
context.SetResult(result);
}
}
Stacktrace:
System.InvalidOperationException: Each parameter in the deserialization constructor on type 'MyProject.Workflows.General.SumActivity' must bind to an object property or field on deserialization. Each parameter name must match with a property or field on the object. Fields are only considered when 'JsonSerializerOptions.IncludeFields' is enabled. The match can be case-insensitive.
at System.Text.Json.ThrowHelper.ThrowInvalidOperationException_ConstructorParameterIncompleteBinding(Type parentType)
at System.Text.Json.Serialization.Converters.ObjectWithParameterizedConstructorConverter`1.OnTryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value)
at System.Text.Json.Serialization.JsonConverter`1.TryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value, Boolean& isPopulatedValue)
at System.Text.Json.Serialization.JsonConverter`1.ReadCore(Utf8JsonReader& reader, JsonSerializerOptions options, ReadStack& state)
at System.Text.Json.Serialization.Metadata.JsonTypeInfo`1.DeserializeAsObject(Utf8JsonReader& reader, ReadStack& state)
at System.Text.Json.JsonSerializer.ReadFromSpanAsObject(ReadOnlySpan`1 utf8Json, JsonTypeInfo jsonTypeInfo, Nullable`1 actualByteCount)
at System.Text.Json.JsonSerializer.Deserialize(JsonElement element, Type returnType, JsonSerializerOptions options)
at Elsa.Workflows.Services.ActivityFactory.Create(Type type, ActivityConstructorContext context)
at Elsa.Workflows.Services.ActivityDescriber.<>c__DisplayClass4_0.<DescribeActivityAsync>b__8(ActivityConstructorContext context)
at Elsa.Workflows.Management.Serialization.Converters.ActivityJsonConverter.Read(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options)
at System.Text.Json.Serialization.JsonConverter`1.TryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value, Boolean& isPopulatedValue)
at System.Text.Json.Serialization.JsonCollectionConverter`2.OnTryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, TCollection& value)
at System.Text.Json.Serialization.JsonConverter`1.TryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value, Boolean& isPopulatedValue)
at System.Text.Json.Serialization.JsonConverter`1.ReadCore(Utf8JsonReader& reader, JsonSerializerOptions options, ReadStack& state)
at System.Text.Json.JsonSerializer.ReadFromSpan[TValue](ReadOnlySpan`1 utf8Json, JsonTypeInfo`1 jsonTypeInfo, Nullable`1 actualByteCount)
at System.Text.Json.JsonSerializer.Deserialize[TValue](JsonElement element, JsonSerializerOptions options)
at Elsa.Workflows.Activities.Flowchart.Serialization.FlowchartJsonConverter.Read(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options)
at System.Text.Json.Serialization.JsonConverter`1.TryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value, Boolean& isPopulatedValue)
at System.Text.Json.Serialization.JsonConverter`1.ReadCore(Utf8JsonReader& reader, JsonSerializerOptions options, ReadStack& state)
at System.Text.Json.Serialization.Metadata.JsonTypeInfo`1.DeserializeAsObject(Utf8JsonReader& reader, ReadStack& state)
at System.Text.Json.JsonSerializer.ReadFromSpanAsObject(ReadOnlySpan`1 utf8Json, JsonTypeInfo jsonTypeInfo, Nullable`1 actualByteCount)
at System.Text.Json.JsonSerializer.Deserialize(JsonElement element, Type returnType, JsonSerializerOptions options)
at Elsa.Workflows.Services.ActivityFactory.Create(Type type, ActivityConstructorContext context)
at Elsa.Workflows.Services.ActivityDescriber.<>c__DisplayClass4_0.<DescribeActivityAsync>b__8(ActivityConstructorContext context)
at Elsa.Workflows.Management.Serialization.Converters.ActivityJsonConverter.Read(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options)
at System.Text.Json.Serialization.JsonConverter`1.TryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value, Boolean& isPopulatedValue)
at System.Text.Json.Serialization.Metadata.JsonPropertyInfo`1.ReadJsonAndSetMember(Object obj, ReadStack& state, Utf8JsonReader& reader)
at System.Text.Json.Serialization.Converters.ObjectDefaultConverter`1.OnTryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value)
at System.Text.Json.Serialization.JsonConverter`1.TryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value, Boolean& isPopulatedValue)
at System.Text.Json.Serialization.Metadata.JsonPropertyInfo`1.ReadJsonAndSetMember(Object obj, ReadStack& state, Utf8JsonReader& reader)
at System.Text.Json.Serialization.Converters.ObjectDefaultConverter`1.OnTryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value)
at System.Text.Json.Serialization.JsonConverter`1.TryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value, Boolean& isPopulatedValue)
at System.Text.Json.Serialization.JsonConverter`1.ReadCore(Utf8JsonReader& reader, JsonSerializerOptions options, ReadStack& state)
at System.Text.Json.Serialization.Metadata.JsonTypeInfo`1.ContinueDeserialize(ReadBufferState& bufferState, JsonReaderState& jsonReaderState, ReadStack& readStack)
at System.Text.Json.Serialization.Metadata.JsonTypeInfo`1.DeserializeAsync(Stream utf8Json, CancellationToken cancellationToken)
at System.Text.Json.Serialization.Metadata.JsonTypeInfo`1.DeserializeAsObjectAsync(Stream utf8Json, CancellationToken cancellationToken)
at FastEndpoints.RequestBinder`1.BindJsonBody(HttpRequest httpRequest, JsonSerializerContext serializerCtx, CancellationToken cancellation)
at FastEndpoints.RequestBinder`1.BindAsync(BinderContext ctx, CancellationToken cancellation)
at FastEndpoints.Endpoint`2.BindRequestAsync(EndpointDefinition def, HttpContext ctx, List`1 failures, CancellationToken ct)
at FastEndpoints.Endpoint`2.ExecAsync(CancellationToken ct)
at FastEndpoints.Endpoint`2.ExecAsync(CancellationToken ct)
at Elsa.Http.Middleware.WorkflowsMiddleware.InvokeAsync(HttpContext httpContext)
at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)
I had the same problem, just change it to below code, I think it is something to do with constructor parameters not being serializable
public class Sum : CodeActivity<int>
{
public Input<int> A { get; set; } = default!;
public Input<int> B { get; set; } = default!;
protected override void Execute(ActivityExecutionContext context)
{
var input1 = A.Get(context);
var input2 = B.Get(context);
var result = input1 + input2;
context.SetResult(result);
}
}
In general I've found the docs not to be great when persisting the workflows.
When deserialising the object, it must use one of the contructors to create it. The only information it has to deserialise it with is Input A and Input B (it doesn't have any Variables)
You can specify a private constructor that the Json serialiser should use with the information it has. By making it private, you're not changing the public interface to the class.
public class Sum : CodeActivity<int>
{
// --------------
[JsonConstructor]
private Sum(Input<int> a, Input<int> b){
A = a;
B = b;
}
// ----- OR -----
[JsonConstructor]
private Sum() { }
// ---------------
public Sum(Variable<int> a, Variable<int> b, Variable<int> result)
{
A = new(a);
B = new(b);
Result = new(result);
}
public Input<int> A { get; set; } = default!;
public Input<int> B { get; set; } = default!;
protected override void Execute(ActivityExecutionContext context)
{
var a = A.Get(context);
var b = B.Get(context);
var result = a + b;
context.SetResult(result);
}
}