Search code examples
c#asp.net-core-mvcentity-framework-coreapi-designasp.net-core-webapi

One Web API Controller to control all models


I am creating a MVC Core 1.0.1 project that will include both API and Web. So i have created my models and now i want to create CRUD operations within a single controller instead of scaffolding each model. I have created an ApiController that looks like this

[Consumes("application/json")]
[Produces("application/json")]
[Route("/api/{resource}")]
public class ApiController : Controller
{
    private readonly MasterContext _context;

    public ApiController(MasterContext context)
    {
        _context = context;
    }

    [HttpPost]
    public IActionResult Create(string resource, [FromBody] object body)
    {
        //_context.Add();
        return Ok("ok api Create");

    }

    [HttpGet("{id?}")]
    public IActionResult Read(string resource, int? id)
    {
        return Ok("ok api get Read");
    }
    [HttpPatch("{id}")]
    public IActionResult Update(string resource, [FromBody] object body)
    {
        //_context.Update();
        return Ok("ok api Update");
    }
    [HttpDelete("{id}")]
    public IActionResult Delete(string resource, [FromBody] object body)
    {
        return Ok("ok api Delete");
    }
}

This way i have a method for each HTTP method that i need (Post, Get, Patch, Delete), in the resource i have the model as a string and in body i have the body of the request as an object. Before working with entity framework to do the requested operation, i have to find the model according to resource and convert the object body to that class.

Any suggestions how to do that? A colleague has done this using Python, can this be done using c# and what downside will the result have? For example, i presume model validation will be hard to accomplish.


Solution

  • Yes, it's possible. Let's say we have this DbContext:

    public partial class FooContext : DbContext
    {
        //has "MyAssembly.Blog" type
        public virtual DbSet<Blog> Blog { get; set; }
    }        
    

    To save a new entity in database we should find Blog type first. Having the type, it's easy to deserialize object and save it:

    //you called POST /blog
    string resource = "blog";
    string body = "{...}";
    
    var context = new FooContext();
    
    IEntityType entityType = context.Model
        .GetEntityTypes()
        .First(x => x.Name.EndsWith($".{resource}", StringComparison.OrdinalIgnoreCase));
    
    //This type should be "MyAssembly.Blog" - exact entity CLR type.
    //Another option to get this CLR type is assembly scan.
    Type type = entityType.ClrType;
    
    //having type, it is possible to create instance
    object entity = JsonConvert.DeserializeObject("body", type);
    //var entity = Activator.CreateInstance(type);
    
    context.Entry(entity).State = EntityState.Added;
    context.SaveChanges();
    

    To read an entity by ID from database, use non-generic DbContext.Find

    var entityFromDb = context.Find(type, id);
    

    P.S. I think a generic ApiController is generally bad idea. It's bulky and it brings a huge unnecessary complexity but small benefits.