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

适配SqlServer与Sqlite:EF Core复合主键自增的Sqlite触发器实现求助

解决Sqlite下复合主键内SectionId按Document自增的问题

我正好踩过这个Sqlite的坑,它确实不支持复合主键里的字段自增,但用触发器完全能实现你要的「每个Document实例对应的SectionId按序自增」需求,下面是具体的落地步骤:

1. 调整EF Core实体配置

先区分SqlServer和Sqlite的配置逻辑:SqlServer继续用原生自增,Sqlite则关闭自动自增,交给触发器处理。在DbContext的OnModelCreating方法里添加如下配置:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    // 配置复合主键
    modelBuilder.Entity<DocumentSection>()
        .HasKey(ds => new { ds.DocumentId, ds.SectionId });

    // SqlServer环境下,SectionId沿用自增特性
    modelBuilder.Entity<DocumentSection>()
        .Property(ds => ds.SectionId)
        .UseIdentityColumn()
        .HasAnnotation("SqlServer:Identity", "1, 1");

    // Sqlite环境下,关闭自动自增,由触发器接管
    modelBuilder.Entity<DocumentSection>()
        .Property(ds => ds.SectionId)
        .HasAnnotation("Sqlite:Autoincrement", false);
}

2. 编写Sqlite触发器

我们需要一个触发器,在插入DocumentSection时,自动计算当前DocumentId对应的最大SectionId,再加1作为新的SectionId值。触发器SQL代码如下:

CREATE TRIGGER IF NOT EXISTS Trigger_DocumentSection_SectionId_AutoIncrement
AFTER INSERT ON DocumentSection
FOR EACH ROW
WHEN NEW.SectionId IS NULL OR NEW.SectionId = 0
BEGIN
    UPDATE DocumentSection
    SET SectionId = (
        SELECT IFNULL(MAX(SectionId), 0) + 1
        FROM DocumentSection
        WHERE DocumentId = NEW.DocumentId
    )
    WHERE rowid = NEW.rowid;
END;

触发器逻辑说明:

  • AFTER INSERT:在插入操作完成后执行触发器
  • WHEN条件:仅当插入的SectionId为null或0时触发,保留手动指定SectionId的灵活性
  • 用Sqlite自带的rowid定位刚插入的行(所有Sqlite表默认都有这个内部行标识符)
  • IFNULL(MAX(SectionId), 0)处理该Document还没有任何Section的情况,此时从1开始自增

3. 通过EF Core迁移部署触发器

我们可以借助EF Core迁移,自动在Sqlite环境下创建触发器:

  1. 生成空迁移文件:
dotnet ef migrations add AddDocumentSectionAutoIncrementTrigger
  1. 打开生成的迁移文件,修改Up和Down方法:
protected override void Up(MigrationBuilder migrationBuilder)
{
    // 仅在Sqlite环境下创建触发器
    if (migrationBuilder.ActiveProvider == "Microsoft.EntityFrameworkCore.Sqlite")
    {
        migrationBuilder.Sql(@"
            CREATE TRIGGER IF NOT EXISTS Trigger_DocumentSection_SectionId_AutoIncrement
            AFTER INSERT ON DocumentSection
            FOR EACH ROW
            WHEN NEW.SectionId IS NULL OR NEW.SectionId = 0
            BEGIN
                UPDATE DocumentSection
                SET SectionId = (
                    SELECT IFNULL(MAX(SectionId), 0) + 1
                    FROM DocumentSection
                    WHERE DocumentId = NEW.DocumentId
                )
                WHERE rowid = NEW.rowid;
            END;
        ");
    }
}

protected override void Down(MigrationBuilder migrationBuilder)
{
    // 回滚迁移时删除触发器
    if (migrationBuilder.ActiveProvider == "Microsoft.EntityFrameworkCore.Sqlite")
    {
        migrationBuilder.Sql("DROP TRIGGER IF EXISTS Trigger_DocumentSection_SectionId_AutoIncrement;");
    }
}
  1. 执行迁移,将触发器部署到数据库:
dotnet ef database update

4. 实际使用方式

插入DocumentSection时,只需要设置DocumentId,SectionId留空或设为0即可,触发器会自动填充正确的自增值:

var newSection = new DocumentSection
{
    DocumentId = existingDocument.Id
    // SectionId 可以不赋值,或者设为0
};

context.DocumentSections.Add(newSection);
await context.SaveChangesAsync();

// 此时newSection.SectionId已经是当前Document下的最新自增值
Console.WriteLine($"新Section的编号: {newSection.SectionId}");

注意事项

  • 并发场景:Sqlite默认使用串行化事务隔离级别,多个请求同时插入同一个Document的Section时,不会出现重复SectionId的问题
  • 手动指定SectionId:如果需要自定义SectionId,只要给一个非0的值,触发器就不会执行,保留你设置的数值

内容的提问来源于stack exchange,提问作者Michael Schnerring

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 07:57:20