ChangeTracker.Npgsql 1.0.1

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

ChangeTracker

NuGet Status NuGet Status NuGet Status NuGet Status NuGet Status

ChangeTracker is inspired by Delta Project

Change Tracker is a library for efficient HTTP caching using database change tracking. It implements 304 Not Modified responses by generating ETags based on database timestamps, reducing server load while ensuring clients always receive current data.

๐Ÿ“‹ Overview

ChangeTracker monitors database changes and generates ETags that combine:

  • Assembly write time (when your application was built)
  • Database timestamp (last data modification time)
  • Custom suffix (optional runtime context)

When a client requests data with a cached ETag, the server compares it with the current state.

  • If unchanged: it returns 304 Not Modified and the client uses its cached copy.
  • If changed: fresh data is returned with a new ETag.

ETags follow this format:

{AssemblyWriteTime}-{DbTimeStamp}-{Suffix}

๐Ÿ’ก Ideal Use Case

  • Read-heavy applications where data changes less frequently than it's read
  • APIs serving semi-static data that changes periodically
  • Applications needing reduced server load without compromising data freshness

๐Ÿ“š Documentation

๐Ÿ› ๏ธ How It Works

Assembly Write Time

The last modification time of your web application's assembly is handled by the AssemblyTimestampProvider, which implements the IAssemblyTimestampProvider interface.

public sealed class AssemblyTimestampProvider(Assembly assembly) : IAssemblyTimestampProvider
{
    public DateTimeOffset GetWriteTime()
    {
        ArgumentNullException.ThrowIfNull(assembly, nameof(assembly));

        if (!File.Exists(assembly.Location))
            throw new FileNotFoundException($"Assembly file not found at '{assembly.Location}'");

        return File.GetLastWriteTimeUtc(assembly.Location);
    }
}

Database Timestamp

Tracks when data was last modified. Implementation varies by database:

Custom Suffix (Optional)

Dynamic string based on HTTP context for fine-grained cache control:

var builder = WebApplication.CreateBuilder(args);
{
  builder.Services
     .AddTracker(options =>
     {
         options.Suffix = (httpContext) => "Suffix";
     });
}

var app = builder.Build();
{
    app.UseTracker(options =>
    {
        options.Suffix = (httpContext) => "Suffix";
    });

    app.MapGet("route", () => { })
      .WithTracking(options =>
      {
          options.Suffix = (httpContext) => "Suffix";
      });
}

ETag Generation & Comparison

For comparison and generation of ETags, see the implementation in DefaultETagProvider of the IETagProvider interface.

Chanage Tracker Client Registration

Tracker services can be registered using the AddTracker extension method, which accepts a GlobalOptions configuration object.

builder.Services.AddTracker();

builder.Services.AddTracker(new GlobalOptions()
{
    CacheControl = "max-age=60, stale-while-revalidate=60, stale-if-error=86400",
});

builder.Services.AddTracker(options =>
{
    options.Filter = (httpContext) => true;
});

Provider Documentation

For ChangeTracker to function correctly, you must register a database-specific source provider. This component monitors database changes and provides timestamps for ETag generation.

Detailed implementation guides for each database:

๐Ÿ”ง Usage

Controller Action (MVC/Web API)

Apply caching to specific endpoints using the [Track] attribute:

[HttpGet]
[Track(tables: ["roles"], cacheControl: "no-cache")]
public ActionResult<IEnumerable<Role>> GetAll() 
{
    return dbContext.Roles.ToList();
}

Middleware Configuration

Apply caching globally:

app.UseTracker(options =>
{
    options.CacheControl = "max-age=60, stale-while-revalidate=60, stale-if-error=86400";
    options.Filter = (httpContext) => httpContext.Request.Path.Value.Contains("/api/");
});

Minimal APIs

Configure tracking directly on minimal API endpoints:

app.MapGet("/api/user-profile", () => 
{
    // Your endpoint logic
})
.WithTracking(options =>
{
    options.Tables = ["users", "profiles", "preferences"];
    options.CacheControl = "max-age=300"; // 5 minutes
});

Fast Endpoints

Configure tracking directly with Fast Endpoints:

builder.Services.AddTrackerFastEndpoints();

[Track(tables: ["roles"])] //attribute usage is optional, if not specified options will be fully taked from DI
public sealed class MyEndpoint : Endpoint<EmptyRequest>
{
    public override void Configure()
    {
        PreProcessor<TrackerPreProcessor<EmptyRequest>>();
    }
}

//or global
app.UseFastEndpoints((config) =>
{
    config.Endpoints.Configurator = ep =>
    {
        ep.PreProcessor<GlobalTrackerPreProcessor>(Order.Before);
    };
});

๐Ÿงช Verifying behavior

Testing Cache Hits

  • Open your application in a browser
  • Open Developer Tools (F12)
  • Navigate to the Network tab
  • Refresh the page

Cached responses will show:

  • Status: 304 Not Modified
  • Request Header: if-none-match (with ETag value)
  • Response Header: etag (current ETag)

Testing Cache Misses

To test the full request pipeline:

  • Open Developer Tools โ†’ Network tab
  • Check "Disable cache" in the toolbar
  • Refresh the page

This prevents the browser from sending if-none-match, forcing a cache miss and full server execution.

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 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.0.1 135 1/5/2026
1.0.0 123 1/4/2026