dotnet / aspnet-api-versioning

Provides a set of libraries which add service API versioning to ASP.NET Web API, OData with ASP.NET Web API, and ASP.NET Core.
MIT License
3.06k stars 703 forks source link

v6 preview: OData addroutecomponents with the current version #831

Closed spaasis closed 2 years ago

spaasis commented 2 years ago

Hi! I finally had a slot to start fiddling with the new release. Looks good so far!

My app is using a routing convention like "api/v{version:apiVersion}/odata" but this doesn't seem to work straight away in AddRouteComponents since I'm getting

System.ArgumentNullException: Value cannot be null. (Parameter 'type')
   at Asp.Versioning.OData.TypeExtensions.SubstituteIfNecessary(Type type, TypeSubstitutionContext context)
   at Asp.Versioning.ApiExplorer.ODataApiDescriptionProvider.UpdateModelTypes(ApiDescription description, IODataRoutingMetadata metadata)
   at Asp.Versioning.ApiExplorer.ODataApiDescriptionProvider.OnProvidersExecuted(ApiDescriptionProviderContext context)

I debugged enough to find out that the library hits the version parameter it cannot parse the type and leaves it as null, leading to the exception.

Is such a naming scheme possible with the new version? We are housing both OData and "normal" routes in the app and would like to have these separate: /api/v1/odata/ODataEntities /api/v1/NormalEntities

commonsensesoftware commented 2 years ago

Yes, the two can live together. Have you looked at the new Advanced Example, which shows the two mixed? As always, if you have a repro to share, I can help look into it.

dxynnez commented 2 years ago

I am actually trying this out with odata 8 too but couldn't find an example to bind different edm models for different versions.. Actually in all the examples I don't see any of them adding an edm model for a route. Is it not needed? How does that work?

spaasis commented 2 years ago

@commonsensesoftware After I fixed my package references by the other issue, this magically fixed itself too. I can't believe endpoint routing actually works ❤

@dxynnez I believe there is some magic involved but as long as you define your IModelConfigurations they get automatically added and configured. See the advanced example above for a minimal setup

commonsensesoftware commented 2 years ago

@dxynnez EDMs are created by implementing IModelConfiguration instances. All of the OData examples should have a /Configuration folder with one or more implemented configurations. If you don't want to use that approach, you can also define a ODataApiVersioningOptions.ModelBuilder.DefaultModelConfiguration callback function. These two approaches are mutually inclusive.

The DI extensions know to look for IModelConfiguration automatically, so you don't need to do any additional registration; they are discovered automatically. A model configuration is required because it will be called for each API version and route prefix combination. The configuration decides what, if anything, is configured for that particular EDM. This is much more complex than the default OData configuration. Since a sequence of IModelConfiguration is always processed, you don't even have to call API Versioning variant of AddRouteComponents because this will implicitly be done with the prefix of / as long as the configuration creates a non-empty EDM.

Do not attempt to call the native OData AddRouteComponents; it will be ignored. Despite some talks with the OData team in the early design phase of 8.0, there was no concessions made to make the setup/configuration of OData more flexible for API Versioning. I tried to make the API Versioning variant feel as natural as the built-in OData method, even though you have to configure it a different way.

I hope that help clear it up.

dxynnez commented 2 years ago

@dxynnez EDMs are created by implementing IModelConfiguration instances. All of the OData examples should have a /Configuration folder with one or more implemented configurations. If you don't want to use that approach, you can also define a ODataApiVersioningOptions.ModelBuilder.DefaultModelConfiguration callback function. These two approaches are mutually inclusive.

The DI extensions know to look for IModelConfiguration automatically, so you don't need to do any additional registration; they are discovered automatically. A model configuration is required because it will be called for each API version and route prefix combination. The configuration decides what, if anything, is configured for that particular EDM. This is much more complex than the default OData configuration. Since a sequence of IModelConfiguration is always processed, you don't even have to call API Versioning variant of AddRouteComponents because this will implicitly be done with the prefix of / as long as the configuration creates a non-empty EDM.

Do not attempt to call the native OData AddRouteComponents; it will be ignored. Despite some talks with the OData team in the early design phase of 8.0, there was no concessions made to make the setup/configuration of OData more flexible for API Versioning. I tried to make the API Versioning variant feel as natural as the built-in OData method, even though you have to configure it a different way.

I hope that help clear it up.

Thanks so much for the explanation! I like the auto-discovered magic more than the AddRouteComponents of configuring the edm model. It's much more compact / clean than the native way IMO as we finally can separate the verbose configuration out of the app startup code.

Just want to confirm one thing - can we expect that whatever supported in the native AddRouteComponents API, should be supported by the IModelConfiguration? Or at least that is the ultimate goal? - Scratch that - I just realized that it's using the native ODataModelBuilder so we are all good. Thanks for such a neat improvement @commonsensesoftware !