0

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...

2
  • Prefer a more composition based approach over inheritance. That will also save you problems like this one - don't understand what do you mean ((( Also, your error message mentions CreditController, which isn't even included in your example - sorry, fixed - it was the result of "code simplification" for the question Commented Mar 19, 2023 at 23:33
  • Ok, I think I'v got it.. Not to use inherited controllers, but the only one: [ApiController] [Route("[controller]")] public class DocumentsController : ControllerBase { [HttpGet] public async <IEnumerable<Document>> Get(string? FromDate, string? ToDate) { ... } [HttpGet] [Route("invoices")] public async <IEnumerable<Invoice>> GetInvoices(string? FromDate, string? ToDate) { ... } } Thanks, man! So easy, so good! Commented Mar 19, 2023 at 23:59

1 Answer 1

2

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
    }
}
Sign up to request clarification or add additional context in comments.

1 Comment

Reddog, thanks for your answer! The idea was that some common functionality - like deleting and some more - is in the base DocumentsController, and the specific functionality - like inserting, listing and some others - in inherited ones. Using two independent controllers, inherited from base one, I need to implement common functionality in both of them Now I used one controller with additional route postfix, and its intermediate decision

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.