EF PostgreSQL迁移脚本未生成CREATE TABLE IF NOT EXISTS的原因与修复方案
问题:EF Core生成PostgreSQL迁移脚本未包含
CREATE TABLE IF NOT EXISTS导致重复执行报错? 我在项目中编写了如下InitialCreate迁移类:
public partial class InitialCreate : Migration { protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.CreateTable( name: "Studios", columns: table => new { Id = table.Column<Guid>(nullable: false), Company = table.Column<string>(nullable: true) }, constraints: table => { table.PrimaryKey("PK_Studios", x => x.Id); }); } protected override void Down(MigrationBuilder migrationBuilder) { migrationBuilder.DropTable( name: "Studios"); } }
使用PostgreSQL(pgsql.EntityFrameworkCore.PostgreSQL),通过Script-Migration或dotnet ef migrations script生成的SQL文件内容如下:
CREATE TABLE IF NOT EXISTS "__EFMigrationsHistory" ( "MigrationId" character varying(150) NOT NULL, "ProductVersion" character varying(32) NOT NULL, CONSTRAINT "PK___EFMigrationsHistory" PRIMARY KEY ("MigrationId") ); START TRANSACTION; CREATE TABLE "Studios" ( "Id" uuid NOT NULL, "Company" text NULL, CONSTRAINT "PK_Studios" PRIMARY KEY ("Id") ); INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") VALUES ('20210202135422_InitialCreate', '7.0.13'); COMMIT;
该SQL仅能执行一次,第二次执行会报错:
ERROR: relation "Studios" already exists
请问为何生成的SQL未包含CREATE TABLE IF NOT EXISTS "Studios",该如何解决?
解答
为什么生成的SQL没有CREATE TABLE IF NOT EXISTS
EF Core的迁移机制默认是基于__EFMigrationsHistory迁移历史表追踪已执行的迁移,它的设计逻辑是每个迁移只会被执行一次。所以生成的脚本不会自动加IF NOT EXISTS——正常流程中,只要迁移历史表有该MigrationId的记录,这个迁移就不会再次执行,自然不会出现重复建表的问题。
而__EFMigrationsHistory表本身加了IF NOT EXISTS,是因为它是EF Core追踪迁移的基础表,必须确保首次执行能创建,后续执行不报错。
解决方法
如果需要生成可重复执行的脚本,有两种常用方案:
- 生成幂等脚本
使用迁移脚本命令时添加--idempotent(缩写-i)参数,命令示例:
# Package Manager Console Script-Migration -i # .NET CLI dotnet ef migrations script --idempotent
生成的脚本会自动加入IF NOT EXISTS判断,同时会先检查迁移历史表是否已有该迁移记录,只有未执行过的迁移才会运行对应SQL逻辑,可安全重复执行。
- 手动修改迁移类(不推荐)
如果不想用幂等参数,也可以在迁移类的Up方法中手动写判断逻辑,确保建表前先检查表是否存在:
protected override void Up(MigrationBuilder migrationBuilder) { // 手动编写带IF NOT EXISTS的SQL,替代原CreateTable调用 migrationBuilder.Sql(@" CREATE TABLE IF NOT EXISTS ""Studios"" ( ""Id"" uuid NOT NULL, ""Company"" text NULL, CONSTRAINT ""PK_Studios"" PRIMARY KEY (""Id"") ); "); }
这种方式需要手写SQL,会失去EF Core迁移的跨数据库兼容性,仅适合特殊场景使用。
内容的提问来源于stack exchange,提问作者Anna Melashkina
相关产品推荐
相关产品推荐

