EF Core 6 Code First如何启用仅追加账本表
EF Core Code First 配置Azure SQL单表账本类型实现方案
截至EF Core 8版本,官方没有提供原生特性(Attribute)或一级Fluent API直接映射Azure SQL账本表的LEDGER配置项,但可以通过扩展EF Core迁移生成逻辑的方式,完全实现按表指定可更新/仅追加账本类型的需求,不需要全库统一开启账本模式。
单表账本类型配置
方式1:通过自定义表注解+迁移SQL生成器自动生成建表语句
这个方案不需要手动修改每次生成的迁移文件,配置一次后全局生效:
- 重写SQL Server迁移SQL生成器,识别自定义的表注解,在
CREATE TABLE语句末尾自动追加账本配置的WITH子句:
public class AzureSqlLedgerMigrationsSqlGenerator : SqlServerMigrationsSqlGenerator { public AzureSqlLedgerMigrationsSqlGenerator( MigrationsSqlGeneratorDependencies dependencies, IRelationalAnnotationProvider migrationsAnnotations) : base(dependencies, migrationsAnnotations) { } protected override void Generate( CreateTableOperation operation, IModel model, MigrationCommandListBuilder builder, bool terminate = true) { base.Generate(operation, model, builder, terminate: false); var ledgerOption = operation.FindAnnotation("SqlServer:LedgerOption")?.Value as string; if (ledgerOption != null) { if (ledgerOption == "AppendOnly") { builder.Append(" WITH (LEDGER = ON (APPEND_ONLY = ON))"); } else if (ledgerOption == "Updatable") { builder.Append(" WITH (LEDGER = ON (APPEND_ONLY = OFF))"); } } if (terminate) { builder.AppendLine(";"); } } }
- 在DbContext配置中注册这个自定义生成器,替换默认的SQL Server迁移生成器:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseSqlServer( "你的Azure SQL数据库连接字符串", b => b.MigrationsAssembly(typeof(YourDbContext).Assembly.FullName)) .ReplaceService<IMigrationsSqlGenerator, AzureSqlLedgerMigrationsSqlGenerator>(); }
- 在
OnModelCreating中给需要配置账本的表添加对应注解即可,不需要配置账本的普通表不加注解就会按普通表创建:
protected override void OnModelCreating(ModelBuilder modelBuilder) { // 配置KeyCardEvents为仅追加账本表 modelBuilder.Entity<KeyCardEvent>() .ToTable(t => t.HasAnnotation("SqlServer:LedgerOption", "AppendOnly")); // 配置其他业务表为可更新账本表 modelBuilder.Entity<Order>() .ToTable(t => t.HasAnnotation("SqlServer:LedgerOption", "Updatable")); }
方式2:自定义特性标记实体,实现注解式配置
如果你希望直接在实体类上用Attribute标记账本类型,可以自己实现一个简单的特性,配合模型扫描逻辑自动完成注解配置:
- 定义账本表特性和类型枚举:
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] public class LedgerTableAttribute : Attribute { public LedgerTableType TableType { get; } public LedgerTableAttribute(LedgerTableType tableType) => TableType = tableType; } public enum LedgerTableType { AppendOnly, Updatable }
- 在实体类上直接标记:
[LedgerTable(LedgerTableType.AppendOnly)] public class KeyCardEvent { public int Id { get; set; } public DateTime Timestamp { get; set; } }
- 在
OnModelCreating中加一段全局扫描逻辑,自动给标记了特性的实体添加对应注解:
protected override void OnModelCreating(ModelBuilder modelBuilder) { foreach (var entityType in modelBuilder.Model.GetEntityTypes()) { var ledgerAttr = entityType.ClrType.GetCustomAttribute<LedgerTableAttribute>(); if (ledgerAttr == null) continue; var optionValue = ledgerAttr.TableType == LedgerTableType.AppendOnly ? "AppendOnly" : "Updatable"; entityType.SetAnnotation("SqlServer:LedgerOption", optionValue); } }
配合前面写的自定义迁移生成器,后续新增实体只要加特性就能自动生成对应账本类型的建表语句,不需要额外写Fluent API配置。
配置__EFMigrationsHistory为仅追加账本表
迁移历史表本身只有插入操作,完全适配仅追加账本表的场景,有两种稳定的实现方式:
- 方式1:直接在之前写的自定义迁移生成器里加判断逻辑,检测到创建的表是
__EFMigrationsHistory时,自动追加仅追加账本的配置,不需要修改迁移文件,逻辑和处理普通表的账本配置一致。 - 方式2:在项目的首次初始迁移的
Up方法最开头,提前创建仅追加类型的迁移历史表,EF Core检测到表已存在就不会重复创建:
protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.Sql(@" IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = '__EFMigrationsHistory' AND schema_id = SCHEMA_ID('dbo')) BEGIN CREATE TABLE [dbo].[__EFMigrationsHistory] ( [MigrationId] NVARCHAR(150) NOT NULL PRIMARY KEY, [ProductVersion] NVARCHAR(32) NOT NULL ) WITH (LEDGER = ON (APPEND_ONLY = ON)); END"); // 后续保留迁移自动生成的其他建表逻辑即可 }
注意事项
- 不要在数据库级别开启默认账本配置(即不要执行
ALTER DATABASE 你的库名 SET LEDGER = ON),否则所有未单独配置的新表会默认变成可更新账本表,不符合按需指定的需求。 - 已经创建完成的普通表无法直接通过上述配置修改为账本表,需要按照Azure SQL的官方要求编写单独的迁移SQL完成转换。
- 可更新账本表的关联历史表、账本视图会由Azure SQL自动创建,不需要在EF Core模型中定义对应实体。
内容的提问来源于stack exchange,提问作者zawisza
相关产品推荐
相关产品推荐

