.net-8.0azure-durable-functions

How to fix a Durable Function .NET v8 ActivityTrigger (isolated worker) object casting (Error: JSON value could not be converted error)?


I've been working with Durable Functions and Task Orchestration (Fan-out/Fan-in pattern) in .NET 6 for a while now, and object casting works fine with ActivityTriggers. However when upgrading to .NET 8 (isolated workers) it seems that object casting from ActivityTriggers only works with strings. This means I'm doing a lot of unnecessary serializing / deserializing when passing objects around. Is there a better way of doing this without extra serialization step? The sample below results in the error "The JSON value could not be converted..."

var jobTypeName = "OrderImport";

[Function($"{jobTypeName}-Trigger")]
public async Task Trigger([OrchestrationTrigger] TaskOrchestrationContext context)
{   
    var functionName = $"{jobTypeName}-Trigger";
    var content = context.GetInput<string>();
    var (request, traceBundle) = Initialize(content, InitFunctionName(functionName, context));

    try
    {
        // Failure occurs here with Error "The JSON value could not be converted to System.Collections.Generic.List`1[Mindwire.Domain.Models.OrderRequestModel]"
        var processingGroups = await context.CallActivityAsync<List<OrderRequestModel>>($"{jobTypeName}-InitActivity", content);
        
        // .NET v8.0 Revision which corrects the issue.
        // var initActivityResult = await context.CallActivityAsync<string>($"{jobTypeName}-InitActivity", content);
        // var processingGroups = JsonConvert.DeserializeObject<List<OrderRequestModel>>(initActivityResult);
            
        var itemCount = processingGroups.Count();
        var parallelTasks = new Task[itemCount];

        for (int i = 0; i < itemCount; i++)
        {
            processingGroups[i].Iterator = i + 1;
            parallelTasks[i] = context.CallActivityAsync($"{jobTypeName}-Activity", processingGroups[i]);
        }

        await Task.WhenAll(parallelTasks);
        
        await FinalizeDurableOrchestration(context, request, true);
        logger.LogInformation($"{functionName} completed");
    }
    catch (Exception ex)
    {
        logger.LogError($"{functionName} Error: [{ex.Message}]");
        await FinalizeDurableOrchestration(context, request, false, ex.Message);
        throw;
    }
}

[Function($"{jobTypeName}-InitActivity")]
public async Task<List<OrderRequestModel>> InitActivity([ActivityTrigger] string content)
{
    var functionName = $"{jobTypeName}-InitActivity";
    var (request, traceBundle) = Initialize(content, functionName);
    return myService.ParseOrder(request);

    // .NET v8.0 Revision which corrects the issue.
    // var result = myService.ParseOrder(request);
    // return JsonConvert.SerializeObject(result);
}

Solution

  • Answered by comment from phil, with the solution here: Is a parameterless constructor now always required by System.Text.Json?

    For my needs I began using the [JsonConstructor] decorator on my related constructors (as the previous question suggests).

    [JsonConstructor]
    public MyClass()
    {
    }