aspnet-api-versioning: OData - Same Named Function in Two Configurations Causes System.InvalidOperationException on Startup

tldr - In the OData example project I added a function with the same name to two different configurations. The examples can no longer run. I used to be able to add functions with the same name to two different OData endpoints without issue.

Example Repo - https://github.com/benhysell/aspnet-api-versioning Example Project - https://github.com/benhysell/aspnet-api-versioning/tree/master/samples/aspnetcore/SwaggerODataSample

Using the examples I’ve duplicated the MostExpensive function from Order to Person via the steps below to trigger the exception.

Steps to Reproduce

Is it allowable to have the same named function in two different models? And if not why not?

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 2
  • Comments: 19

Commits related to this issue

Most upvoted comments

I’ve run into this issue as well.

This appears to be caused by a bug way down in the depths of Microsoft.OData.Edm, right here.

For some context, I created a quick sample to isolate this issue with two edm entity types Foo and Bar, each having a function Bulk

The line of code linked above is ultimately called by ODataRouteBuilderContext, from here

When we hit line 2528 of HasEquivalentBindingType, parameterType.TypeKind is EdmTypeKind.Collection while bindingType.TypeKind is EdmTypeKind.Entity. This results in this method immediately returning false on line 2530.

I do not know, but it looks like the special collection handling on line 2533 might need to be run first such as

if (parameterType.TypeKind == EdmTypeKind.Collection)
{
    // covariance applies here, so IEnumerable<A> is applicable to IEnumerable<B> where B:A
    IEdmCollectionType parameterCollectionType = (IEdmCollectionType)parameterType;
    IEdmCollectionType bindingCollectionType = (IEdmCollectionType)bindingType;

    return bindingCollectionType.ElementType.Definition.IsOrInheritsFrom(parameterCollectionType.ElementType.Definition);
}
else if (parameterType.TypeKind != bindingType.TypeKind)
{
    return false;
}
else
{
    return bindingType.IsOrInheritsFrom(parameterType);
}

or something along those lines. I should point out that the above code doesn’t actually work because parameterType.TypeKind is Collection and bindingType does not implement IEdmCollectionType

@commonsensesoftware I don’t know if it’d be more productive for you to communicate this issue with whoever maintains Microsoft.OData.Edm or if I should write up an issue against that repo.

Sample project can be found here

It should be allowable. Resolving an operation by its qualified name was previously an issue. I could have sworn I had a test for that. I’ll have to spelunk some more. This seems to be the issue. I distinctly recall using SingleOrDefault to make sure this exact failure would happen in the event the name is ambiguous (as FirstOrDefault would likely be harder to understand or track down).

I did notice you copied the MostExpensive method verbatim from OrdersController. The OrdersController intentionally uses attribute-routing and PeopleController intentionally uses convention-based routing to demonstrate the different styles. I doubt that’s the cause this issue, but you’ll want to be careful with mixing and matching as it’s bound to cause issues.

Since you already have a repro setup with the source, you might break inside ODataRouteBuilderContext.ResolveOperation. You might find why the function is not being resolved.