{"id":16690795,"url":"https://github.com/omaxel/simplepatch","last_synced_at":"2025-03-21T18:33:42.804Z","repository":{"id":23190649,"uuid":"98534279","full_name":"omaxel/SimplePatch","owner":"omaxel","description":"A simple library for partial entity changes in ASP.NET and ASP.NET Core.","archived":false,"fork":false,"pushed_at":"2022-12-08T01:24:16.000Z","size":133,"stargazers_count":55,"open_issues_count":6,"forks_count":15,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-13T04:34:47.979Z","etag":null,"topics":["asp-net-core","asp-net-core-2","asp-net-core-web-api","asp-net-web-api","asp-net-web-api-2","entity-framework","entity-framework-core","http-patch","partial-entity-changes"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/omaxel.png","metadata":{"files":{"readme":"README-v1.x.md","changelog":"Changelog.txt","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-07-27T12:40:32.000Z","updated_at":"2024-06-09T03:48:50.000Z","dependencies_parsed_at":"2023-01-13T22:54:39.811Z","dependency_job_id":null,"html_url":"https://github.com/omaxel/SimplePatch","commit_stats":null,"previous_names":["omarmuscatello/simplepatch"],"tags_count":9,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/omaxel%2FSimplePatch","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/omaxel%2FSimplePatch/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/omaxel%2FSimplePatch/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/omaxel%2FSimplePatch/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/omaxel","download_url":"https://codeload.github.com/omaxel/SimplePatch/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244849804,"owners_count":20520791,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["asp-net-core","asp-net-core-2","asp-net-core-web-api","asp-net-web-api","asp-net-web-api-2","entity-framework","entity-framework-core","http-patch","partial-entity-changes"],"created_at":"2024-10-12T16:05:28.692Z","updated_at":"2025-03-21T18:33:42.117Z","avatar_url":"https://github.com/omaxel.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"﻿\u003cimg src=\"simplepatch.svg\" height=\"50\" alt=\"SimplePatch\"\u003e\n\u003cp\u003e\u003c/p\u003e\n\nA simple library for partial entity changes in ASP.NET and ASP.NET Core.\n\n\n**ATTENTION:** This documentation refers to the 1.x version of SimplePatch. Check out the new version [here](https://github.com/OmarMuscatello/SimplePatch).\n\n### Summary\n- [Introduction](#introduction)\n- [Install](#install)\n- [How to use](#how-to-use)\n- [Integration with the Entity Framework](#integration-with-entity-framework)\n- [Configuration](#configuration)\n\n## Introduction\n\n### The problem\nPartial modification of entities is one of the common issues when implementing a RESTful service in ASP.NET Web API. The client, in fact, must specify the value for all entity properties, including those properties whose value has not been changed. Typically, to solve this problem, you use these solutions with their own problems:\n- [`Delta\u003cT\u003e`](https://msdn.microsoft.com/en-us/library/jj890572(v=vs.118).aspx) (part of Microsoft ASP.NET WebAPI OData): it has some problems with numbers when using JSON (see [this answer](https://stackoverflow.com/a/14734273/7772490)). You also need to install the package with all its non-trivial dependencies;\n- [JSON Patch](http://jsonpatch.com/): the client must organize the data per operation and the size of the request is not optimized.\n\n##### Demonstrative example\nThe client must set the `Enabled` property of the `User` entity. The latter, however, also exposes the `Name` property. The client is forced to pass both the values of the `Enabled` and `Name` properties in the request body.\n\n*Request body*\n```   \n{ \"Enabled\": true, \"Name\": \"User1\" }\n```\n\nIn a real case, however, the properties of an entity are more than two, making the problem more pronounced.\n```   \n{ \"Enabled\": true, \"Name\": \"User1\", \"Prop1\": \"Value1\", \"Prop2\": \"Value2\", \"Prop3\": \"Value3\", ... }\n```\n\n### The solution\nThe ideal solution is to allow the client to make a request with the only properties to modify.\nReturning to the example shown in the *[Problem](#the-problem)* section, the request body for changing the value of the `Enabled` property will be:\n```   \n{ \"Enabled\": true }\n```\nIf the entity has more than one property, the request body will remain the same.\n\n*SimplePatch* allows you to implement this solution in ASP.NET Web API and ASP.NET Core Web API.\n\n## Install\nLaunch the following command from *Package Manager Console*:\n```\nInstall-Package SimplePatch\n```\n\n## How to use\n\nSee [*examples* folder](https://github.com/OmarMuscatello/SimplePatch/tree/master/examples) to learn of to use this library with ASP.NET and ASP.NET Core.\n\n##### Patching a single entity\n    [HttpPatch]\n    public IHttpActionResult PatchOne(int id, Delta\u003cPerson\u003e person)\n    {\n        // Determines the entity to be updated according to the id parameter\n        var personToPatch = TestData.People.FirstOrDefault(x =\u003e x.Id == id);\n        if (personToPatch == null) return BadRequest(\"Person not found\");\n\n        // Apply the changes specified to the original entity\n        person.Patch(personToPatch);\n\n        // Now the personToPatch variable is updated\n\n        return Ok(personToPatch);\n    }\n##### Patching multiple entities\n    [HttpPatch]\n    public IHttpActionResult PatchMultiple(DeltaCollection\u003cPerson\u003e people)\n    {\n        foreach (var person in people)\n        {\n            // Try to get the value of the Id property\n            if (person.TryGetPropertyValue(nameof(Person.Id), out var id))\n            {\n                // Determines the entity to be updated according to the specified id\n                var personToPatch = TestData.People.FirstOrDefault(x =\u003e x.Id == Convert.ToInt32(id));\n                if (personToPatch == null) return BadRequest(\"Person not found (Id = \" + id + \")\");\n\n                // Apply the specified changes to the original entity       \n                person.Patch(personToPatch);\n            }\n            else\n            {\n                // The Id property was not specified for the person represented by the person variable \n                return BadRequest(\"Id property not found for a person\");\n            }\n        }\n\n        return Ok();\n    }\n\n## Integration with Entity Framework\n##### Patching a single entity\n```\n[HttpPatch]\npublic async Task\u003cIHttpActionResult\u003e PatchOne(int id, Delta\u003cPersonEF\u003e person)\n{\n    // Determines the entity to be updated according to the id parameter\n    var personToPatch = await db.People.FindAsync(id);\n    if (personToPatch == null) return BadRequest(\"Person not found\");\n\n    // Apply the specified changes to the original entity     \n    person.Patch(personToPatch);\n\n    // Mark the entity as modified\n    db.Entry(personToPatch).State = EntityState.Modified;\n\n    // Now the personToPatch variable is updated\n\n    // Save the changes\n    await db.SaveChangesAsync();\n\n    return Ok(personToPatch);\n}\n```\n\n##### Patching multiple entities\n```\n[HttpPatch]\npublic async Task\u003cIHttpActionResult\u003e PatchMultiple(DeltaCollection\u003cPersonEF\u003e people)\n{\n    foreach (var person in people)\n    {\n        // Try to get the value of the Id property\n        if (person.TryGetPropertyValue(nameof(PersonEF.Id), out var id))\n        {\n            // Determines the entity to be updated according to the id parameter\n            var personToPatch = await db.People.FindAsync(Convert.ToInt32(id));\n            if (personToPatch == null) return BadRequest(\"Person not found (Id = \" + id + \")\");\n\n            // Apply the specified changes to the original entity\n            person.Patch(personToPatch);\n\n            // Mark the entity as modified\n            db.Entry(personToPatch).State = EntityState.Modified;\n        }\n        else\n        {\n            // The Id property was not specified for the person represented by the person variable \n            return BadRequest(\"Id property not found for a person\");\n        }\n    }\n\n    // Save the changes\n    await db.SaveChangesAsync();\n\n    return Ok();\n}\n```\n\n## Configuration\n\n#### Exclude properties\nYou can exclude one or more properties of an entity while applying the changes to the original entity to preserve the original value of the property. This might be useful for properties used to uniquely identify the entity.\n\n**Global.asax** or **Startup.cs**\n```\nDeltaConfig.Init((cfg) =\u003e\n{\n    // Exclude the Id property of the Person entity.\n    cfg.ExcludeProperties\u003cPerson\u003e(x =\u003e x.Id);\n});\n```\n\n**Note:** When a property is marked as *excluded* it will still be present in the `Delta \u003cT\u003e` object, but it will be ignored when the changes are applied (`Patch` method) to the original entity.\n\n#### Ignore letter case for property names\nYou can ignore letter case for property names. This is useful when you have different name convention between client code and server code.\nFor example, the property `name` sent by the client wouldn't be detected as part of an entity which has a property named `Name` (note the first letter is **upper case**).\n\n**Global.asax** or **Startup.cs**\n```\nDeltaConfig.Init((cfg) =\u003e\n{\n    cfg.IgnoreLetterCase();\n});\n```\n\n#### Ignore null value for specified properties\nYou can ignore null value for specified properties of an entity.\n\nThis is particularly useful in two cases:\n\n- when your property is a value type (like `int` and `DateTime`) and your client still send a null value for that property. Ignoring null value will avoid exception.\n- when your property is a reference type (which allows null) but you don't want that `null` overwrites your previous stored data.\n\n**Global.asax** or **Startup.cs**\n```\nDeltaConfig.Init(cfg =\u003e\n{\n    cfg.IgnoreNullValue\u003cMyClass\u003e(x =\u003e x.Date);\n\n    // Multiple properties\n    // cfg.IgnoreNullValue\u003cMyClass\u003e(x =\u003e x.Date1, x =\u003e x.Date2);\n});\n```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fomaxel%2Fsimplepatch","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fomaxel%2Fsimplepatch","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fomaxel%2Fsimplepatch/lists"}