Azure / Azure/azure-functions-dotnet-worker

BindInputAsync caches without regard to type, which leads to invalid cast later

Open
#2,332 5 comments 0 reactions 1 assignee Claimed by @RohitRanjanMS View on GitHub
needs-discussion Needs: Attention :wave:
Dominant language
C#
Stars
466
Forks
215
Avg merge
3d 10h
Merged PRs (30d)
7

Description

### Description

If I use `BindInputAsync(metadata)` where `T` is a base type of the actual input type, it successfully deserializes, however, the function later fails to invoke, saying it cannot cast from the base class to the actual class. I think this is because `BindInputAsync` caches the binding result regardless of what `T` is.

I want to enforce a common base class for `ActivityTrigger` inputs so that I can have middleware that can read the input for any `ActivityTrigger`.

See below... If you change the below to do `BindInputAsync()` it all works, but now the middleware isn't general enough.

### Steps to reproduce

```C#
using Azure.Messaging;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Middleware;
using Microsoft.DurableTask;
using Microsoft.DurableTask.Client;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

await new HostBuilder()
.ConfigureFunctionsWorkerDefaults(x => x.UseMiddleware())
.Build()
.RunAsync();

public class MessageBase
{
public int TenantId { get; set; }
}

public class MessageA : MessageBase
{
public string Name { get; set; }
}

internal class TenantMiddleware : IFunctionsWorkerMiddleware
{
public async Task Invoke(FunctionContext context, FunctionExecutionDelegate next)
{
int tenantId = 0;

if (context.FunctionDefinition.InputBindings.Values.FirstOrDefault(x => x.Type == "activityTrigger") is { } activity)
{
var input = await context.BindInputAsync(activity);
tenantId = input.Value!.TenantId;
}

context.Items.Add("TenantId", tenantId);

await next(context);
}
}

public class CloudEventFunction
{
private readonly ILogger _logger;

public CloudEventFunction(ILogger logger)
{
_logger = logger;
}

[Function("CloudEventFunction")]
public async Task RunCloudEventAsync(
[EventGridTrigger] CloudEvent cloudEvent,
[DurableClient] DurableTaskClient durableTaskClient)
{
await durableTaskClient.ScheduleNewOrchestrationInstanceAsync("OrchestratorFunction");
}

[Function("OrchestratorFunction")]
public async Task RunOrchestratorAsync([OrchestrationTrigger] TaskOrchestrationContext taskOrchestrationContext)
{
var message = new MessageA { TenantId = 42, Name = "Test" };
await taskOrchestrationContext.CallActivityAsync("ActivityFunction", message);
}

[Function("ActivityFunction")]
public Task RunActivityAsync([ActivityTrigger] MessageA message, FunctionContext context)
{
_logger.LogInformation("TenantId: {TenantId}", context.Items["TenantId"]);
return Task.CompletedTask;
}
}
```

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.