PTrampert.SimplePatch 1.3.7

dotnet add package PTrampert.SimplePatch --version 1.3.7
                    
NuGet\Install-Package PTrampert.SimplePatch -Version 1.3.7
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="PTrampert.SimplePatch" Version="1.3.7" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="PTrampert.SimplePatch" Version="1.3.7" />
                    
Directory.Packages.props
<PackageReference Include="PTrampert.SimplePatch" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add PTrampert.SimplePatch --version 1.3.7
                    
#r "nuget: PTrampert.SimplePatch, 1.3.7"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package PTrampert.SimplePatch@1.3.7
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=PTrampert.SimplePatch&version=1.3.7
                    
Install as a Cake Addin
#tool nuget:?package=PTrampert.SimplePatch&version=1.3.7
                    
Install as a Cake Tool

PTrampert.SimplePatch

Library supporting simple patch objects in .NET.

Overview

PTrampert.SimplePatch is a C# library designed to facilitate the handling of flat PATCH objects in .NET web applications. With this library, you can easily distinguish between properties that are omitted from a PATCH request and those explicitly set to null or a value.

This is especially useful when you want to update only specific properties of an object, leaving others unchanged. For example, if you send a PATCH request like:

{
  "name": "New Name"
}

and your object contains additional properties besides name, PTrampert.SimplePatch makes it easy to ensure that only name is updated, and all other properties remain untouched.

Features

  • Supports "Optional" types for PATCH operations.
  • Dynamically generates implementations of IPatchObject for your write models.
  • Preserves validation attributes on properties, allowing for validation of PATCH requests.
  • Handles complex types with custom JSON converters.
  • Documents PATCH routes properly in OpenAPI, via optional Swashbuckle and Microsoft.AspNetCore.OpenApi integration packages.

Getting Started

A full sample project is available in the PTrampert.SimplePatch.Samples

  • Install the library via NuGet:
dotnet add package PTrampert.SimplePatch
  • Create your write model class
public record PersonWriteModel
{
    // For PATCH requests, Required will only be enforced if the property is present in the request body.
    [Required]
    [StringLength(255, MinimumLength = 3)]
    public required string Name { get; init; }
    
    public DateTime DateOfBirth { get; init; }
    
    // Validation attributes can be used to enforce rules on the email field.
    [EmailAddress]
    public string? Email { get; init; }
    
    // Using a custom JSON converter to handle phone number serialization and deserialization
    [JsonConverter(typeof(PhoneNumberJsonConverter))]
    public PhoneNumber? PhoneNumber { get; init; }
}
  • Use IPatchObject<PersonWriteModel> to create an optional object for PATCH operations:
    [HttpPatch("{id:int}")]
    public ActionResult<PersonReadModel> PatchPerson(
        int id,
        // PTrampert.SimplePatch automatically generates an implementation of IPatchObject<PersonWriteModel>
        [FromBody] IPatchObject<PersonWriteModel> patchObject)
    {
        // Validation is preserved on the patch object, so we can check ModelState
        if (!ModelState.IsValid)
            return BadRequest(ModelState);
        
        var existingPerson = _db.GetPersonById(id);
        if (existingPerson == null)
            return NotFound();
        
        var patchedPerson = patchObject.Patch(existingPerson);
        var updatedPerson = _db.UpdatePerson(id, patchedPerson);
        
        return updatedPerson!;
    }

This will allow you to make the following HTTP request to update only the specified properties of Person (in this example, only the 'name' property).

PATCH /person/1 HTTP/1.1
Content-Type: application/json
Content-Length: 24

{
  "name": "New Name"
}

Sending patches from .NET

AddSimplePatchConverters() also makes serialization omit every Optional<T> property that has no value, so Optional<T> bodies are safe to send from a client too. An unset property is left out, an explicit null is written as null, and any other value (including 0 or false) is written as is:

public record PersonPatch
{
    public Optional<string> Name { get; init; }
    public Optional<string?> Email { get; init; }
    public Optional<DateTime> DateOfBirth { get; init; }
}

var options = new JsonSerializerOptions(JsonSerializerDefaults.Web);
options.AddSimplePatchConverters();

JsonSerializer.Serialize(new PersonPatch { Name = "New Name", Email = null }, options);
// {"name":"New Name","email":null}

No DefaultIgnoreCondition is needed for this, so it doesn't affect your other types.

OpenAPI

Out of the box, an OpenAPI generator describes a [FromBody] IPatchObject<T> parameter from the interface, which has no properties. The request body ends up documented as an empty object — useless to a reader, and strict enough to reject every legal PATCH:

"PersonWriteModelIPatchObject": { "type": "object", "additionalProperties": false }

Install the integration package for your generator to document the patched model's schema instead, with every property optional.

Swashbuckle

dotnet add package PTrampert.SimplePatch.Swashbuckle
builder.Services.AddSwaggerGen(options => options.AddSimplePatchSchemas());

Microsoft.AspNetCore.OpenApi (.NET 10+)

dotnet add package PTrampert.SimplePatch.OpenApi
builder.Services.AddOpenApi(options => options.AddSimplePatchSchemas());

Either way the PATCH body is now described with the model's own properties, validation constraints and converters, and nothing marked required:

"PersonWriteModelIPatchObject": {
  "type": "object",
  "properties": {
    "name":        { "maxLength": 255, "minLength": 3, "type": "string" },
    "dateOfBirth": { "type": "string", "format": "date-time" },
    "email":       { "type": "string", "format": "email", "nullable": true },
    "phoneNumber": { "$ref": "#/components/schemas/PhoneNumber" }
  },
  "additionalProperties": false,
  "description": "Partial update of PersonWriteModel. Omitted properties are left unchanged."
}

Because the patch schema is derived from the model's own schema, anything you already do to tune that model — a MapType, a schema filter, XML comments — applies to the PATCH body too, and the two can never describe different shapes.

Setting an example

Swagger UI builds its example from the schema's properties and ignores required, so by default the PATCH example lists every property — which reads as "send all of these", and is what Try it out will submit. Supply an example to show the partial-update semantics instead:

builder.Services.AddSwaggerGen(options => options.AddSimplePatchSchemas(patch =>
{
    patch.Example = _ => new JsonObject { ["name"] = "New Name" };
}));

Other options

AddSimplePatchSchemas takes a SimplePatchSchemaOptions:

Option Default Effect
Example null Example body for the patch schema. See above.
SchemaId null Names the patch schema component. t => t.Name + "Patch" gives PersonWriteModelPatch instead of PersonWriteModelIPatchObject.
DescriptionFormat "Partial update of {0}. Omitted properties are left unchanged." The patch schema's description. Null leaves it alone.
ClearRequired true Drops required, which is what makes the body a partial update.
SchemaId and your own schema-id selector

With the built-in generator, SchemaId takes effect whatever CreateSchemaReferenceId you set, before or after AddSimplePatchSchemas.

Swashbuckle picks a schema's id before any filter runs, so the SwaggerGenOptions extension applies SchemaId by wrapping whichever selector is in place when it is called. A CustomSchemaIds call made after it replaces the wrapper and silently drops SchemaId. Either call AddSimplePatchSchemas after CustomSchemaIds, or use the service-collection overload, which applies SchemaId after all of your configuration regardless of order:

builder.Services.AddSwaggerGen(options => options.CustomSchemaIds(t => t.Name));
builder.Services.AddSimplePatchSchemas(patch => patch.SchemaId = t => t.Name + "Patch");

Use one overload or the other, not both.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (3)

Showing the top 3 NuGet packages that depend on PTrampert.SimplePatch:

Package Downloads
PTrampert.SimplePatch.Schema

Shared OpenAPI schema generation for PTrampert.SimplePatch. Referenced by the Swashbuckle and Microsoft.AspNetCore.OpenApi integration packages; not usually installed directly.

PTrampert.SimplePatch.OpenApi

Microsoft.AspNetCore.OpenApi integration for PTrampert.SimplePatch. Documents routes taking an IPatchObject<T> with the patched model's schema instead of an empty object.

PTrampert.SimplePatch.Swashbuckle

Swashbuckle integration for PTrampert.SimplePatch. Documents routes taking an IPatchObject<T> with the patched model's schema instead of an empty object.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.3.7 0 10/1/2026
1.3.6 30 10/1/2026
1.3.5 29 10/1/2026
1.3.4 27 10/1/2026
1.3.3 29 10/1/2026
1.3.2 27 10/1/2026
1.3.1 41 10/1/2026
1.3.0 35 10/1/2026
1.2.9 43 10/1/2026
1.2.8 34 10/1/2026
1.2.7 41 10/1/2026
1.2.6 36 10/1/2026
1.2.5 38 10/1/2026
1.2.4 41 10/1/2026
1.2.3 50 10/1/2026
1.2.2 46 10/1/2026
1.2.1 183 9/12/2026
1.2.0 130 9/12/2026
1.1.15 92 9/12/2026
1.1.14 111 8/7/2026
Loading failed