EF Core Code First新增数据报Id重复 如何设置自增主键起始值
主键重复报错的核心原因是:你手动向Destinations表插入存量数据时,没有同步更新Id列绑定的自增序列当前值,序列生成的下一个Id小于表内已存在的最大主键,导致EF Core调用SaveChangesAsync插入新记录时,拿到重复Id触发主键唯一约束冲突。
你实体类中对Id配置的[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]特性本身没有问题,不需要修改实体定义和业务层的Create方法逻辑。
所有方案均不会删除或修改表现有存量数据,满足不丢失历史数据的要求。
1. 先确认表绑定的自增序列名称
PostgreSQL中EF Core Code First生成的int类型自增主键,默认绑定的序列名格式为"表名_列名_seq",你当前库中对应的序列就是public."Destinations_Id_seq",可以执行以下SQL确认:
SELECT column_default FROM information_schema.columns WHERE table_name = 'Destinations' AND column_name = 'Id';
返回结果格式为nextval('"Destinations_Id_seq"'::regclass)即可确认序列名称正确。
2. 分环境设置自增起始值
自增序列调用nextval时会先累加1再返回值,因此设置序列重启值时,需要比你预期的第一条新记录Id小1:
- 本地环境新Id从301开始,序列重启值设为300
- 开发环境新Id从201开始,序列重启值设为200
- 生产环境新Id从241开始,序列重启值设为240
你可以根据项目实际情况选以下任意一种方式配置:
方式一:代码层适配多环境(推荐长期维护使用)
通过配置文件+EF Core模型配置实现环境自动适配,后续发布无需手动操作数据库:
- 在不同环境的配置文件
appsettings.{环境名}.json中加入对应配置项
开发环境对应值设为200,生产环境对应值设为240即可。// 本地appsettings.Development.json示例 { "DbSequence": { "DestinationIdRestartValue": 300 } } - 在DbContext中注入配置,重写
OnModelCreating方法指定自增列起始值private readonly IConfiguration _configuration; // 构造函数注入IConfiguration public CatalogDbContext(DbContextOptions<CatalogDbContext> options, IConfiguration configuration) : base(options) { _configuration = configuration; } protected override void OnModelCreating(ModelBuilder modelBuilder) { var restartValue = _configuration.GetValue<int>("DbSequence:DestinationIdRestartValue"); modelBuilder.Entity<Destination>() .Property(d => d.Id) // 第一个参数为自增起始值,第二个参数为自增步长固定为1 .UseIdentityColumn(restartValue + 1, 1); base.OnModelCreating(modelBuilder); } - 因为表已经存在,首次上线前需要在对应环境执行一次序列更新SQL,后续EF Core迁移会自动维护序列值,无需重复操作。
方式二:手动执行SQL快速修复(适合存量库紧急修复)
直接连接对应环境的数据库,执行对应环境的序列调整SQL即可,执行后立即生效:
-- 本地环境执行 ALTER SEQUENCE "Destinations_Id_seq" RESTART WITH 300; -- 开发环境执行 ALTER SEQUENCE "Destinations_Id_seq" RESTART WITH 200; -- 生产环境执行 ALTER SEQUENCE "Destinations_Id_seq" RESTART WITH 240;
3. 配置验证
执行完配置后,可以通过以下SQL查询序列下一个返回值,确认配置生效:
SELECT nextval('"Destinations_Id_seq"');
如果返回值+1等于你预期的第一条新记录Id,说明配置正确,后续新增Destination记录时会按设定值自动递增,不会再出现主键重复错误。
内容的提问来源于stack exchange,提问作者hanushi-thana

