The PATCH method is a request method in HTTP for making partial content changes to an existing entity. This would be ideal where one or few properties need to be update against the entity since where we have control and validity, there won’t data alter happen which are not mentioned.

Let jump into detail how we can make it in a better way,

For example, will take the Employee entity and where we have to make PATCH request.

namespace TuneTipsNet.Domain.Entities
{
    public class Employee
    {
        public long ID { get; set; }
        public string Name { get; set; }
        public string Description { get; set; }
        public string Phone { get; set; }
        public string Address { get; set; }
        public string City { get; set; }
        public string Region { get; set; }
        public string PostalCode { get; set; }
        public string Country { get; set; }
        public string EmployeeNo { get; set; }
    }
}

Now I want to update few properties which can be added in the update DTO class

namespace TuneTipsNet.Services.Employee.Models
{
    public class PartialEmployeeUpdateRequest
    {
        public string Name { get; set; }
        public string Description { get; set; }
        public string Address { get; set; }
        public string City { get; set; }
        public string Region { get; set; }
        public string PostalCode { get; set; }
        public string Country { get; set; }
    }
}

Next, include the service class where handle the necessary operations for the content updates. In most cases we need to get the existing entity from the database, do the validation and if it succeeds, we will map the DTO and update the entity.

using System.Threading.Tasks;
using System;

namespace TuneTipsNet.Domain.Entities
{
    public class EmployeeService(IEmployeeRepository employeeRepository) : IEmployeeService
    {
        private readonly IEmployeeRepository _employeeRepository = employeeRepository;
        private const string INVALID_MODEL_MESSAGE = "Invalid model requested to partial update";
        public async Task PartialUpdate(long id, Func<PartialEmployeeUpdateRequest, (bool, string)> validateModel)
        {
            var existingEmployee = await _employeeRepository.GetByIdAsync(id);
            var employeeToPatch = existingEmployee.MapTo<PartialEmployeeUpdateRequest>();

            var (isInvalid, errorMsg) = validateModel(employeeToPatch); //Validate the PATCH request properties match with original entity
            if (isInvalid)
            {
                throw new InvalidOperationException(INVALID_MODEL_MESSAGE, new Exception(errorMsg)); //errorMsg will provide the detail information what wrong with the PATCH model
            }

            var employeeToUpdate = employeeToPatch.MapTo(existingEmployee);
            await _employeeRepository.UpdateAsync(employeeToUpdate);
        }
    }
}

Finally, you have to invoke this method in the controller

using System.Threading.Tasks;
using Microsoft.AspNetCore.JsonPatch;
using Microsoft.AspNetCore.Mvc;

namespace TuneTipsNet.WebApi
{
    [ApiController]
    public class EmployeeController(IEmployeeService employeeService) : Controller
    {
        private readonly IEmployeeService _employeeService = employeeService;

        [HttpPatch("{id:long}")]
        public async Task<IActionResult> Patch(long id, [FromBody] JsonPatchDocument<PartialEmployeeUpdateRequest> request)
        {
            await _employeeService.PartialUpdate(id,
                (PartialEmployeeUpdateRequest patchUpdate) => //The delegate is utilized due to dependencies on ApplyTo and TryValidateModel from AspNetCore.Mvc
                {
                    request.ApplyTo(patchUpdate, ModelState);
                    TryValidateModel(patchUpdate);

                    var isInvalid = !ModelState.IsValid;

                    var errors = isInvalid ? string.Join(" | ", ModelState.SelectMany(x => x.Value.Errors).Select(e => e.ErrorMessage)) : string.Empty;
                    return (isInvalid, errors);
                });
            return NoContent();
        }
    }
}

That’s all! You can proceed this approach for the patch request, and integrate it into the client-side application. I have simplified and improve the code to make efficient and general. There are various pattern we could follow, but I believe this would be the simplest and most convenient. Please share your comments and suggestions. šŸ˜…

Iā€™d love to hear your thoughts!

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *