Enabling Hindsight
UseHindsight() turns Hindsight on for a context:
services.AddDbContext<AppDbContext>(options => options
.UseNpgsql(connectionString)
.UseHindsight());It does two things:
- it adds a history table to the EF Core model for every
IsTemporal()entity, so migrations create and evolve it; - it installs the history writer that fills those tables.
Options
services.AddDbContext<AppDbContext>(options => options
.UseNpgsql(connectionString)
.UseHindsight(hindsight => hindsight
.UseHistoryWriter(HistoryWriter.Trigger)
.WithChangeContext<HttpChangeContextProvider>()));| option | default | see |
|---|---|---|
UseHistoryWriter(HistoryWriter) | HistoryWriter.Interceptor | Choosing a history writer |
WithChangeContext<TProvider>() | none: the change-context columns are written NULL | Change context |
Use the trigger writer in production
The default writer is HistoryWriter.Interceptor for compatibility, but HistoryWriter.Trigger is the recommended one: it also records ExecuteUpdate, ExecuteDelete and raw SQL.
Without dependency injection
UseHindsight works on any DbContextOptionsBuilder, including the generic one, which it returns unchanged in type:
var options = new DbContextOptionsBuilder<AppDbContext>()
.UseNpgsql(connectionString)
.UseHindsight(hindsight => hindsight.UseHistoryWriter(HistoryWriter.Trigger))
.Options;
var db = new AppDbContext(options);PostgreSQL only
Hindsight supports the Npgsql provider only. The first time a context is used with Hindsight and any other provider (UseSqlite, UseSqlServer, …), it throws an InvalidOperationException that names the provider it found and the one it needs, rather than failing later in migrations or SQL generation.
Timestamps and TimeProvider
The two writers take the time of a change from different places:
HistoryWriter.Interceptortakes one timestamp perSaveChangesfrom theTimeProviderregistered in the application's service provider, orTimeProvider.Systemif there is none. Register a fake one to make history timestamps deterministic in tests:cs// HistoryWriter.Interceptor stamps history with the TimeProvider registered here, // for example a FakeTimeProvider in tests; without one it uses TimeProvider.System. services.AddSingleton(clock); services.AddDbContext<AppDbContext>(options => options .UseNpgsql(connectionString) .UseHindsight(hindsight => hindsight.UseHistoryWriter(HistoryWriter.Interceptor)));HistoryWriter.Triggeruses PostgreSQL'snow()and ignoresTimeProvider. In tests, assert on the shape of the periods (contiguous, half-open, one open version) or save in separate transactions.