Temporal entities
An entity becomes temporal with one call in OnModelCreating:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Policy>().IsTemporal();
}The next dotnet ef migrations add creates its history table. See What a migration creates.
Defaults
| setting | default |
|---|---|
| history table | <main table>_history, in the main table's schema |
| period columns | valid_from, valid_to |
| versioned properties | every mapped property of the entity, including the properties of its complex and owned members |
Customizing
Pass a builder to IsTemporal to change any default:
modelBuilder.Entity<Policy>(policy =>
{
policy.ToTable("policies");
policy.IsTemporal(temporal => temporal
.UseHistoryTable("policy_versions", schema: "audit") // default: policies_history, same schema
.HasPeriodStart("sys_from") // default: valid_from
.HasPeriodEnd("sys_to") // default: valid_to
.Exclude(p => p.UpdatedAt)); // never copied to history
});| method | effect |
|---|---|
UseHistoryTable(name, schema) | Names the history table, and optionally its schema. Changing it later renames the table; see Renaming tables. |
HasPeriodStart(column), HasPeriodEnd(column) | Names the period columns. |
Exclude(property) | Leaves a property out of the history; see below. |
WithDbSessionUser() | Adds the db_session_user audit column; see Change context. |
WithRetention() | Allows pruning old history with PruneHistoryAsync; see Retention and partitioning. |
Excluding properties
Exclude removes a property from versioning completely:
- the history table has no column for it;
- a
SaveChangesthat changed only excluded properties writes no history row.
Use it for bookkeeping columns that change often and mean little on their own, such as UpdatedAt or a cached calculation. The sample project excludes Policy.UpdatedAt:
using Hindsight;
using Microsoft.EntityFrameworkCore;
namespace InsuranceSample;
public sealed class InsuranceDbContext(DbContextOptions<InsuranceDbContext> options) : DbContext(options)
{
public DbSet<Policy> Policies => Set<Policy>();
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Policy>(e =>
{
e.ToTable("policies");
e.Property(p => p.Status).HasConversion<string>();
e.IsTemporal(t => t.Exclude(p => p.UpdatedAt));
});
}
}You can exclude some properties of a composite primary key, but not all of them: Hindsight needs at least one key column to tell which history rows belong to which entity.
What can be temporal
A temporal entity must be a standalone entity type mapped to its own table, with a primary key. It can have complex properties and owned references stored in that table; their columns are versioned too. Owned collections, JSON-mapped members and inheritance hierarchies are rejected when the model is built. See Model validation for the full list and what to do instead.