BindingDataProvider.FromType Fails to Resolve Shadowed Properties
See original GitHub issueWhen a trigger binds to a type that shadows a base class property, the call to BindingTypeProvider.FromType
fails with:
Error Message:
Microsoft.Azure.WebJobs.Host.Indexers.FunctionIndexingException : Error indexing method '[[ METHOD ]]'
----> System.InvalidOperationException : Multiple properties named '[[ PROPERTY ]]' found in type '[[ TYPE ]]'.
Stack Trace:
--InvalidOperationException
at Microsoft.Azure.WebJobs.Host.Bindings.BindingDataProvider.FromType(Type type) in C:\projects\azure-webjobs-sdk-rqm4t\src\Microsoft.Azure.WebJobs.Host\Bindings\BindingDataProvider.cs:line 103
at Microsoft.Azure.WebJobs.Host.Triggers.CustomTriggerArgumentBinding`2..ctor(ITriggerBindingStrategy`2 bindingStrategy, IConverterManager converterManager, FuncAsyncConverter converter, Type userType) in C:\projects\azure-webjobs-sdk-rqm4t\src\Microsoft.Azure.WebJobs.Host\Triggers\TriggerArgumentBinding\CustomTriggerArgumentBinding.cs:line 37
at Microsoft.Azure.WebJobs.Host.Bindings.BindingFactoryHelpers.GetDirectTriggerBindingWorker[TMessage,TTriggerValue](ITriggerBindingStrategy`2 bindingStrategy, IConverterManager converterManager, Type userType) in C:\projects\azure-webjobs-sdk-rqm4t\src\Microsoft.Azure.WebJobs.Host\Bindings\BindingFactoryHelpers.cs:line 42
at Microsoft.Azure.WebJobs.Host.Bindings.BindingFactoryHelpers.GetDirectTriggerBinding[TMessage,TTriggerValue](Type exactType, ITriggerBindingStrategy`2 bindingStrategy, IConverterManager converterManager) in C:\projects\azure-webjobs-sdk-rqm4t\src\Microsoft.Azure.WebJobs.Host\Bindings\BindingFactoryHelpers.cs:line 30
at Microsoft.Azure.WebJobs.Host.Bindings.BindingFactoryHelpers.GetTriggerArgumentElement
When FromType
processes properties of the target type returned by PropertyHelper.GetProperties(type)
, it adds them to a dictionary in the order that they’re returned, failing by design if there are any duplicate names (src):
// The properties on user-defined types are valid binding data.
IReadOnlyList<PropertyHelper> bindingDataProperties = PropertyHelper.GetProperties(type);
if (bindingDataProperties.Count == 0)
{
return null;
}
Dictionary<string, Type> contract = new Dictionary<string, Type>(StringComparer.OrdinalIgnoreCase);
foreach (PropertyHelper property in bindingDataProperties)
{
if (contract.ContainsKey(property.Name))
{
throw new InvalidOperationException(
string.Format(CultureInfo.InvariantCulture,
"Multiple properties named '{0}' found in type '{1}'.", property.Name, type.Name));
}
contract.Add(property.Name, property.Property.PropertyType);
}
}
This disallows binding to types with properties that shadow a base class member.
Repro steps
-
Create a function that references the
Microsoft.Azure.WebJobs.Extensions.EventHubs
v5.x+ and the latest nightly build ofAzure.Messaging.EventHubs
from the Azure SDK development package feed. -
Create a function with an Event Hubs trigger, such as:
public static void ProcessMultipleEvents([EventHubTrigger(TestHubName, Connection = TestHubName)] EventData[] events)
{
// Code
}
- Observe the exception due to the new hierarchy for
EventData
where property shadowing is used:
EventData (src)
public class EventData : MessageWithMetadata
{
public new string ContentType { get; set; }
[EditorBrowsable(EditorBrowsableState.Never)]
protected override ContentType? ContentTypeCore
{
get => new ContentType(ContentType);
set => ContentType = value.ToString();
}
}
MessageWithMetadata (src)
public class MessageWithMetadata
{
public virtual ContentType? ContentType
{
get => ContentTypeCore;
set => ContentTypeCore = value;
}
[EditorBrowsable(EditorBrowsableState.Never)]
protected virtual ContentType? ContentTypeCore { get; set; }
}
Expected behavior
The binding succeeds by selecting the property declared on the type being bound rather than giving equal consideration to properties deeper in the hierarchy. In this case, the ContentType
property from EventData
should have been selected and the property from the base class ignored.
Actual behavior
Error Message
Microsoft.Azure.WebJobs.Host.Indexers.FunctionIndexingException : Error indexing method 'EventHubTestInitialOffsetFromEnqueuedTimeJobs.ProcessMultipleEvents'
----> System.InvalidOperationException : Multiple properties named 'ContentType' found in type 'EventData'.
Stack Trace
at Microsoft.Azure.WebJobs.Host.RecoverableException.TryRecover(ILogger logger) in C:\projects\azure-webjobs-sdk-rqm4t\src\Microsoft.Azure.WebJobs.Host\Exceptions\RecoverableException.cs:line 81
at Microsoft.Azure.WebJobs.Host.Indexers.FunctionIndexer.IndexTypeAsync(Type type, IFunctionIndexCollector index, CancellationToken cancellationToken) in C:\projects\azure-webjobs-sdk-rqm4t\src\Microsoft.Azure.WebJobs.Host\Indexers\FunctionIndexer.cs:line 94
at Microsoft.Azure.WebJobs.Host.Indexers.FunctionIndexProvider.CreateAsync(CancellationToken cancellationToken) in C:\projects\azure-webjobs-sdk-rqm4t\src\Microsoft.Azure.WebJobs.Host\Indexers\FunctionIndexProvider.cs:line 97
at Microsoft.Azure.WebJobs.Host.Indexers.FunctionIndexProvider.GetAsync(CancellationToken cancellationToken) in C:\projects\azure-webjobs-sdk-rqm4t\src\Microsoft.Azure.WebJobs.Host\Indexers\FunctionIndexProvider.cs:line 69
at Microsoft.Azure.WebJobs.Host.Executors.JobHostContextFactory.Create(JobHost host, CancellationToken shutdownToken, CancellationToken cancellationToken) in C:\projects\azure-webjobs-sdk-rqm4t\src\Microsoft.Azure.WebJobs.Host\Executors\JobHostContextFactory.cs:line 119
at Microsoft.Azure.WebJobs.JobHost.InitializeHostAsync(CancellationToken cancellationToken, TaskCompletionSource`1 initializationTask) in C:\projects\azure-webjobs-sdk-rqm4t\src\Microsoft.Azure.WebJobs.Host\JobHost.cs:line 347
at Microsoft.Azure.WebJobs.JobHost.StartAsyncCore(CancellationToken cancellationToken) in C:\projects\azure-webjobs-sdk-rqm4t\src\Microsoft.Azure.WebJobs.Host\JobHost.cs:line 95
at Microsoft.Azure.WebJobs.Host.EndToEndTests.WebJobsEventHubTestBase.BuildHost[T](Action`1 configurationDelegate, Action`1 preStartCallback) in /Users/runner/work/1/s/sdk/eventhub/Microsoft.Azure.WebJobs.Extensions.EventHubs/tests/WebJobsEventHubTestBase.cs:line 92
at Microsoft.Azure.WebJobs.Host.EndToEndTests.EventHubEndToEndTests.EventHub_InitialOffsetFromEnqueuedTime() in /Users/runner/work/1/s/sdk/eventhub/Microsoft.Azure.WebJobs.Extensions.EventHubs/tests/EventHubEndToEndTests.cs:line 411
at Microsoft.Azure.WebJobs.Host.EndToEndTests.EventHubEndToEndTests.EventHub_InitialOffsetFromEnqueuedTime() in /Users/runner/work/1/s/sdk/eventhub/Microsoft.Azure.WebJobs.Extensions.EventHubs/tests/EventHubEndToEndTests.cs:line 430
at Microsoft.Azure.WebJobs.Host.EndToEndTests.EventHubEndToEndTests.EventHub_InitialOffsetFromEnqueuedTime() in /Users/runner/work/1/s/sdk/eventhub/Microsoft.Azure.WebJobs.Extensions.EventHubs/tests/EventHubEndToEndTests.cs:line 430
at NUnit.Framework.Internal.TaskAwaitAdapter.GenericAdapter`1.BlockUntilCompleted()
at NUnit.Framework.Internal.MessagePumpStrategy.NoMessagePumpStrategy.WaitForCompletion(AwaitAdapter awaiter)
at NUnit.Framework.Internal.AsyncToSyncAdapter.Await(Func`1 invoke)
at NUnit.Framework.Internal.Commands.TestMethodCommand.RunTestMethod(TestExecutionContext context)
at NUnit.Framework.Internal.Commands.TestMethodCommand.Execute(TestExecutionContext context)
at NUnit.Framework.Internal.Commands.BeforeAndAfterTestCommand.<>c__DisplayClass1_0.<Execute>b__0()
at NUnit.Framework.Internal.Commands.BeforeAndAfterTestCommand.RunTestMethodInThreadAbortSafeZone(TestExecutionContext context, Action action)
--InvalidOperationException
at Microsoft.Azure.WebJobs.Host.Bindings.BindingDataProvider.FromType(Type type) in C:\projects\azure-webjobs-sdk-rqm4t\src\Microsoft.Azure.WebJobs.Host\Bindings\BindingDataProvider.cs:line 103
at Microsoft.Azure.WebJobs.Host.Triggers.CustomTriggerArgumentBinding`2..ctor(ITriggerBindingStrategy`2 bindingStrategy, IConverterManager converterManager, FuncAsyncConverter converter, Type userType) in C:\projects\azure-webjobs-sdk-rqm4t\src\Microsoft.Azure.WebJobs.Host\Triggers\TriggerArgumentBinding\CustomTriggerArgumentBinding.cs:line 37
at Microsoft.Azure.WebJobs.Host.Bindings.BindingFactoryHelpers.GetDirectTriggerBindingWorker[TMessage,TTriggerValue](ITriggerBi
Proposed fix
Update BindingDataProvider.FromType
on L99 to consider the type declaring the property. If a property is declared on the type
parameter directly, remove other members discovered lower in the hierarchy. Throw only when there is a naming conflict and the property does not exist directly on type
.
Related information
This behavior can be observed running tests for the Microsoft.Azure.WebJobs.Extensions.EventHubs
package from the Azure SDK for .NET repository.
Issue Analytics
- State:
- Created 2 years ago
- Comments:13 (9 by maintainers)
Top GitHub Comments
The new tools version is in testing now and will hopefully be released by mid next week. You’ll then be able to update just the tools - no VS update needed.
@mathewc I had used npm to update my version as described here, which had no effect.
I’ve now used the Windows installer (MSI) (from the link you provided) and this does work.
Thank you! 👍