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.
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.