Orchestration input IInputConverter
does not handle colletion iterfaces
#3035
Labels
IInputConverter
does not handle colletion iterfaces
#3035
Description
Orchestrations receive
null
instead of an input if input binding type is anything other thanList<T>
orT[]
(perhaps some other select cases).If input type is e.g.
IEnuemrable<T>
orIReadOnlyCollection<T>
, input silently gets converted intonull
.Expected behavior
Input is set to the value that was passed to the orchestration.
Actual behavior
We get
null
Relevant source code snippets
Test setup looks like this:
Now I dug deep into the framework and found the following:
Durable Functions Monitor
extension for VSCode.OrchestrationInputConverter
fromMicrosoft.Azure.Functions.Worker.Extensions.DurableTask
, which I believe to me the main source of problems, since it returnsConversionResult.Unhandled
when in reality it should returnSuccess
.DefaultFunctionInputBindingFeature
fromMicrosoft.Azure.Functions.Worker.Context.Features
, which fall-backs to setting default (i.e.null
) values to bindings that failed conversion with all available input converters (i.e. if all returnedConversionResult.Unhandled
).azure-functions-durable-extension/src/Worker.Extensions.DurableTask/OrchestrationInputConverter.cs
Lines 51 to 73 in 85d4c87
Why is this wrong (in my opinion)? JSON (de-)serializer deserializes a collection (e.g.,
[1, 2, 3, 4]
) into aList<T>
, sovalue.GetType()
is actuallyList<T>
. However, the target type comes from the function signature! And it can be, e.g., anIEnumerable<T>
! Naturally,typeof(List<T>) != typeof(IEnumerable<T>)
and conversion fails. I believe the last check in condition should be replaced withcontext.TargetType.IsInstanceOfType(value)
, which will allow passingList<T>
asIEnumerable<T>
(or practically any other meaningful collection as long as interfaces match). I did not check if target type is used for deseriazliation, but I believe it is the expected behavior when deserializing into collection interfaces -- you always get some sort ofList<T>
orDictionary<TK, TV>
.Alternatively, a special branch could be introduced to only handle
IEnumerable<T>
-based types, ifcontext.TargetType.IsInstanceOfType(value)
is deemed too broad for general use cases.The second issue is here:
https://github.com/Azure/azure-functions-dotnet-worker/blob/5366211c497e10e7c1534010069f79a9d06106ca/src/DotNetWorker.Core/Context/Features/DefaultFunctionInputBindingFeature.cs#L71-L92
When all conversions fail, the input binding is set to
null
becauseIEnumerable<T>
is a reference type. Personally I would expect this branch to hard fail -- we have an input that is unbound and has no default parameter values, hence there is no way to populate it. That way we will get a much more concise and informative exception rather than dealing with wildNullReferenceExceptions
because our collection input is suddenlynull
.Known workarounds
Yup, steal
OrchestrationInputConverter
code and tweak the condition mentioned above:ModifiedOrchestrationInputConverter .cs
Which can be then globally injected as
App Details
<PackageReference Include="Microsoft.Azure.Functions.Worker" Version="2.0.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.DurableTask" Version="1.2.2" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http" Version="3.2.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" Version="2.0.0" />
Screenshots
The text was updated successfully, but these errors were encountered: