Assuming you have the following object:
public class Person {
public string Name { get; set; }
public string Surname { get; set; }
public int Age { get; set; }
}
and got a PATCH request as below:
[{"replace": "/name", "value": "tugberk"}]
and assuming we have a json-patch formatter. Wouldn't you get the following result?
public class PeopleController : ApiController {
//new Person { Name = "tugberk", Surname = null, Age = 0 }
public Person PatchPerson(Person person) {
//...
}
}
-
Create an interface for
IPatchCommand
that adheres to JSON Patch specification -
Create a media type formatter than can translate the request body into an
IPatchCommand
. TheIPatchCommand
contains a list ofPatchOperation
's to apply to an object (as per the spec) -
In the controller, create a new instance of a patch and map the current values from the domain object (we can use AutoMapper for this).
-
Pass the patch object and patch command to a patch service that understands how to translate a
PatchOperation
into an Action that is applied to the patch object. Since the patch objects should just have simple public properties, this shouldn't be too difficult. -
Finally, pass the "updated" patch object into the domain layer (either directly or perhaps by a patching handler). This makes domain operations explicit - it would just be too damn hard to automatically update every domain object directly.
public class PatchOperation { public OperationType Type { get; set; } public string Path { get; set; } public string Value { get; set; } public enum OperationType { Test, Remove, Add, Replace, Move, Copy } } public interface IPatchCommand { public IEnumerable<PatchOperation> Operations {get; set;} } // Domain object, no public setters public class Customer { public string FirstName {get;set;} public string LastName {get;set;} public Customer(string first, string last) { First = first; Last = last; } public void Patch(CustomerPatch patch) { First = patch.FirstName; Last = patch.LastName; } } // Patch object (like a view model, for patches) public class CustomerPatch { public string FirstName {get;set;} public string LastName {get;set;} } public class CustomersController { public HttpResponseMessage Patch(int id, CustomerPatchCommand command) { var customer = session.Load<Customer>(command.Id); // initialize the patch with the current values var patch = patchService.CreatePatch(Mapper.Map<CustomerPatch>(customer), command); // pass patch to domain object (or a handler, to perform the actual update) customer.ApplyPatch(patch); // save session.SaveChanges(); return Request.CreateResponse(HttpStatusCode.OK); } } public class PatchService { public TPatchCommand CreatePatch<TPatch, TPatchCommand>(TPatch patch, TPatchCommand command) where TPatchCommand : IPatchCommand { // translate command.Operations into a actions that are // applied to the patch object UseMagicToUpdatePatchFromPatchCommand(patch, command); return patch; } }