Skip to content

Microsoft.AspNetCore.OData.Versioning.ApiExplorer 5.0.0 + SwaggerGen does not display ODataController actions #698

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
engenb opened this issue Nov 13, 2020 · 4 comments

Comments

@engenb
Copy link

engenb commented Nov 13, 2020

ODataController actions are discovered and produced in swagger json with Microsoft.AspNetCore.OData.Versioning.ApiExplorer 4.1.1 but not with 5.0.0

this is a placeholder issue for now as a create a sample project to demonstrate this.

@engenb
Copy link
Author

engenb commented Nov 13, 2020

repo may be found here https://github.com/engenb/AspNetCore.OData.Issues

I created two sample proejcts with relevant framework components configured as in my app:
project Issue698_4_1_1_Working illustrates working example
project Issue698_5_0_0_NotWorking illustrates non-working example

each project has the same code - I copy/pasted the project contents from the working to non-working project.

steps to reproduce:

some notes from my investigation:
while I said I copy/pasted the code, there were a couple updates required due to breaking changes in 5.0.0

  • I'm calling MapVersionedODataRoutes with 4.1.1 and this was removed in 5.0.0
  • I must admit I'm not clear on the difference between MapVersionedODataRoutes and MapVersionedODataRoute and I suspect this change may be the cause of my missing ODataControllers
  • I have experimented with ODataApiExplorerOptions.QueryOptions.Controller<T>() with no luck

if there's any other information I can provide, I'm happy to help.

thanks, @commonsensesoftware

@commonsensesoftware
Copy link
Collaborator

Thanks for the repro[s]; they will be helpful.

I'll start with "there's definitely a combination things all work." I wouldn't ship otherwise. The rules, combinations, and documentation for OData is ... how do I say this tactfully ... not so good. This is more true than ever with the OData support for Endpoint Routing (which is really a dog and pony show under the hood). It has very few sample configurations beyond the most basic of setups. Even the littlest of missteps and things can go sideways. My real bane in supporting OData is the number of significant changes it undergoes. It's often really hard for me to tell whether it's a bug/regression in OData, an expected change in behavior, or a bug in API Versioning - which occasionally does happen (90% of the issues are OData-related these days).

I'm an imperfect being and I rewrite imperfect code. I won't be surprised if I've made a mistake somewhere, but it will take some time to investigate (darn that "j-o-b" thing 😉). My top priority is unblocking you. There should some combination that works E2E. This should be evident by the sample project. If you still don't have something working, then 4.1.1 is your best fall back.

In addition to Endpoint Routing support, 5.0.0 introduced or changed functionality in accordance to what people have been asking for:

  • MapVersionedODataRoutes was plural because it literally created multiple OData routes; one per API version. This was unnecessarily complex and required a ton of reimplementing and hackery of the default OData setup. I was able to refactor things down to just MapVersionedODataRoute, which has a lot more parity with MapODataServiceRoute. API Versioning introduces a new IEdmModelSelector service that can resolve the appropriate IEdmModel by incoming request or explicitly by version (ex: API Explorer). The IEdmModel in the request container is, therefore, now transient instead of singleton because it depends on the selector.
  • People have been trying to treat the route prefix as though it is a nested application. This is strange to me, but people want it. After much deliberation, there was no magical way to solve this or match up models to a prefix without a developer specifying it. To address it, the routePrefix is added to IModelConfiguration.Apply. You decide whether a model matches a particular API version and route prefix combination. This results in the filtering and division that you want. To avoid you having to specify the prefix over and over again, VersionedODataModelBuilder.GetEdmModels has an overload that accepts the route prefix and it flows through all models. This also enables simplifying registration because you have to specify the route prefix once and it flows through. For example: endpoints.MapVersionedODataRoute("odata", "api", modelBuilder);
    • This simplified some other things too because with MapVersionedODataRoutes each route had to have a unique name and this was achieved with routePrefix + "-" + apiVersion.ToString(). Now that there is a single route, this munging is removed.

I hope that helps clarify things and at least get you moving in the right direction.

@anranruye
Copy link

anranruye commented Nov 22, 2020

@engenb
Well, this is a problem about 'names' in Microsoft.AspNetCore.OData.Versioning.ApiExplorer 5.0.0 and Microsoft.AspNetCore.OData.Versioning 5.0.0.

Do the following to make your OData controllers and actions work:

  1. Change controller name to match the name configured by ODataModelBuilder(case-sensitive)
    //public class FooController : ODataController
    public class foosController : ODataController

or

    [ControllerName("foos")]
    public class FooController : ODataController

or

    //var foo = builder.EntitySet<Foo>("foos").EntityType;
    var foo = builder.EntitySet<Foo>("Foo").EntityType;
  1. Change action name to match the http method name(case-sensitive)
    //public IActionResult GetFoos() => Ok(Foos);
    public IActionResult Get() => Ok(Foos);

or

    [ActionName("Get")]
    public IActionResult GetFoos() => Ok(Foos);

I mentioned this problem in issues:
#689
#692

In conclusion, odata attribute routing is not respected in Microsoft.AspNetCore.OData.Versioning.ApiExplorer 5.0.0, you must
use same controller names, action names and parameter name as they are in convention routing even though you use [ODataRoute] attribute(changing route parameter name 'key' to 'id' can work, but only in the lowest api version found in the project). Another problem is that names in plural form, such as 'GetOrders', can not work, only 'Get' or 'GetOrder' can work.

(ps: 'GetOrder' can not work in convention routing, only 'Get' can work in convention routing. It seems that 'GetXXX' is only used for getting navigation properties to avoid confliction.)

Removing api versioning from your project can make odata attribute routing work correctly, however , Microsoft.AspNetCore.OData has its own bug related to names:
OData/WebApi#2363

@commonsensesoftware Is there any progress with issue #689 and #692 ?

@engenb
Copy link
Author

engenb commented Nov 27, 2020

That's the key - I just needed to make my entity set names pascal-cased and that resolved the issue. Thanks @anranruye !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants