Philiprehberger.ChangeTracker 0.2.0

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

Philiprehberger.ChangeTracker

CI NuGet Last updated

Track and diff property changes on objects over time for audit logging.

Installation

dotnet add package Philiprehberger.ChangeTracker

Usage

Mark your class with [TrackChanges], then create a tracker and inspect changes:

using Philiprehberger.ChangeTracker;

[TrackChanges]
public class User
{
    public string Name { get; set; } = "";
    public string Email { get; set; } = "";

    [IgnoreChanges]
    public DateTime LastLogin { get; set; }

    [SensitiveProperty]
    public string Password { get; set; } = "";
}

var user = new User { Name = "Alice", Email = "alice@example.com", Password = "secret" };
var tracker = ChangeTracker.For(user);

user.Name = "Bob";
user.Email = "bob@example.com";
user.Password = "new-secret";
user.LastLogin = DateTime.UtcNow; // ignored

IReadOnlyList<PropertyChange> changes = tracker.GetChanges();
// [
//   PropertyChange { PropertyName = "Name", OldValue = "Alice", NewValue = "Bob", ... },
//   PropertyChange { PropertyName = "Email", OldValue = "alice@example.com", NewValue = "bob@example.com", ... },
//   PropertyChange { PropertyName = "Password", OldValue = "***", NewValue = "***", ... }
// ]

Nested Object Tracking

Properties that are complex objects are tracked recursively with dot-notation paths:

public class Address
{
    public string City { get; set; } = "";
    public string Street { get; set; } = "";
}

[TrackChanges]
public class Customer
{
    public string Name { get; set; } = "";
    public Address Address { get; set; } = new();
}

var customer = new Customer
{
    Name = "Alice",
    Address = new Address { City = "Berlin", Street = "Main St" }
};
var tracker = ChangeTracker.For(customer);

customer.Address.City = "Munich";

var changes = tracker.GetChanges();
// PropertyChange { PropertyName = "Address.City", OldValue = "Berlin", NewValue = "Munich" }

Collection Diff

When a tracked list property changes, individual element additions, removals, and modifications are reported:

[TrackChanges]
public class Order
{
    public List<string> Items { get; set; } = new();
}

var order = new Order { Items = new List<string> { "Apple", "Banana" } };
var tracker = ChangeTracker.For(order);

order.Items = new List<string> { "Apple", "Cherry", "Date" };

var changes = tracker.GetChanges();
// changes[0].CollectionDiff.Changes:
//   CollectionChange { Index = 1, Kind = Modified, OldValue = "Banana", NewValue = "Cherry" }
//   CollectionChange { Index = 2, Kind = Added, OldValue = null, NewValue = "Date" }

Rollback

Revert the tracked object to its snapshot state:

var user = new User { Name = "Alice", Email = "alice@example.com" };
var tracker = ChangeTracker.For(user);

user.Name = "Bob";
user.Email = "bob@example.com";

tracker.Rollback();
// user.Name == "Alice", user.Email == "alice@example.com"

JSON Serialization

ChangeSet changeSet = tracker.GetChangeSet();
string json = changeSet.ToJson();

ChangeSet restored = ChangeSet.FromJson(json);

API

ChangeTracker

Method Description
For<T>(T target) Creates a new ChangeTracker<T> that snapshots the target's current state

ChangeTracker<T>

Method Description
GetChanges() Returns an IReadOnlyList<PropertyChange> of properties that differ from the snapshot
GetChangeSet() Returns a ChangeSet wrapping all changes with type metadata and timestamp
Rollback() Reverts the tracked object to the snapshot state taken at construction time

PropertyChange

Property Type Description
PropertyName string Name of the changed property (dot-notation for nested properties)
OldValue object? Original value (masked as "***" for sensitive properties)
NewValue object? Current value (masked as "***" for sensitive properties)
Timestamp DateTimeOffset When the change was detected
CollectionDiff CollectionDiff? Element-level diff for collection properties, null otherwise

CollectionDiff

Property Type Description
Changes IReadOnlyList<CollectionChange> Individual element changes within the collection

CollectionChange

Property Type Description
Index int Zero-based index of the element
Kind CollectionChangeKind Added, Removed, or Modified
OldValue object? Original value (null for additions)
NewValue object? Current value (null for removals)

ChangeSet

Member Description
TypeName Full name of the tracked type
Changes IReadOnlyList<PropertyChange> of detected changes
TrackedAt Timestamp when tracking began
ToJson() Serializes the change set to JSON
FromJson(string) Deserializes a change set from JSON

Attributes

Attribute Target Description
[TrackChanges] Class Required. Opts a class into change tracking
[IgnoreChanges] Property Excludes the property from tracking
[SensitiveProperty] Property Masks old/new values with "***" in change records

Development

dotnet build src/Philiprehberger.ChangeTracker.csproj --configuration Release
dotnet test tests/Philiprehberger.ChangeTracker.Tests/Philiprehberger.ChangeTracker.Tests.csproj --configuration Release

Support

If you find this project useful:

Star the repo

🐛 Report issues

💡 Suggest features

❤️ Sponsor development

🌐 All Open Source Projects

💻 GitHub Profile

🔗 LinkedIn Profile

License

MIT

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.
  • net8.0

    • 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.

Version Downloads Last Updated
0.2.0 119 4/1/2026
0.1.6 109 3/25/2026
0.1.5 107 3/23/2026
0.1.4 103 3/23/2026
0.1.3 110 3/17/2026
0.1.2 123 3/16/2026
0.1.1 119 3/16/2026
0.1.0 111 3/16/2026