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.