You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

EF Core 6 Code First如何启用仅追加账本表

EF Core Code First 配置Azure SQL单表账本类型实现方案

截至EF Core 8版本,官方没有提供原生特性(Attribute)或一级Fluent API直接映射Azure SQL账本表的LEDGER配置项,但可以通过扩展EF Core迁移生成逻辑的方式,完全实现按表指定可更新/仅追加账本类型的需求,不需要全库统一开启账本模式。

单表账本类型配置

方式1:通过自定义表注解+迁移SQL生成器自动生成建表语句

这个方案不需要手动修改每次生成的迁移文件,配置一次后全局生效:

  1. 重写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(";");
        }
    }
}
  1. 在DbContext配置中注册这个自定义生成器,替换默认的SQL Server迁移生成器:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    optionsBuilder.UseSqlServer(
        "你的Azure SQL数据库连接字符串",
        b => b.MigrationsAssembly(typeof(YourDbContext).Assembly.FullName))
        .ReplaceService<IMigrationsSqlGenerator, AzureSqlLedgerMigrationsSqlGenerator>();
}
  1. 在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标记账本类型,可以自己实现一个简单的特性,配合模型扫描逻辑自动完成注解配置:

  1. 定义账本表特性和类型枚举:
[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
}
  1. 在实体类上直接标记:
[LedgerTable(LedgerTableType.AppendOnly)]
public class KeyCardEvent
{
    public int Id { get; set; }
    public DateTime Timestamp { get; set; }
}
  1. 在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 09:51:46