TGViewer
.NET Разработчик .NET Разработчик @netdeveloperdiary · 6.75K subscribers
Post #3217 1.44K
День 2687. #ЗаметкиНаПолях
Темпоральные таблицы в EF Core. Часть 2

Часть 1

Настройка темпоральных таблиц в EF Core
Системные требования:
- .NET 6+
- EF Core 6+ (Microsoft.EntityFrameworkCore.SqlServer)
- SQL Server 2016+ (или Azure SQL Database)

1. Определяем сущности
// Models/Order.cs
public class Order
{
public int Id { get; set; }
public int CustomerId { get; set; }
// …

public Customer Customer { get; set; } = null!;
public List<OrderItem> Items { get; set; } = [];
}

// Models/OrderItem.cs
public class OrderItem
{
public int Id { get; set; }
public int OrderId { get; set; }
public string Product { get; set; } = "";
public int Quantity { get; set; }
public decimal Price { get; set; }

public Order Order { get; set; } = null!;
}

Обратите внимание, что сами сущности не имеют столбцов аудита — это исключительно вопрос конфигурации уровня данных.

2. Настраиваем DbContext
// Data/AppDbContext.cs
public class AppDbContext : DbContext
{
public DbSet<Order> Orders => Set<Order>();
public DbSet<OrderItem> OrderItems => Set<OrderItem>();
public DbSet<Customer> Customers => Set<Customer>();

//…

protected override void
OnModelCreating(ModelBuilder mb)
{
mb.Entity<Order>(e =>
{
e.ToTable("Orders", x => x.IsTemporal(t =>
{
t.HasPeriodStart("ValidFrom");
t.HasPeriodEnd("ValidTo");
t.UseHistoryTable("OrdersHistory", "audit");
}));

e.Property(o => o.TotalAmount)
.HasColumnType("decimal(18,2)");

e.Property(o => o.Status)
.HasMaxLength(50);
});

mb.Entity<OrderItem>(e =>
{
e.ToTable("OrderItems", x => x.IsTemporal(t =>
{
t.HasPeriodStart("ValidFrom");
t.HasPeriodEnd("ValidTo");
t.UseHistoryTable("OrderItemsHistory", "audit");
}));

e.Property(i => i.UnitPrice)
.HasColumnType("decimal(18,2)");
});
}
}

Несколько нюансов:
- схема audit - таблицы истории размещены в отдельной схеме, что позволяет сохранить основную схему чистой и упрощает управление правами доступа (например, читать audit могут только администраторы);
- явное указание имён столбцов периода - ValidFrom/ValidTo — общепринятые имена, но вы можете использовать PeriodStart/PeriodEnd, если хотите;
- и Order и OrderItem, являются временными - нам нужна полная картина.

3. Создаём миграцию
dotnet ef migrations add AddTemporalTables
dotnet ef database update

SQL Server сгенерирует что-то вроде этого:
sql 
CREATE TABLE [Orders] (
[Id] INT NOT NULL IDENTITY,
[CustomerId] INT NOT NULL,
…
[ValidFrom] DATETIME2 GENERATED ALWAYS AS ROW START NOT NULL,
[ValidTo] DATETIME2 GENERATED ALWAYS AS ROW END NOT NULL,
PERIOD FOR SYSTEM_TIME ([ValidFrom], [ValidTo]),
CONSTRAINT [PK_Orders] PRIMARY KEY ([Id])
)
WITH (SYSTEM_VERSIONING = ON (
HISTORY_TABLE = [audit].[OrdersHistory]
));


Продолжение следует…

Источник:
https://thecodeman.net/posts/temporal-tables-efcore-auditing-history
  • 👍 10
  • 👎 1
More from @netdeveloperdiary
  1. Sep 27, 2026День 2797. #ЗаметкиНаПолях #AI Рабочий процесс с Copilot для .NET. Окончание Начало Продол…
  2. Sep 26, 2026День 2796. #ЗаметкиНаПолях #AI Рабочий процесс с Copilot для .NET. Продолжение Начало Три…
  3. Sep 25, 2026День 2795. #ЗаметкиНаПолях #AI Рабочий процесс с Copilot для .NET. Начало Проблема с позиц…
  4. Sep 24, 2026День 2794. #Оффтоп #Здоровье Сегодня будет необычный пост. Завтра в Москве стартует конфер…
  5. Sep 23, 2026День 2793. #ЗаметкиНаПолях #SQL 10 Редких Возможностей SQL, Которые Стоит Знать Каждому. Ч…
  6. Sep 22, 2026День 2792. #ЗаметкиНаПолях #SQL 10 Редких Возможностей SQL, Которые Стоит Знать Каждому. Ч…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →