2

I'm using Entity Framework in a .NET Web API (not Core) project with an Angular front end. Currently, the only examples of patch implementation I can find is for ASP.Net Core products - so I'd firstly like confirm if patch is even possible in plain ol' ASP.Net Web API (not Core).

I'd like to implement patch in some of my controllers, but by default, the Entity controller does not seem to come with code for a patch operation. Instead it comes with GET, PUT, POST and DELETE. What code is needed in my Entity controller so that patch requests are valid? Is there a way to specify this when adding a new Entity controller, or must it always be entered in manually?

My controller:

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using System.Web.Http.Description;
using api.Models;
using api.Models.my_model;
using System.Net.Mail;
using System.Configuration;

namespace api.Controllers.my_model
{
    public class myController : ApiController
    {
        private myCodeFirst db = new myCodeFirst();

        // GET: api/my
        public IQueryable<myTable> GetmyTable()
        {
            return db.myTable;
        }

        // GET: api/my/5
        [ResponseType(typeof(myTable))]
        public IHttpActionResult GetmyTable(int id)
        {
            myTable myTable = db.myTable.Find(id);
            if (myTable == null)
            {
                return NotFound();
            }

            return Ok(myTable);
        }

        // PUT: api/my/5
        [ResponseType(typeof(void))]
        public IHttpActionResult PutmyTable(int id, myTable myTable)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }

            if (id != myTable.ID)
            {
                return BadRequest();
            }

            db.Entry(myTable).State = EntityState.Modified;

            try
            {
                db.SaveChanges();
            }
            catch (DbUpdateConcurrencyException)
            {
                if (!myTableExists(id))
                {
                    return NotFound();
                }
                else
                {
                    throw;
                }
            }

            return StatusCode(HttpStatusCode.NoContent);
        }

        // POST: api/my
        [ResponseType(typeof(myTable))]
        public IHttpActionResult PostmyTable(myTable myTable)
        {

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

            db.myTable.Add(myTable);


            db.SaveChanges();


            return CreatedAtRoute("DefaultApi", new { id = myTable.ID }, myTable);
        }

        // DELETE: api/my/5
        [ResponseType(typeof(myTable))]
        public IHttpActionResult DeletemyTable(int id)
        {
            myTable myTable = db.myTable.Find(id);
            if (myTable == null)
            {
                return NotFound();
            }

            db.myTable.Remove(myTable);
            db.SaveChanges();

            return Ok(myTable);
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                db.Dispose();
            }
            base.Dispose(disposing);
        }

        private bool myTableExists(int id)
        {
            return db.myTable.Count(e => e.ID == id) > 0;
        }


    }



}
Kyle Vassella
  • 2,296
  • 10
  • 32
  • 62
  • 2
    Use the `[HttpPatch]` attribute. In fact you should likely use all the Http method attributes instead of relying on the naming conventions – maccettura Sep 18 '18 at 20:49
  • 1
    Agreeing with maccettura. Naming conventions like this can actually result in poor naming of methods, and thus poor maintainability and readability. – ColinM Sep 18 '18 at 21:27
  • Thanks guys. Can either of you post an example of a controller implementing a patch? All the examples I found online are for `ASP.NET Core` and use namespaces like JsonPatchDocument, which doesn't seem to be available in `.Net Web API` (non Core), which is what I'm using. – Kyle Vassella Sep 26 '18 at 16:09

1 Answers1

2

The PATCH method is a request method supported by the HTTP protocol for making partial changes to an existing resource. There is some JSON Patch operations you can see:

Add

{
    "op": "add",
    "path": "/a/b",
    "value": "foo"
}

Remove

{
    "op": "remove",
    "path": "/a/b"
}

Replace

{
    "op": "replace",
    "path": "/a/b",
    "value": "foo"
}

Copy

{
    "op": "copy",
    "from": "/a/b",
    "path": "/a/c"
}

Move

{
    "op": "move",
    "from": "/a/b",
    "path": "/a/c"
}

Test

{
    "op": "test",
    "path": "/a/b",
    "value": "foo"
}

In ASP.NET Core you specify patch methods with [HttpPatch] attribute. For getting data in this methods you should use JsonPatchDocument<TModel> exist in Microsoft.ApsNetCore.JsonPatch namespace where TModel is your entity that you want convert to it. Another package that will be useful is AutoMapper. You can install it from NuGet package manager as below:

Install-Package AutoMapper

and referenced it in your controller.

Now it's time to look at an example in ASP.NET Core Web API:

public async Task<IActionResult> PartiallyUpdateBook([FromRoute] Guid id, [FromBody] JsonPatchDocument<BookModel> patchDoc)
{
    // If the received data is null
    if (patchDoc == null)
    {
        return BadRequest();
    }

    // Retrieve book from database
    var book = await _context.Books.SingleOrDefaultAsync(x => x.Id == id)

    // Check if is the book exist or not
    if (book == null)
    {
        return NotFound();
    }

    // Map retrieved book to BookModel with other properties (More or less with eexactly same name)
    var bookToPatch = Mapper.Map<BookModel>(book);

    // Apply book to ModelState
    patchDoc.ApplyTo(bookToPatch, ModelState);

    // Use this method to validate your data
    TryValidateModel(bookToPatch);

    // If model is not valid, return the problem
    if (!ModelState.IsValid)
    {
        return BadRequest(ModelState);
    }

    // Assign entity changes to original entity retrieved from database
    Mapper.Map(bookToPatch, book);

    // Say to entity framework that you have changes in book entity and it's modified
    _context.Entry(book).State = EntityState.Modified;

    // Save changes to database
    await _context.SaveChangesAsync();

    // If everything was ok, return no content status code to users
    return NoContent();
}
Emmanuel DURIN
  • 4,803
  • 2
  • 28
  • 53
Sajad Afaghiy
  • 524
  • 5
  • 12
  • Thanks Sajad. We are using `ASP.net Web API`, not `ASP.NET Core Web API` - will this still work in that case? Also, is it possible to implement a patch without a third party addon? Or is having a good addon pretty important? – Kyle Vassella Sep 21 '18 at 21:37
  • All of ASP.NET Web APIs support this attributes as I know. Absolutely you can implement the sample code without Automapper package. But you should write many codes! This package is powerful and I recommend use it in all of your projects. Automapper homepage: https://automapper.org and source code on GitHub: https://github.com/AutoMapper/AutoMapper – Sajad Afaghiy Sep 22 '18 at 00:08
  • Hmm...for example, even in `.net Core`, JsonPatchDocument requires an install for JsonPatchDocument: `Install-Package Microsoft.AspNetCore.JsonPatch`. In `.NET Web API`, this install is not compatible and will not work. So in .net web API (not core), I don't think I have the ability to use JSON Patch Document. There really is not a lot of documentation about this online - I really hope Patch is possible for .NET Web API but I'm unsure. – Kyle Vassella Sep 26 '18 at 16:05
  • 1
    Ok, it's just for .net core. So you can use this package ```Install-Package Marvin.JsonPatch``` for .net framework. – Sajad Afaghiy Sep 26 '18 at 18:32
  • 1
    Thanks Sajad, I actually found this extension just before you posted - there was a choice between it, or another JsonPatch extension by MyQuay, or using ODATA. I think this Marvin.Jsonpatch may be a better choice than having all of ODATA's dependencies just for a patch. Any way you could check my question here and see if you can comprehend his documentation for *implementing `JsonPatchDocument` class on the client*? https://stackoverflow.com/questions/52527311/kevindockxs-jsonpatch-in-an-angular-net-web-api-project Thanks for your advice so far – Kyle Vassella Sep 26 '18 at 23:38
  • On further research I think the documentation for KevinDockx's `Marvin.JsonPatch` was not written for sending requests from a separate front end framework like Angular. I'm really struggling with the concept of how to send my patch data from `Angular` to `Web API` aside from manually building a dynamic JsonPatchDocument of my own within Angular. And manually building it out seems like a challenge or there wouldn't be so many extensions to build the request for you. I've hit a brick wall with this one. – Kyle Vassella Sep 27 '18 at 17:12