7

I'm working on ASP.NET Core Webapi project. I would like to implement some kind of Base/Abstract generic controller for all methods that are common for every controller (e.g. CRUD methods) and inherit this Controller in all other controllers. I attach example code below:

public abstract class BaseApiController : Controller { [HttpGet] [Route("")] public virtual IActionResult GetAll() { ... } [HttpGet] [Route("{id}")] public virtual IActionResult GetById(int id) { ... } [HttpPost] [Route("")] public virtual IActionResult Insert(myModel model) { ... } } [Route("api/Student")] public class StudentController : BaseApiController { // Inherited endpoints: // GetAll method is available on api/Student [GET] // GetById method is available on api/Student/{id} [GET] // Insert method is available on api/Student [POST] // // Additional endpoints: // ShowNotes is available on api/Student/{id}/ShowNotes [GET] [HttpGet] [Route("{id}/ShowNotes")] public virtual IActionResult ShowNotes(int id) { ... } } [Route("api/Teacher")] public class TeacherController : BaseApiController { // Inherited endpoints: // GetAll method is available on api/Teacher [GET] // GetById method is available on api/Teacher/{id} [GET] // Insert method is available on api/Teacher [POST] // // Additional endpoints: // ShowHours is available on api/Teacher/{id}/ShowHours [GET] [HttpGet] [Route("{id}/ShowHours")] public virtual IActionResult ShowHours(int id) { ... } } 

I have seen this kind of solution in .NET Framework WebApi, with additional custom RouteProvider, e.g.:

public class WebApiCustomDirectRouteProvider : DefaultDirectRouteProvider { protected override IReadOnlyList<IDirectRouteFactory> GetActionRouteFactories(HttpActionDescriptor actionDescriptor) { return actionDescriptor.GetCustomAttributes<IDirectRouteFactory>(inherit: true); } } 

Every time I try to reach Endpoint in derived controller I got AmbiguousActionException:

Multiple actions matched. The following actions matched route data and had all constraints satisfied: XXX.WebApi.Controllers.CommonAppData.TeacherController.GetById XXX.WebApi.Controllers.CommonAppData.StudentController.GetById 

Is it possible to create such Base controller in .NET Core WebApi? How should I write it to reach Action Methods without declaring it explicitly in derived Controller? How should I configure this kind of solution? Any additional configuration in Startup class?

9
  • Are there any problems with that in .NET Core WebApi? Commented Nov 26, 2018 at 20:06
  • Your code works fine in .NET Core WebAPI without any additional custom RouteProvider. Where is the problem? Commented Nov 26, 2018 at 20:17
  • Yes you can achieve this for CRUD operations and catching exceptions and logging. BaseController is helpful for corss-cutting cuncerns in your application. I would create a generic base controller ... public class BaseController<T> where T will be your model for Teacher and Student types. Commented Nov 26, 2018 at 20:19
  • Also, the routing in .net core is attribute based. There is no route.config file. Commented Nov 26, 2018 at 20:20
  • 1
    I edited the question :) I receive AmbiguousActionException for every derived endpoint. My Base controller is Generic, but for more "clear" question I skipped this part. Commented Nov 26, 2018 at 20:21

1 Answer 1

1

As this topic is a good one so it worth and I explain how I did it. You can create a Generic Controller like this :

[Route("api/[controller]/[action]")] [ApiController] public class BaseController<TEntity> : Controller where TEntity : class, new() { private readonly YourDbContext _db; public BaseController(YourDbContext db) { _db=db; } public virtual async Task<IActionResult> GetAll() { var data= await _db.Set<TEntity>().ToListAsync(); return View(data); } public virtual IActionResult GetById(int id) { var data= await _db.Set<TEntity>().FindAsync(e=>e.Id == id); return View(data); } ..... } 

Then you can use it like this :

public class StudentController : BaseController<Student> { } 

Off course you can write it better with optional parameters something like this:

public virtual async Task<IActionResult> GetAll(Expression<Func<TEntity, bool>>? predicate = null) { var data= await _db.Set<TEntity>().Where(predicate).ToListAsync(); .... } 

Or even override every action in inherited controller as you like and adding necessary parameters to your actions

public override Task<IActionResult> Get(.... 

I hope enjoy it ;) and don't forget to up Vote .

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.