-
Notifications
You must be signed in to change notification settings - Fork 699
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
.NET 6.0 - AmbiguousMatchException - Correctly Implemented (apparently) #801
Comments
From your description, it sounds like the issue is inheritance. API Versions are not inherited out-of-the-box. This is by design. You cannot uninherit an API version, but it is likely you'll sunset an API at some point. The main reason this is not supported, however, is that if There are ways to use inheritance, but it needs to be done with care. Personally, I think inheritance is the wrong approach for APIs. HTTP is the API and it intrinsically doesn't support inheritance. However, there is value using inheritance for common behaviors that you can't otherwise achieve (say through extension methods). The logic in controllers should purely be about what's happening over the wire. Moving other logic to separate components will reduce the level of duplication. A key driving factor is your versioning policy. Most services define a policy such as N-2 versions. Maintaining 10 different versions of a service can get messy. Maintaining 1-3, not so much. Copying and pasting vCurrent into a new vNext folder/namespace can be an easy and effective way to light up or shut down versions. You mentioned new controllers go into new namespaces. Since you're organizing your code that way, you might find it easier to let the containing namespace itself define the API version rather than use attributes. This can be achieved by adding the built-in convention that versions by namespace: Some additional side notes based on the information you shared:
|
Thank you for your timely response. I appreciate the time spent writing it. Ill address your message as subheadings, as it just makes it easier. Context Inheritence Versioning Resolution Namespace Versioning |
No problem. I provide advice and guidance, but the decisions are up to you. 😉 ContextThis is fine and how it is meant to work. In this case, you do want the Inheritance
Not really. It was never the intent to force developers to do more work, but inheritance is an implementation detail. From experience, working with many teams over the years, I've come the conclusion that inheritance in this context causes more problems than it solves. Honestly, a controller action shouldn't have more 10 lines of code, if even that, IMHO. Saving those few lines of code doesn't buy much. In addition, you have to consider the cascading effects, which might require you to undo inheritance as an implementation detail. Imagine a scenario where a bug is detected in Let me illustrate an example that may shed some additional light. Consider that you have: [assembly: ApiController] // indicate that all controllers in the assembly are API controllers =D
[ApiVersion("1.0")]
[Route("api/[controller]")]
public class LocationsController : ControllerBase
{
// GET api/locations (back-compat, no ver option).
// GET api/locations?api-version=1.0
[HttpGet]
public virtual IActionResult Get(ApiVersion version) => Ok(new {ver = version.ToString()});
} If the [ApiVersion("2.0")]
public class Locations2Controller : LocationsController
{
// GET api/locations?api-version=2.0
public override IActionResult Get(ApiVersion version) => Ok(new {version.Major, version.Minor});
} What you wanted was:
What you actually have is:
This is how you end up with There are several ways to get out of this situation:
Version ResolutionIt's fine if you want to version by URL segment. I don't recommend it and it's not what I would do (because it's not RESTful), but it's a popular method nevertheless. If this is your strategy, then you should do the following:
|
InheritanceSo, the reason for looking at inheritance is this; We have 65 entities that we perform CRUD operations on through a Vue.js client. The original (now V1) API implementation was thicc controllers with all of the business logic in the actions and no SOLID principles or ability to test or scale etc (The business was brought in to redesign). The controllers were pretty much identical, save for about 10 that additional functionality in them. So in our case the V2 controller, will NEVER inherit from the V1 because these will be fully deprecated and removed once V2 works. The V2 BaseApiController looks like this for a reason.
I have use the generic base API controller pattern before and it has worked, it just seems to be the API versioning that isn't! I have implemented the |
I am going to try the route of implementing Inheritance aware Attributes and see if this solves my issue. |
To confirm, I have resolved this issue by implementing the following. Thank you @commonsensesoftware .
|
Awesome! Glad you got it working. For completeness, this also should have been solvable with inheritance like this: // define base functionality
[ApiController]
[Route("api/v{version:apiVersion}/[controller]")]
public class BaseApiController<T, TId> : ControllerBase
where T : class, IHasId<TId>
where TId : struct
{
protected BaseApiController() { }
// implementation omitted for brevity
}
// apply base functionality to v2 via inheritance
[ApiVersion("2.0")]
public class V2Controller<T, TId> : BaseApiController<T, TId>
where T : class, IHasId<TId>
where TId : struct
{
// the implementation is completely inherited and implicitly maps all actions to v2
} |
Hey!
So I am working on a .NET 6 WebAPI project where I am introducing API versioning. As an example, I have separated out the new (v2) controllers into a new namespace and they inherit from our new base API class, the original controllers (v1) inherit from the old base class.
V1 Base Class
V2 Base Class
I am registering in the startup.cs:
Each method / action / endpoint has either:
[MapToApiVersion("1.0")] or [MapToApiVersion("2.0")]
But I am still getting:
I am using the following packages:
The text was updated successfully, but these errors were encountered: