CoreOne.ModelPatch 1.4.0

dotnet add package CoreOne.ModelPatch --version 1.4.0
                    
NuGet\Install-Package CoreOne.ModelPatch -Version 1.4.0
                    
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="CoreOne.ModelPatch" Version="1.4.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="CoreOne.ModelPatch" Version="1.4.0" />
                    
Directory.Packages.props
<PackageReference Include="CoreOne.ModelPatch" />
                    
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 CoreOne.ModelPatch --version 1.4.0
                    
#r "nuget: CoreOne.ModelPatch, 1.4.0"
                    
#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 CoreOne.ModelPatch@1.4.0
                    
#: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=CoreOne.ModelPatch&version=1.4.0
                    
Install as a Cake Addin
#tool nuget:?package=CoreOne.ModelPatch&version=1.4.0
                    
Install as a Cake Tool

CoreOne.ModelPatch

A powerful .NET 9 / .NET 10 library for applying partial updates (PATCH operations) to EF Core entities. It converts partial model data into Delta<T> objects and intelligently patches entities while handling nested relationships, unique constraints, and parent-child foreign key relationships.

Perfect for RESTful APIs, microservices, and any scenario requiring partial entity updates without manual property-by-property mapping.

πŸš€ Key Features

  • Selective Property Updates - Only update properties present in the delta, ignoring missing fields
  • Nested Relationship Handling - Automatically processes parent-child relationships with foreign key injection
  • Unique Constraint Support - Respects [Index(IsUnique = true)] attributes (updates existing records instead of duplicating)
  • Transaction Management - Wraps all operations in EF Core transactions with automatic rollback on errors
  • Composite Key Support - Handles entities with single or composite primary keys
  • Case-Insensitive Delta Properties - Property name matching is case-insensitive for flexibility
  • Built-In JSON Naming Helpers - Configure Newtonsoft.Json or System.Text.Json property names without hand-written reflection lambdas
  • Custom Property Name Mapping - Support custom property name resolution through ModelOptions.NameResolver
  • Strict Field Validation (Optional) - Fail fast on unknown delta fields to catch typos early
  • Optimistic Concurrency (Optional) - Validate [Timestamp] and [ConcurrencyCheck] tokens during updates
  • Automatic Key Generation - Generates primary keys when missing (GUID by default, extensible via IKeyGenerator)
  • Type-Safe Delta Operations - Strongly-typed Delta<T> with compile-time safety
  • DTO-Friendly Delta Mapping - Map request DTOs into Delta<TEntity> with explicit field selection
  • Rich Patch Results - PatchResult exposes Created, Updated, Unchanged, Items, and Get<T>()

πŸ“¦ Installation

Install via NuGet:

dotnet add package CoreOne.ModelPatch

Or via the NuGet Package Manager:

Install-Package CoreOne.ModelPatch

Requirements:

  • net9.0 or net10.0
  • EF Core 9.0.x on net9.0, EF Core 10.0.x on net10.0
  • CoreOne 1.4.0.3 (automatically installed as dependency)

πŸ—οΈ Setup

1. Register Services in DI Container

// In Program.cs or Startup.cs
services.AddDbContext<YourDbContext>(options => 
    options.UseSqlServer(connectionString));

services.AddModelPatch(options => {
    options.UseJsonPropertyNames();
    options.StrictPropertyMatching = true; // optional fail-fast mode
});

2. Inject into Your Services/Controllers

public class YourController : ControllerBase
{
    private readonly IDataModelService<YourDbContext> _dataService;

    public YourController(IDataModelService<YourDbContext> dataService)
    {
        _dataService = dataService;
    }
    
    // ... use _dataService in your endpoints
}

πŸ› οΈ Usage Examples

Basic PATCH Operation

// Your entity model
public class User
{
    public Guid Id { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
    public string? PhoneNumber { get; set; }
}

// Create a partial update model
var userUpdate = new User {
    Id = existingUserId,
    Name = "John Doe",
    Email = "john@example.com"
};

// Apply the patch directly and optionally exclude fields from the generated delta
var result = await _dataService.Patch(userUpdate, delta => {
    delta.Remove(nameof(User.PhoneNumber));
}, cancellationToken);

if (result.ResultType == ResultType.Success)
{
    var updated = result.Updated;
    Console.WriteLine($"Updated {updated} records");
}

Parent-Child Relationships

The library automatically handles nested relationships:

public class Blog
{
    [Key] public Guid BlogId { get; set; }
    [Required] public string Name { get; set; }
    
    [InverseProperty(nameof(Tag.Blog))]
    public List<Tag> Tags { get; init; } = [];
}

[Index(nameof(Name), IsUnique = true)]
public class Tag
{
    [Key] public Guid Id { get; set; }
    public string Name { get; set; }
    
    public Guid? BlogId { get; set; }
    [ForeignKey(nameof(BlogId))]
    public Blog? Blog { get; set; }
}

// Create blog with nested tags
var blog = new Blog {
    BlogId = Guid.NewGuid(),
    Name = "Tech Blog",
    Tags = [
        new Tag { Name = "CSharp" },
        new Tag { Name = "DotNet" },
        new Tag { Name = "CSharp" } // Duplicate - will update existing due to unique index
    ]
};

var result = await _dataService.Patch(blog, cancellationToken);

// The library will:
// 1. Insert/update the Blog
// 2. Process each Tag
// 3. Automatically set BlogId foreign key in each Tag
// 4. Respect unique index (only 2 tags created: CSharp and DotNet)

Unique Index Constraint Handling

When entities have unique indexes, the library updates existing records instead of creating duplicates:

[Index(nameof(Email), IsUnique = true)]
public class User
{
    public Guid Id { get; set; }
    public string Email { get; set; }
    public string Name { get; set; }
}

// If a user with email "john@example.com" already exists:
var newUser = new User {
    Email = "john@example.com",
    Name = "John Updated"
};

var result = await _dataService.Patch(newUser, cancellationToken);

// Instead of throwing a duplicate key error, the existing user is updated
var updated = result.Get<User>().FirstOrDefault();

Multiple Models (Collection Patching)

// Patch multiple models of the same type directly
var users = new List<User> {
    new User { Id = id1, Name = "Alice" },
    new User { Id = id2, Name = "Bob" }
};

var result = await _dataService.Patch(users, cancellationToken);

// Or patch mixed model types
var mixedModels = new List<object> { user1, blog1, tag1 };
var result = await _dataService.PatchCollection(mixedModels, cancellationToken);

DTO-First PATCH Mapping

public class UpdateBlogRequest
{
    public Guid BlogId { get; set; }
    public string? Name { get; set; }
    public string? Url { get; set; }
    public string? IgnoredByPatch { get; set; }
}

var request = new UpdateBlogRequest {
    BlogId = blogId,
    Name = "Updated title",
    Url = "https://example.com"
};

// Keep only the target entity fields you want to patch
var delta = request.ToDelta<Blog>(p => p.BlogId, p => p.Name, p => p.Url);
var result = await _dataService.Patch(delta, cancellationToken);

// Or patch DTO directly with target entity type
var directResult = await _dataService.Patch<Blog, UpdateBlogRequest>(request, cancellationToken);

// Note: direct DTO patch maps DTO fields into a delta by matching names.
// Use the explicit include-list overload when you want strict field allow-listing.
var safeResult = await _dataService.Patch<Blog, UpdateBlogRequest>(
    request,
    [p => p.BlogId, p => p.Name, p => p.Url],
    cancellationToken);

Optimistic Concurrency Example

public class Blog
{
    [Key] public Guid BlogId { get; set; }
    public string Name { get; set; } = string.Empty;

    [Timestamp]
    public byte[] RowVersion { get; set; } = [];
}

services.AddModelPatch(options => {
    options.ValidateConcurrencyTokens = true;
    options.RequireConcurrencyTokenForUpdates = true; // optional stricter mode
});

var delta = new Delta<Blog> {
    [nameof(Blog.BlogId)] = blogId,
    [nameof(Blog.Name)] = "Updated",
    [nameof(Blog.RowVersion)] = Convert.ToBase64String(rowVersionFromClient)
};

var result = await _dataService.Patch(delta, cancellationToken);
// Fails with ResultType.Fail when token is stale or missing (if required)

🌐 ASP.NET Core Web API Integration

PATCH Endpoint Example

[ApiController]
[Route("api/[controller]")]
public class BlogsController : ControllerBase
{
    private readonly IDataModelService<AppDbContext> _dataService;

    public BlogsController(IDataModelService<AppDbContext> dataService)
    {
        _dataService = dataService;
    }

    [HttpPatch("{id}")]
    public async Task<IActionResult> PatchBlog(
        Guid id, 
        [FromBody] Blog patchData, 
        CancellationToken cancellationToken)
    {
        // Ensure the ID matches
        patchData.BlogId = id;
        
        var result = await _dataService.Patch(patchData, cancellationToken);

        if (result.ResultType == ResultType.Success)
        {
            return Ok(new {
                message = "Blog updated successfully",
                created = result.Created,
                updated = result.Updated,
                items = result.Items
            });
        }

        return BadRequest(new { 
            error = result.Message 
        });
    }

    [HttpPatch("bulk")]
    public async Task<IActionResult> PatchMultipleBlogs(
        [FromBody] List<Blog> blogs, 
        CancellationToken cancellationToken)
    {
        var result = await _dataService.Patch(blogs, cancellationToken);

        if (result.ResultType == ResultType.Success)
        {
            var stats = new {
                created = result.Created,
                updated = result.Updated,
                unchanged = result.Unchanged
            };
            return Ok(new { message = "Bulk update completed", stats });
        }

        return BadRequest(new { error = result.Message });
    }
}

Handling Validation Errors

The library automatically rolls back transactions when validation fails:

public class Blog
{
    [Key] public Guid BlogId { get; set; }
    
    [Required, StringLength(50)] 
    public string Name { get; set; }
    
    [Url, StringLength(200)] 
    public string? Url { get; set; }
}

// If validation fails (e.g., Name exceeds 50 chars), 
// the entire transaction is rolled back automatically
var result = await _dataService.Patch(delta, cancellationToken);

if (result.ResultType == ResultType.Fail)
{
    // Handle validation error
    return BadRequest(result.Message);
}

πŸ”§ Advanced Configuration

Built-In JSON Property Name Helpers

Use the built-in helpers when your models already declare JSON names:

services.Configure<ModelOptions>(options => {
    options.UseNewtonsoftJsonPropertyNames();
    // or options.UseSystemTextJsonPropertyNames();
    // or options.UseJsonPropertyNames(); // supports both attribute types
});

// Example entity with JSON property mapping
public class Tag
{
    [Key] public Guid Id { get; set; }
    
    [JsonProperty("tag_name")]  // Maps "tag_name" in JSON to Name property
    public string Name { get; set; }
}

// Now you can use either property name in your delta
var delta = new Delta<Tag> {
    ["tag_name"] = "CSharp",  // Works!
    ["Name"] = "CSharp"        // Also works! (case-insensitive)
};

You can still provide a custom NameResolver when you need non-attribute-based naming.

Default Options

ModelOptions defaults are:

  • StrictPropertyMatching = false
  • ValidateConcurrencyTokens = true
  • RequireConcurrencyTokenForUpdates = false
  • KeyGenerator = GuidGenerator (creates version 7 GUID keys wrapped in KeyModel)

Strict Field Validation

services.AddModelPatch(options => {
    options.StrictPropertyMatching = true;
});

// Unknown fields will fail with a clear message instead of being ignored.

Working with PatchResult

The Patch methods return PatchResult, which provides summary counts and typed access to processed entities:

var result = await _dataService.Patch(delta, cancellationToken);

if (result.ResultType == ResultType.Success)
{
    var created = result.Created;
    var updated = result.Updated;
    var unchanged = result.Unchanged;
    var blogs = result.Get<Blog>();
    var newTags = result.Get<Tag>(p => p.CrudType == CrudType.Created);
}

Custom Key Generation

Implement IKeyGenerator for custom primary key generation:

public class CustomKeyGenerator : IKeyGenerator
{
    public KeyModel Create() => KeyModel.Create(Guid.CreateVersion7());
}

services.Configure<ModelOptions>(options => {
    options.KeyGenerator = new CustomKeyGenerator();
});

Strongly Typed IDs (ICoreId<T>)

AddModelPatch(...) also registers an open generic IKeyGenerator<TKey> implementation (StronglyTypedIdGenerator<TKey>), which can be used for strongly typed IDs backed by GUID values.

[StronglyTypedId<Guid>]
public readonly partial struct NoteId : ICoreId<NoteId>
{
}

// Example custom generator for NoteId
public sealed class NoteIdKeyGenerator : IKeyGenerator<NoteId>
{
    public KeyModel Create() => KeyModel.Create(NoteId.Create());
}

For runtime patching, the built-in primary-key auto-generation path currently targets Guid/Guid? key properties. For other key property types, include the key value in the incoming delta/model payload or provide a custom patch pipeline.

🎯 How It Works

The Delta Pattern

Delta is a case-insensitive dictionary that holds partial model data:

// Delta inherits from Data<string, object> (case-insensitive dictionary)
var delta = new Delta<User> {
    ["name"] = "John",      // Case-insensitive
    ["Name"] = "John",      // Same as above
    ["EMAIL"] = "john@example.com"
};

// Or convert from model
var user = new User { Name = "John", Email = "john@example.com" };
var delta = user.ToDelta();

// Or map from a DTO into an entity delta
var request = new { blogId = blogId, name = "Updated title", url = "https://example.com" };
var blogDelta = request.ToDelta<Blog>(nameof(Blog.BlogId), nameof(Blog.Name), nameof(Blog.Url));

// Remove unwanted properties
delta.Remove("CreatedAt");
delta.Remove("UpdatedAt");

Processing Workflow

  1. Delta Conversion - Convert your model to Delta<T> using .ToDelta()
  2. Transaction Begin - Library wraps operation in EF Core transaction
  3. Entity Resolution - Finds existing entities by primary/unique keys
  4. Property Patching - Updates only properties present in delta
  5. Relationship Processing - Recursively processes child collections
  6. Foreign Key Injection - Automatically sets parent foreign keys in children
  7. Unique Constraint Handling - Updates existing records with unique values
  8. Validation - EF Core validates changes
  9. Transaction Commit - Saves all changes atomically (or rolls back on error)

Relationship Discovery

The library uses reflection and EF Core attributes to discover relationships:

  • [InverseProperty] - Links parent to child collections
  • [ForeignKey] - Identifies foreign key properties
  • Convention-based - Falls back to {ParentName}Id pattern
public class Blog
{
    [Key] public Guid BlogId { get; set; }
    
    // InverseProperty tells the library where this collection is referenced
    [InverseProperty(nameof(Post.Blog))]
    public List<Post> Posts { get; init; } = [];
}

public class Post
{
    [Key] public Guid Id { get; set; }
    
    // ForeignKey identifies the foreign key property
    public Guid? BlogId { get; set; }
    [ForeignKey(nameof(BlogId))]
    public Blog? Blog { get; set; }
}

πŸ“‹ API Reference

IDataModelService<TContext>

Main service contract for processing patches.

Interface Methods
// Patch a single typed delta
Task<PatchResult> Patch<T>(
    Delta<T> delta, 
    CancellationToken cancellationToken = default) where T : class, new()

// Patch multiple deltas of the same type
Task<PatchResult> Patch<T>(
    DeltaCollection<T> items, 
    CancellationToken cancellationToken = default) where T : class, new()

// Patch mixed model types
Task<PatchResult> PatchCollection(
    IEnumerable<object?> items, 
    CancellationToken cancellationToken = default)
Extension Patch Methods

The following overloads are extension methods on IDataModelService<TContext> (from DataModelServiceExtensions):

// Patch a single model directly
Task<PatchResult> Patch<TContext, TModel>(
    this IDataModelService<TContext> service,
    TModel model,
    CancellationToken cancellationToken = default)
    where TContext : DbContext where TModel : class, new()

// Patch a single model directly and customize generated delta
Task<PatchResult> Patch<TContext, TModel>(
    this IDataModelService<TContext> service,
    TModel model,
    Action<Delta<TModel>> configure,
    CancellationToken cancellationToken = default)
    where TContext : DbContext where TModel : class, new()

// Patch a DTO into a target entity
Task<PatchResult> Patch<TContext, TEntity, TDto>(
    this IDataModelService<TContext> service,
    TDto? dto,
    CancellationToken cancellationToken = default)
    where TContext : DbContext where TEntity : class, new()

// Patch DTO with delta customization callback
Task<PatchResult> Patch<TContext, TEntity, TDto>(
    this IDataModelService<TContext> service,
    TDto? dto,
    Action<Delta<TEntity>> configure,
    CancellationToken cancellationToken = default)
    where TContext : DbContext where TEntity : class, new()

// Patch DTO with explicit included entity properties
Task<PatchResult> Patch<TContext, TEntity, TDto>(
    this IDataModelService<TContext> service,
    TDto? dto,
    IEnumerable<Expression<Func<TEntity, object?>>> includedProperties,
    CancellationToken cancellationToken = default)
    where TContext : DbContext where TEntity : class, new()

// Patch a collection of model instances directly
Task<PatchResult> Patch<TContext, TModel>(
    this IDataModelService<TContext> service,
    IEnumerable<TModel?> items,
    CancellationToken cancellationToken = default)
    where TContext : DbContext where TModel : class, new()

// Patch a collection of model instances directly with delta customization
Task<PatchResult> Patch<TContext, TModel>(
    this IDataModelService<TContext> service,
    IEnumerable<TModel?> items,
    Action<Delta<TModel>> configure,
    CancellationToken cancellationToken = default)
    where TContext : DbContext where TModel : class, new()

// Patch a collection of DTO payloads
Task<PatchResult> Patch<TContext, TEntity, TDto>(
    this IDataModelService<TContext> service,
    IEnumerable<TDto?> items,
    CancellationToken cancellationToken = default)
    where TContext : DbContext where TEntity : class, new()

// Patch a collection of DTO payloads with explicit included entity properties
Task<PatchResult> Patch<TContext, TEntity, TDto>(
    this IDataModelService<TContext> service,
    IEnumerable<TDto?> items,
    IEnumerable<Expression<Func<TEntity, object?>>> includedProperties,
    CancellationToken cancellationToken = default)
    where TContext : DbContext where TEntity : class, new()

Extension Methods

// Convert model to Delta<T>
Delta<T> ToDelta<T>(this T model) where T : class, new()

// Convert collection to DeltaCollection<T>
DeltaCollection<T> ToDeltaCollection<T>(this IEnumerable<T?> items) where T : class, new()

// Map a DTO into a target entity delta
Delta<TEntity> ToDelta<TEntity>(this object model) where TEntity : class, new()

// Map a DTO into a target entity delta using an explicit field list
Delta<TEntity> ToDelta<TEntity>(this object model, params string[] includedFields) where TEntity : class, new()

// Map a DTO into a target entity delta using entity property expressions
Delta<TEntity> ToDelta<TEntity>(this object model, params Expression<Func<TEntity, object?>>[] includedProperties) where TEntity : class, new()

// Filter a patch result
IEnumerable<T> Get<T>(Predicate<ModelState>? predicate = null)
int Count(Predicate<ModelState>? predicate = null)

CrudType Enum

Indicates what operation was performed:

public enum CrudType
{
    Created = 1,
    Read = 2,
    Updated = 4,
    Deleted = 8
}

⚠️ Important Notes

Transaction Behavior

  • All operations are wrapped in transactions
  • Validation failures automatically rollback the entire transaction
  • Nested operations are part of the same transaction
  • Use cancellation tokens to cancel long-running operations

Performance Considerations

  • The library uses reflection and caches metadata for performance
  • Composite keys and unique indexes are discovered once per type
  • For bulk operations, use DeltaCollection<T> or PatchCollection instead of individual calls

Limitations

  • Entities must have parameterless constructors
  • Navigation properties must be settable (at least init accessors)
  • Circular references in object graphs may cause issues (design your models carefully)
  • Currently supports INSERT and UPDATE operations (DELETE planned for future)

🧰 Troubleshooting Cookbook

1) Strict mode fails with unknown field errors

Symptom

  • ResultType.Fail with a message similar to: Unknown fields for Blog: does_not_exist

Likely cause

  • StrictPropertyMatching is enabled and the incoming payload contains fields that do not map to the target entity.
  • The payload uses DTO names that differ from entity names without JSON name mapping.

Fix

services.AddModelPatch(options => {
    options.StrictPropertyMatching = true;
    options.UseJsonPropertyNames(); // if your model uses JsonProperty/JsonPropertyName
});

// Option A: send only entity field names
var delta = new Delta<Blog> {
    [nameof(Blog.BlogId)] = id,
    [nameof(Blog.Name)] = "Updated"
};

// Option B: map DTO to entity delta with explicit include list
var deltaFromDto = request.ToDelta<Blog>(p => p.BlogId, p => p.Name, p => p.Url);

2) Concurrency mismatch on update

Symptom

  • ResultType.Fail with a message similar to: Concurrency token mismatch for Blog.RowVersion

Likely cause

  • The client is sending an outdated token value for a model using [Timestamp] or [ConcurrencyCheck].

Fix

services.AddModelPatch(options => {
    options.ValidateConcurrencyTokens = true;
    options.RequireConcurrencyTokenForUpdates = true;
});

var delta = new Delta<Blog> {
    [nameof(Blog.BlogId)] = blogId,
    [nameof(Blog.Name)] = "Updated",
    [nameof(Blog.RowVersion)] = Convert.ToBase64String(currentRowVersionFromClient)
};

var result = await dataService.Patch(delta, token);

3) Update fails because concurrency token is required

Symptom

  • ResultType.Fail with a message similar to: Concurrency token is required for updates to Blog

Likely cause

  • RequireConcurrencyTokenForUpdates is enabled for a model that has concurrency tokens, but the payload omitted the token.

Fix

  • Include token values on update requests for concurrency-enabled models.
  • If your API does not require this strict behavior, set RequireConcurrencyTokenForUpdates = false.

4) DTO patch updates missing or wrong fields

Symptom

  • Patch succeeds but expected fields are unchanged, or wrong fields are updated.

Likely cause

  • DTO property names don’t match entity names.
  • DTO includes fields that should not be patched.

Fix

// Recommended: target entity + explicit allowed fields
var result = await dataService.Patch<Blog, UpdateBlogRequest>(
    request,
    [p => p.BlogId, p => p.Name, p => p.Url],
    token);

// Alternative: generate delta then trim
var delta = request.ToDelta<Blog>(p => p.BlogId, p => p.Name, p => p.Url);
var patchResult = await dataService.Patch(delta, token);

5) Unique index behavior seems unexpected (update vs insert)

Symptom

  • Sending a new object appears to update an existing row instead of inserting a new one.

Likely cause

  • Target entity has a unique index ([Index(..., IsUnique = true)]), and an existing row already matches the unique key.

Fix

  • This is expected behavior for duplicate unique-key payloads.
  • If insert-only semantics are required for a flow, validate uniqueness before calling Patch or use a dedicated insert endpoint.

Quick triage checklist

  • Confirm target entity key and unique index values in the payload.
  • Confirm DTO-to-entity field mapping (UseJsonPropertyNames or explicit include list).
  • Confirm strict mode and concurrency options in AddModelPatch(...).
  • Inspect result.Message, result.Created, result.Updated, result.Unchanged, and result.Get<T>().

⬆️ Upgrade Notes

Current 1.4.0 Notes

  • IKeyGenerator.Create() now returns KeyModel.
  • IKeyGenerator<TKey> support is available for strongly typed key scenarios.
  • AddModelPatch(...) registers IDataModelService<TContext>, IKeyGenerator, and open-generic IKeyGenerator<TKey>.
  • Concurrency token validation options (ValidateConcurrencyTokens, RequireConcurrencyTokenForUpdates) are part of ModelOptions.

πŸ§ͺ Testing

The library includes comprehensive unit and integration tests:

# Run tests
dotnet test

# Run with coverage
dotnet test --collect:"XPlat Code Coverage"

See COVERAGE_REPORT.md for detailed coverage metrics.

πŸ“Š Test Coverage

  • Unit and integration tests run against both net9.0 and net10.0
  • Current suite covers direct patching, DTO mapping, JSON naming helpers, unique indexes, and parent-child processing

πŸ”— Dependencies

  • CoreOne (1.4.0.3+) - Provides Data<TKey, TValue>, IResult<T>, reflection utilities
  • Microsoft.EntityFrameworkCore 9.0.x / 10.0.x - EF Core framework matched to the target framework

🀝 Contributing

Contributions are welcome! Please:

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Write tests for your changes
  4. Ensure all tests pass (dotnet test)
  5. Commit your changes (git commit -m 'Add amazing feature')
  6. Push to the branch (git push origin feature/amazing-feature)
  7. Open a Pull Request

πŸ› Issues & Support

πŸ“„ License

This project is licensed under the MIT License - see the LICENSE file for details.

πŸ™ Acknowledgments

  • Built on top of CoreOne utility library
  • Uses Entity Framework Core for data access
  • Inspired by JSON Patch (RFC 6902) but adapted for EF Core entities

Author: Juan Lopez
Version: 1.4.0 Target Frameworks: net9.0, net10.0

Product Compatible and additional computed target framework versions.
.NET net9.0 is compatible.  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 is compatible.  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

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.4.0 55 4/7/2026
1.3.0 51 4/7/2026
1.2.5 103 1/26/2026
1.2.4 183 10/2/2025
1.2.3 187 9/30/2025
1.2.2 184 9/30/2025
1.2.1 141 9/26/2025
1.2.0.1 186 9/25/2025
1.2.0 183 9/23/2025
1.1.0 184 7/17/2025
1.0.0 184 5/9/2025