In a base class there is a virtual method who's input signature is params object?[]? id
.
This method leverages the Entity Framework Core Find method which can find an entity
with any number of primary key values.
I would like to override (or hide) this method by reading the route template into a prams parameter
when there are multiple primary keys in the route template.
I can introduce an overload in the derived class and call the base method but
the base method is still visible in the API. This is not desirable because when
a model has a composite primary key an exception is thrown by calling the base method through the API.
Below is a simplified example of the architecture
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
namespace Example.Controller;
public class Model { } // Has any number of keys
[ApiController]
[Route("api/[controller]")]
public class Base<TEntity> : ControllerBase where TEntity : class
{
protected DbContext Context;
[HttpGet("{id}")]
public virtual object? Method(params object?[]? id) =>
Context.Set<TEntity>().Find(id);
}
public class Derived : Base<Model>
{
// Overriding does not work because the signature doesn't match
// 'Derived.Get(int, int)': no suitable method found to override
[HttpGet("{a}/{b}")]
public override object? Method(int a, int b) => base.Method(a, b);
// Overloading in the derived class, passing parameters as arguments
// to the base method works as expected
[HttpGet("{a}/{b}/{c}")]
public object? Method(int a, int b, int c) => base.Method(a, b, c);
}
Questions
"{a}/{b}/{c}"
directly into the base method parameter id
?HttpGet
method with a variable number of parameters?You can look into using so called slug parameters:
Asterisk
*
or double asterisk**
:
- Can be used as a prefix to a route parameter to bind to the rest of the URI.
- Are called a catch-all parameters. For example,
blog/{**slug}
: Matches any URI that starts withblog/
and has any value following it. The value followingblog/
is assigned to the slug route value.
So you can treat your id as slug parameter and split the value by the /
, though it will treat everything as string
and will not verify that correct amount of parameters is passed, you will need to do that manually.
Possibly something could be achieved with custom model binding, but personally I would not be introducing such class in the first place, or trying to reuse it via inheritance for composite PK cases (so just do not inherit the Base<TEntity>
or introduce another level of inheritance were base one will just contain the methods as protected helper ones). I would argue that DRY principle should not conflict with KISS one and overgeneralization leading to overcomplication is not a good thing.
Read more: