Err.ChangeTracking
0.4.0
See the version list below for details.
dotnet add package Err.ChangeTracking --version 0.4.0
NuGet\Install-Package Err.ChangeTracking -Version 0.4.0
<PackageReference Include="Err.ChangeTracking" Version="0.4.0" />
<PackageVersion Include="Err.ChangeTracking" Version="0.4.0" />
<PackageReference Include="Err.ChangeTracking" />
paket add Err.ChangeTracking --version 0.4.0
#r "nuget: Err.ChangeTracking, 0.4.0"
#addin nuget:?package=Err.ChangeTracking&version=0.4.0
#tool nuget:?package=Err.ChangeTracking&version=0.4.0
Err.ChangeTracking Solution (Full Documentation)
A complete, high-performance, AOT-friendly solution to track changes on POCO models โ combining runtime efficiency and compile-time generation.
๐ Introduction
Managing changes on POCO models is a common but tedious problem. Manually tracking modifications leads to verbose code, maintenance headaches, and runtime inefficiency.
Err.ChangeTracking provides:
- A lightweight runtime library.
- A powerful Roslyn Source Generator to automate everything at compile time.
Result?
โก Lightning-fast, zero-reflection change tracking ready for Blazor, NativeAOT, Cloud, Web, and Mobile.
๐จ Problem Statement
โ
How to detect if a POCO model has been modified?
โ
How to rollback changes easily?
โ
How to track changes without relying on slow reflection or dynamic proxies?
Conventional solutions introduce runtime overhead and are not AOT-friendly.
๐ Manual Implementation Example (Using Only Err.ChangeTracking
Runtime)
using System;
using Err.ChangeTracking;
public partial class Person : ITrackable<Person>
{
private IChangeTracking<Person> _changeTracker;
public IChangeTracking<Person> GetChangeTracker() => _changeTracker ??= new ChangeTracking<Person>(this);
private string _firstName;
public partial string FirstName
{
get => _firstName;
set { _changeTracker?.RecordChange(nameof(FirstName), _firstName, value); _firstName = value; }
}
private int _age;
public partial int Age
{
get => _age;
set { _changeTracker?.RecordChange(nameof(Age), _age, value); _age = value; }
}
}
Explanation:
_changeTracker
monitors the original values.RecordChange
is called manually inside each setter.IsDirty
,Rollback
, andAcceptChanges
become available.
โ Powerful โ but โ very repetitive and error-prone for large models.
๐ค With Err.ChangeTracking.SourceGenerator (Recommended)
Instead of manually writing setters, just annotate your class:
[Trackable]
public partial class Person
{
public partial string FirstName { get; set; }
public partial int Age { get; set; }
}
The source generator will auto-generate the backing fields, tracking logic, and setter wrappers.
โ Keep your models clean, readable, and efficient.
โจ How the Generator Works
- Class must be marked as
[Trackable]
- Class must be
partial
- Public,
partial
properties are eligible - Attributes control behavior:
Attribute | Behavior |
---|---|
[TrackOnly] |
Track this property only (when using TrackingMode.OnlyMarked ) |
[NotTracked] |
Exclude this property from tracking |
[TrackCollection] |
Track changes inside List/Dictionary items |
TrackingMode.All |
(default) track all eligible properties |
TrackingMode.OnlyMarked |
Only track [TrackOnly] properties |
๐งช Examples From Unit Tests
Tracking simple properties
var person = new Person { FirstName = "Alice", Age = 30 }.AsTrackable();
person.FirstName = "Bob";
Assert.True(person.GetChangeTracker().IsDirty);
Assert.True(person.GetChangeTracker().HasChanged(x => x.FirstName));
Rollback a single property
person.GetChangeTracker().Rollback(x => x.FirstName);
Assert.Equal("Alice", person.FirstName);
Tracking collections
[Trackable]
public partial class Order
{
[TrackCollection]
public partial List<string> Items { get; set; }
}
var order = new Order { Items = new List<string>() }.AsTrackable();
order.Items.AsTrackable().Add("New Item");
Assert.True(order.GetChangeTracker().IsDirty);
TrackingMode OnlyMarked with [TrackOnly]
[Trackable(Mode = TrackingMode.OnlyMarked)]
public partial class Invoice
{
[TrackOnly]
public partial string InvoiceNumber { get; set; }
public partial string Comment { get; set; }
}
var invoice = new Invoice { InvoiceNumber = "INV001", Comment = "Test" }.AsTrackable();
invoice.InvoiceNumber = "INV002";
invoice.Comment = "Changed Comment";
Assert.True(invoice.GetChangeTracker().HasChanged(x => x.InvoiceNumber));
Assert.False(invoice.GetChangeTracker().HasChanged(x => x.Comment));
๐ Typical Use Cases
- Change tracking in CRUD applications
- Undo/Redo features
- Form validation and dirty detection
- Optimizing entity update operations
๐ฆ Requirements
IMPORTANT: This package uses C# 13 feature Patrial properties and requires either:
- .NET 9.0 or higher, OR
- .NET 8.0 with LangVersion set to "preview" in your project file
If you're using .NET 8.0, add the following to your project file:
<PropertyGroup>
<LangVersion>preview</LangVersion>
</PropertyGroup>
! Without this configuration, the source generator will not work correctly.
๐ฆ Installation
dotnet add package Err.ChangeTracking
dotnet add package Err.ChangeTracking.SourceGenerator
๐ License
Licensed under the MIT License.
๐ฅ Related Projects
- Err.ChangeTracking โ Runtime library
- Err.ChangeTracking.SourceGenerator โ Roslyn code generator
๐ Contributions
Contributions are welcome!
Fork the repository, submit a PR, or open an issue to suggest improvements!
Built with โค๏ธ by ERRADIL
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. 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. |
.NET Core | netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
.NET Standard | netstandard2.1 is compatible. |
MonoAndroid | monoandroid was computed. |
MonoMac | monomac was computed. |
MonoTouch | monotouch was computed. |
Tizen | tizen60 was computed. |
Xamarin.iOS | xamarinios was computed. |
Xamarin.Mac | xamarinmac was computed. |
Xamarin.TVOS | xamarintvos was computed. |
Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.1
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.