如何解决PostgreSQL自增标识列在EF Core中的重复键异常
问题背景
我们使用EF Core进行数据库操作,同时需要将EF Core之前版本的数据库升级至最新版本。PostgreSQL中存在如下表结构:
CREATE TABLE "DatabaseVersion" ( "DatabaseVersionId" integer NOT NULL GENERATED BY DEFAULT AS IDENTITY ( INCREMENT 1 START 1 MINVALUE 1 MAXVALUE 2147483647 CACHE 1 ) PRIMARY KEY, "Version" varchar(20) NOT NULL, "VersionDate" timestamptz NULL DEFAULT Now(), "Description" varchar(2000) NULL, "AppliedDate" timestamptz NULL DEFAULT Now() );
在PgAdmin中创建数据库及该表后,执行插入测试语句:
INSERT INTO "DatabaseVersion" ("Version") VALUES ('1.0.0.0');
可成功插入第一行,修改版本号后可继续插入,每行的DatabaseVersionId均保持唯一。
异常现象
通过EF Core执行升级并插入数据时,抛出异常:
duplicate key value violates unique constraint
排查细节
创建新数据库查看EF Core生成的键配置,其定义为:
GENERATED BY DEFAULT AS IDENTITY (INCREMENT 1 START 1 MINVALUE 1 MAXVALUE 2147483647 CACHE 1)
已为该字段添加主键约束,避免在旧升级脚本中重复添加大量主键约束。
EF Core侧的更新代码如下(T对应DatabaseVersion实体):
var dbSet = context.Set<T>(); var id = GetKey(context, item); if (id == 0) { dbSet.Add(item); } else { var p = dbSet.Find(id); dbSet.Attach(p); var a = context.Entry(p); if (a != null) a.CurrentValues.SetValues(item); } context.SaveChanges();
我们的数据库升级流程为:先通过脚本升级到特定版本,再逐个执行EF Core程序化迁移。EF Core会自动跳过初始创建迁移,并在__EFMigrationsHistory表中添加记录,这部分逻辑正常。
解决方案
经排查发现问题根源:在数据库创建或升级过程中,若通过脚本插入了显式指定主键值的数据,PostgreSQL不会自动更新自增序列的当前值。当EF Core后续尝试插入新数据时,自增序列仍从初始值(1)开始生成,从而触发重复键冲突。
解决方法是在数据库创建或升级完成后,执行以下SQL脚本重置所有自增序列的当前值,使其与表中现有主键的最大值同步:
SELECT setval( (SELECT pg_get_serial_sequence( '"<SchemaName>"."<TableName>"', '<KeyName>')), (SELECT MAX("<KeyName>") from "<SchemaName>"."<TableName>"), true );
需遍历所有包含自增主键的表,替换脚本中的<SchemaName>(模式名)、<TableName>(表名)、<KeyName>(主键字段名)后执行。
内容的提问来源于stack exchange,提问作者dgxhubbard

