How to use Microsoft.AspNetCore.JsonPatch with Model and EF entity

41 views Asked by At

I have the following method in my ASP.NET Core Web API. It's using Microsoft.AspNetCore.JsonPatch for patching:

[HttpPatch]
public IActionResult PatchCustomer([FromBody] JsonPatchDocument<CustomerModel> patchDoc)
{
    if (patchDoc != null)
    {
        var customerEntity = _dbContext.Customers.Where(x=>x.CustomerID == id);

        // the ApplyTo() method below throws compilation error because of type mismatch
        patchDoc.ApplyTo(customerEntity, ModelState);

        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }
          
        return Ok();
    }
    else
    {
        return BadRequest(ModelState);
    }
}

I could fix the error by changing the method parameter to type JsonPatchDocument<Customer> where Customer in an entity. However, I don't want to expose entity to the API method. The CustomerModel type has only certain properties that caller can update so it make sense to use CustomerModel as parameter to minimize the risk of caller accidentally updating some other property.

How to update Customer entity based on JsonPatchDocument here?

1

There are 1 answers

0
Yuning Duan On

You can try to use Automapper to map CustomerModel to the Customer entity, and then apply JsonPatchDocument.Or use Linq to query to map the attributes of the Customer entity to the attributes of the CustomerModel model object. Then apply JsonPatchDocument to CustomerModel, and update the modified attribute value to the physical object. You can refer to the following examples:

My Customer:

public class Customer
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
    public string Phone { get; set; }
}

My CusterModel:

public class CustomerModel
{
     public string Name { get; set; }
     public string Email { get; set; }

     public string Phone {  get; set; }
}

My method (useAutomapper):

    [HttpPatch]
    public IActionResult PatchCustomer([FromBody] JsonPatchDocument<CustomerModel> patchDoc, int id)
    {
        if (patchDoc != null)
        {
            var customerEntity = _dbContext.Customers
                .FirstOrDefault(x => x.Id == id);

            if (customerEntity == null)
            {
                return NotFound();
            }

            var customerModel = _mapper.Map<CustomerModel>(customerEntity); 

            patchDoc.ApplyTo(customerModel, ModelState);

            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }

            _mapper.Map(customerModel, customerEntity); 

            _dbContext.SaveChanges();

            return Ok();
        }
        else
        {
            return BadRequest(ModelState);
        }
    }

The entity that needs to be mapped:

public class AutoMapperProfile : Profile
{
    public AutoMapperProfile()
    {
        CreateMap<Customer, CustomerModel>(); 
        CreateMap<CustomerModel, Customer>(); 
    }
}

In program.cs:

builder.Services.AddAutoMapper(typeof(AutoMapperProfile));

Use Linq query(It should be noted that this method requires the attribute name and the type completely matching):

[HttpPatch]
public IActionResult PatchCustomer([FromBody] JsonPatchDocument<CustomerModel> patchDoc, int id)
{
     if (patchDoc != null)
     {
         var customerEntity = _dbContext.Customers
             .FirstOrDefault(x => x.Id == id);

         if (customerEntity == null)
         {
             return NotFound();
         }

         var customerModel = new CustomerModel
         {
             Name = customerEntity.Name,
             Email = customerEntity.Email,
             Phone =customerEntity.Phone,
         };

         patchDoc.ApplyTo(customerModel, ModelState);

         if (!ModelState.IsValid)
         {
             return BadRequest(ModelState);
         }
         customerEntity.Name = customerModel.Name;
         customerEntity.Email = customerModel.Email;
         customerEntity.Phone = customerModel.Phone;
         _dbContext.SaveChanges();

         return Ok();
     }
     else
     {
         return BadRequest(ModelState);
     }
}