4

I'm working on a simple WebApi project and I've defined an abstract CRUD controller that allows me to share the same logic for GET/POST/PUT/DELETE requests.

However, I still obviously need to create concrete controller classes that implement the abstract base class in order for the controllers to be automatically added to the Services Collection (in Program.cs a call to builder.Services.AddControllers() does that).

For example, in order to create a controller for the "Article" object defined in my model, I have to add the following class in my Controllers namespace

public class ArticleController : CrudController<Article, ArticleController>
{
    public ArticleController(ILogger<ArticleController> logger, DbContext ctx) :
        base(logger, ctx) { }
}

Is it possibile to define a custom controller factory that allows me to instantiate a controller for each class of my model that has a specific attribute?

For example, given the following model class

[GenerateController(typeof(Article)]
public class Article
{
    public int Id { get; set; }
    public string Code { get; set; }
    public string Description { get; set; }
    public string DescriptionShort { get; set; }
    public UnitOfMeasure UnitOfMeasure1 { get; set; }
    
    // Other properties here
}

and the following CrudController definition

[Route("api/[area]/[controller]")]
[ApiController]
[Area("Registry")]
public abstract class CrudController<T, B> : ODataController where T : class
{

    private ILogger<B> _logger;
    private DbContext _ctx;

    public CrudController(ILogger<B> logger, DbContext ctx)
    {
        _logger = logger;
        _ctx = ctx;
    }

    [HttpGet("{code}")]
    public ActionResult<T> Get(string code)
    {
        var lambda = GetRuntimeCondition(code);
        var res = _ctx.Set<T>().SingleOrDefault(lambda);
        return res!= null ? Ok(res) : NotFound(new Error($"Element with code {code} not found"));
    }

    [EnableQuery]
    [HttpGet]
    public ActionResult<IEnumerable<T>> GetAll()
    {
        var res = _ctx.Set<T>().AsQueryable();
        return Ok(res);
    }

    [HttpPost]
    public ActionResult<T> Post(T a)
    {
        _ctx.Set<T>().Add(a);
        _ctx.SaveChanges();

        return Ok(a);
    }

    [HttpPut]
    public ActionResult<T> Put(T a)
    {
        _ctx.Set<T>().Update(a);
        _ctx.SaveChanges();

        return Ok(a);
    }

    [HttpDelete("{code}")]
    public ActionResult<T> Delete(string code)
    {
        var lambda = GetRuntimeCondition(code);

        var res = _ctx.Set<T>().SingleOrDefault(lambda);

        if (res is null) return NotFound(new Error($"Element with code {code} not found"));

        _ctx.Set<T>().Remove(res);
        _ctx.SaveChanges();

        return Ok(res);
    }

    private Expression<Func<T, bool>> GetRuntimeCondition(string code)
    {
        var item = Expression.Parameter(typeof(T), "o");
        var prop = Expression.Property(item, "Code");
        var val = Expression.Constant(code);
        var condition = Expression.Equal(prop, val);
        return Expression.Lambda<Func<T, bool>>(condition, item);
    }
}

I would like WebApi to automatically create and inject an ArticleController (that extends the CRUD one) without having to defining one by myself.

canta2899
  • 394
  • 1
  • 7

0 Answers0