odata query options (filter/skip/top/orderby) not included in api explorer
Is there an existing issue for this?
- [x] I have searched the existing issues
Describe the bug
I've tried to configure query options globally, via controller action attribute, the new ODataApiExplorerOptions approach and everything else I can think of. regardless, the only two odata query options that I've ever seen included in the api explorer are select and expand - I'm validating this via swagger.
Expected Behavior
configured query options should be included in openapi / swagger doc
Steps To Reproduce
here is a sample repo that demonstrates the issue
Exceptions (if any)
No response
.NET Version
8.0
Anything else?
I've also noticed the only api doc in swagger is v1 - v2 is excluded for some reason. I have not seen this issue before so not sure if it's related or not.
I'm working on migrating from Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer
The ODataApiExplorerOptions have been there a lot time, even in the old Microsoft.AspNetCore.OData.Versioning.ApiExplorer.
There's a lot going on this in this project, but this could be the expected behavior. filter, skip, top, and orderby are all nonsensical for a scalars type (e.g. single entity or complex type). This would be the for any API that returns one result by key. Obviously, entity sets and functions should not be so limited. There's not much sense in including all of the query option noise for options that are either unsupported or will have no effect.
A few other notes:
- You don't need
[ApiController]since you're using OData; in fact, that might cause undesirable side effects - You're organizing by namespace, you could:
a. Drop the
V#suffix so that you don't needControllerNameb. Use theVersionByNamespaceConvention, which would negate the need for[ApiVersion]
I'll have to pull this down and go through it more, but that may be a few quick hints to try a few other things.
yeah, sorry if there's a bit of unrelated noise in the project. I wanted to set it up following a trimmed-down version of my typical microservice project template, so there might still be some concepts in there that are irrelevant. let me know if it's too noisy and I'll see if I can trim more of the fat.
some relevant concepts:
- this is a versioned odata api (of course)
- I've found it helpful to have a project-per-api-version, so adding new versions and removing old versions is as much "plug and play" as possible.
- ^ means controller-per-version is likely a hard constraint
- ideally, odata model configuration is also per-version. the new
IModelConfigurationlooks perfect for this
- for design/architecture reasons, I can't directly return my entity through the api. I'm utilizing AutoMapper.Extensions.OData to map the ODataQueryOptions from my api contracts to my entities.
- I've found a db view-per-version can simplify this
- also establishes a versioned "contract" for my downstream data warehouse team, currently extracting data from my database via Azure Data Factory.
- I don't know if this is creating OData and ApiExplorer convention-based challenges because my controller actions all return
IActionResultand neverIQueryable<TEntity> - in order to use this project's predecessor, I ended up implementing my own
AttributeRoutingConventionandIApiDescriptionProviderthat just always added all the query parameters if the action had aODataQueryOptiosn<>parameter. pretty brute force and not very elegant, but I decided better to have irrelevant options in swagger than not have needed options (I am not suggesting this as a solution for this framework)- ^ to adopt this project, I'd like to avoid having do that again
- I'm completely open and ready to accept the goals of my project are just not compatible with the approach taken by this framework
This is actually insightful and really useful information.
If you're using per-controller or per-project API versioning, the VersionByNamespaceConvention can really remove a lot of touch points. For example:
First you setup:
builder.Services
.AddApiVersioning()
.AddMvc(options => options.Conventions.Add(new VersionByNamespaceConvention()));
If all of your controllers are APIs, you can apply the conventions for all of them at once (and with no inheritance!):
// all controllers in here are API controllers
[assembly: ApiController]
then you can have:
// ↓ the API version derives from this
namespace Example.Api.V1.Controllers
{
// [ApiVersion(1.0)] ← no longer required because it comes from the namespace
// [ApiController] ← not required if you apply it to the entire assembly
[Route("[controller]")]
public class ExampleController : ControllerBase
{
[HttpGet]
public IActionResult Get() => Ok();
}
}
The wiki has more documentation the version by namespace convention
You are correct that the EDM is always per API version. It would be a soupy mess if it weren't that way.
Based on your description, it seems your services fall into the category of some OData. This typically entails using the query capabilities of OData, but not necessarily adhering to compliance with the protocol. This is allowed and supported. There's a few things that are slightly different from full OData because the intrinsic conventions aren't there. You may have a better time and more flexibility using that approach. You essentially just have regular 'ol controllers, that you use OData queries (and options) on, but nothing else. The IModelConfiguration only comes into play for the API Explorer, which is what feeds OpenAPI. The EDM is ephemerally built for exploration and is then discarded because that's the only time it's needed.
The OpenAPI with Some OData example will provide a working example with support for the API Explorer, OpenAPI, and the Swagger UI.
Happy to answer more questions.
Following up. Did this actually solve your problem and unblock you or is there more to investigate?