This is a .NET 6.0 web application that's being upgraded from .NET Core 3.1, and as part of it, OData is being upgraded from 7.5.8 to 8.0.11.
With OData 7.5.8, I was able to have two different controllers on two different routes:
[Route("api/[controller]")]
[ApiController]
public class MessagesController : ControllerBase
{
[HttpGet("{id}")]
public async Task<IActionResult> Get(Guid id)
{ ... }
}
public class MessagesController : ODataController
{
[HttpGet]
[MessagesEnableQuery(MaxExpansionDepth = 1)] // this is a custom EnableQuery attribute
public IQueryable<Message> Get(ODataQueryOptions options)
{ ... }
}
// routing setup in Startup.cs
app.UseEndpoints(endpoints =>
{
...
endpoints.Select().Expand().Filter().OrderBy().Count().MaxTop(null);
endpoints.EnableDependencyInjection();
endpoints.MapODataRoute("ODataRoute", "odata", GetEdmModel());
}
And these could be reached by two separate routes:
/api/Messages/GUID
/odata/Messages?$filter=...
However, with OData 8.0.11, it doesn't seem like I can do this anymore. If I try to hit the /odata/Messages
route, I get a 404 response. For 8.0.11, I added [ODataRouteComponent("odata")]
to the controller class, and the configuration is now:
services.AddMvc(...)
.AddOData(options =>
{
options.EnableQueryFeatures();
options.AddRouteComponents("odata", GetEdmModel());
});
And my IEdmModel
is built like so:
builder.EntitySet<Message>(nameof(Message) + "s");
From what I can tell, when I have two controllers of the same name, ASP.NET is sending the requests to the /api
route even when the path starts with /odata
. I was able to figure this out by putting break points in each controller's constructor and when I made a request for /odata/Messages
the application paused in the API controller instead.
So, how do I get it so that I can use separate routes for separate controllers again?
Thanks in advance.
I found a relevant Github issue at https://github.com/OData/AspNetCoreOData/issues/238, and from that I was able to use only attribute routing, which did allow me to use both controllers, but with a significant drawback: the OData endpoint would only return the query result and no longer contained any of the query metadata, meaning that if I added $count=true
to the query, it wouldn't actually include the item count. So that's not helpful.
In the end, I decided to merge the two controllers together.
[Route("api/[controller]")]
[ApiController]
[ODataRouteComponent("odata")]
public class MessagesController : ControllerBase
{
[HttpGet]
[MessagesEnableQuery(MaxExpansionDepth = 1, MaxTop = 100)]
public async Task<IQueryable<Message>> Get(ODataQueryOptions options)
{
return await _service.FindAsync();
}
}
Not the best solution, but it works. The only problem is that the endpoint is reachable by both /api/Messages
and /odata/Messages
. Oh, and I had to add [ODataIgnored]
to all the other API methods, but at this point I've spent enough time on this, so this is good enough.