Skip to content

Routing to controllers with the same name #1494

Open
@andrew-laughlin

Description

@andrew-laughlin

I have 2 controllers with the same name, but in different namespaces. Both have GET actions with differing parameter lists. I'm using convention-based routing.

// v1
namespace v1
{
    public class SensorsController : ODataController 
    {
        [HttpGet]
        public IActionResult Get(ODataQueryOptions<v1.Sensor> query, ODataQuerySettings querySettings) {}
     }
}

// v2
namespace v2
{
    public class SensorsController : ODataController 
    {
        [HttpGet]
        public IActionResult Get(ODataQueryOptions<v2.Sensor> query, ODataQuerySettings querySettings, [FromODataUri] Guid tid){}
     }
}

// Startup.cs
app.UseMvc(routeBuilder =>
{
	IEdmModel model = BuildEdmModelV1(app.ApplicationServices); // v1 models
	routeBuilder.MapODataServiceRoute("ODataRouteV1", "odata", model);

	IEdmModel model = BuildEdmModelV2(app.ApplicationServices); // v2 models
	routeBuilder.MapODataServiceRoute("ODataRouteV2", "v2/odata/tenants/{tid}", model);
});

This is successful:
http://localhost:3000/v2/odata/tenants/myTidGuid/Sensors

This results in the exception below:
http://localhost:3000/odata/Sensors

[11:21:06 DBG] Request successfully matched the route with name 'ODataRouteV1' and template 'odata/{*odataPath}'.
[11:21:06 DBG] Executing action v2.SensorsController.Get
[11:21:07 INF] Executed action v2.SensorsController.Get in 337.9326ms
[11:21:07 ERR] Connection id "0HLEMR6DSEVVE", Request id "0HLEMR6DSEVVE:00000003": An unhandled exception was thrown by the application.
System.ArgumentException: The given model does not contain the type 'v1.Sensor'.
Parameter name: elementClrType
at Microsoft.AspNet.OData.ODataQueryContext..ctor(IEdmModel model, Type elementClrType, ODataPath path) in C:\Repos\WebApi\src\Microsoft.AspNet.OData.Shared\ODataQueryContext.cs:line 55
at Microsoft.AspNet.OData.ODataQueryParameterBindingAttribute.ODataQueryParameterBinding.BindModelAsync(ModelBindingContext bindingContext) in C:\Repos\WebApi\src\Microsoft.AspNetCore.OData\ODataQueryParameterBindingAttribute.cs:line 89
at Microsoft.AspNetCore.Mvc.ModelBinding.Binders.BinderTypeModelBinder.d__2.MoveNext()

As shown above, the v2 controller is selected for the v1 route. In the code below (from ODataActionSelector.cs) considerCandidates contains GET actions from both v1 and v2 controllers. However it's not able to disambiguate and just returns FirstOrDefault(), which happens to always be the v2 controller. So the v2 route works, but v1 doesn't.

var matchedCandidates = considerCandidates
                    .Where(c => !c.FilteredParameters.Any() || c.FilteredParameters.All(p => availableKeys.Contains(p.Name.ToLowerInvariant())))
                    .OrderByDescending(c => c.FilteredParameters.Count)
                    .ThenByDescending(c => c.TotalParameterCount);

Assemblies affected

Microsoft.AspNetCore.OData 7.0.0.B4

Reproduce steps

The simplest set of steps to reproduce the issue. If possible, reference a commit that demonstrates the issue.

Expected result

The correct controller should be selected based on the registered route.

Actual result

The correct controller is not selected.

Additional detail

I've tried various forms of attribute routing without success. It does work with MVC routing, however the entities are not returned in an OData JSON envelope, which will break clients.

// v1
[HttpGet("odata/sensors")] // MVC route. This works
public IActionResult Get(ODataQueryOptions<Sensor> query, ODataQuerySettings querySettings)

// Startup.cs
routeBuilder.MapODataServiceRoute("ODataRouteV1", "odata", model);
routeBuilder.EnableDependencyInjection();

Any idea what I'm doing wrong?

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions