AmbiguousMatchException when truing override parent ASP.NET controller

89 Views Asked by At

almighty All!

Second day passed... Need Help!

I have two controllers: base DocumentController and derived InvoiceController in ASP.NET C# WebAPI application**:**

[ApiController]
[Route("Documents")]
public class DocumentController : ControllerBase
{
    [HttpGet]
    public virtual IEnumerable<Document> Get()
    {
        ... //some implementation
    }
}
[ApiController]
[Route("Documents/Invoices")]
public class InvoiceController : DocumentController
{
    [HttpGet]
    public new IEnumerable<Invoice> Get()
    {
        ... //some implementation
    }
}

I need first of them to show all documents, and the second one to show invoices only

Classes Document and Invoice are also base and inherited one

public class Document { ... }
public class Invoice: Document { ... }

when I use override keyword in derived controller, like this:

public override IEnumerable<Document> Get()

everything works fine, except that I need a list on Invoices, not Documents

But when I use new keyword, like this:

public new IEnumerable<Invoice> Get()

I get an error:

AmbiguousMatchException: The request matched multiple endpoints. Matches: InvoiceController.Get DocumentsController.Get

It means, that I have two methods = one from parent "DocumentController", and another one from child "InvoiceController"

It can be solved by adding some additional route attribute to child controller - for example "aaa" - in this case I have two GET endpoints in child controller: Documents/Invoices - with a list of Documents (inherited from the base class) and Documents/Invoices/aaa - with a list of Invoices (from the new implementation in derived class)

but that is ugly

tried use different route attributes and so on...

1

There are 1 best solutions below

1
Reddog On

You shouldn't need to inherit the InvoiceController controller from DocumentController to represent the inheritance of your models / DTOs. So I would suggest to remove that for sure. If you wanted to leverage some existing functionality from the DocumentController you could dependency inject in an instance into your InvoiceController and apply whatever filtering logic you want there... BUT even better would be to use a new type DocumentService that's dependency injected into both controllers and contains all the common logic for retrieving documents.

To set up the matching routes of the /documents/ -> DocumentController and /documents/invoices/ -> InvoiceController you might need to be more specific on the controller actions to specifically put their route details to ensure there is no ambiguity.

[ApiController]
public class DocumentController : ControllerBase
{
    [HttpGet("/documents")]
    public virtual IEnumerable<Document> Get()
    {
        ... //some implementation
    }
}

[ApiController]
public class InvoiceController : ControllerBase
{
    [HttpGet("/documents/invoices")]
    public IEnumerable<Invoice> Get()
    {
        ... //some implementation
    }
}