Entity framework 核心迁移:将列从不可空更改为可空,然后将值设置为空
Entity framework core migration: Change column from non-nullable to nullable then set values to null
我决定将一列从不可空列更改为可空列。这里没问题。我将 属性 改为“?”在类型中 (DateTime
=> DateTime?
) 并且生成的迁移正常。
不过,我想将被视为“null”的值更改为真正的 null。意思是,将值 0001-01-01 00:00:00
更改为 null
.
我创建了一个扩展方法,可以正确生成 SQL 向上和向下代码。我在 Up
中的列更改之后和 Down
中之前执行了此操作。
执行 Up
时出现问题,因为我的扩展方法中的 SQL 在应用列更改的更改之前执行。
有没有办法在执行这条语句之前强制应用更改? (请参阅可以插入代码的代码中的 FIXME
。可能有两个位置。)
扩展名class
public static class MigrationsCorrections
{
public static OperationBuilder<SqlOperation> ChangeDateToNullable(this MigrationBuilder migrationBuilder, string table, string column)
{
//FIXME: Either having a call here this to force previous changes to be applied
return migrationBuilder.Sql($"UPDATE {table} SET {column} = null WHERE {column} = '0001-01-01 00:00:00';");
}
public static OperationBuilder<SqlOperation> ChangeDateToNotNullable(this MigrationBuilder migrationBuilder, string table, string column)
{
return migrationBuilder.Sql($"UPDATE {table} SET {column} = '0001-01-01 00:00:00' WHERE {column} = null;");
}
}
迁移class
// IMPORTATION OF THE EXTENSION CLASS
public partial class MetaPersonBirthDateNullable : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<DateTime>(
name: "BirthDay",
table: "MetaPersons",
type: "TEXT",
nullable: true,
oldClrType: typeof(DateTime),
oldType: "TEXT");
//FIXME: Either having a call here this to force previous changes to be applied
migrationBuilder.ChangeDateToNullable("MetaPersons", "BirthDay");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.ChangeDateToNotNullable("MetaPersons", "BirthDay");
migrationBuilder.AlterColumn<DateTime>(
name: "BirthDay",
table: "MetaPersons",
type: "TEXT",
nullable: false,
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified),
oldClrType: typeof(DateTime),
oldType: "TEXT",
oldNullable: true);
}
}
SQL 迁移生成
BEGIN TRANSACTION;
UPDATE MetaPersons SET BirthDay = null WHERE BirthDay = '0001-01-01 00:00:00';
CREATE TABLE "ef_temp_MetaPersons" (
"Id" INTEGER NOT NULL CONSTRAINT "PK_MetaPersons" PRIMARY KEY AUTOINCREMENT,
"BirthDay" TEXT NULL,
"DeathDay" TEXT NULL,
"ExternalId" TEXT NULL,
"MetaSource" TEXT NULL,
"Name" TEXT COLLATE NOCASE NULL,
"Professions" TEXT NULL
);
INSERT INTO "ef_temp_MetaPersons" ("Id", "BirthDay", "DeathDay", "ExternalId", "MetaSource", "Name", "Professions")
SELECT "Id", "BirthDay", "DeathDay", "ExternalId", "MetaSource", "Name", "Professions"
FROM "MetaPersons";
COMMIT;
PRAGMA foreign_keys = 0;
BEGIN TRANSACTION;
DROP TABLE "MetaPersons";
ALTER TABLE "ef_temp_MetaPersons" RENAME TO "MetaPersons";
COMMIT;
PRAGMA foreign_keys = 1;
BEGIN TRANSACTION;
CREATE UNIQUE INDEX "IX_MetaPersons_ExternalId_MetaSource" ON "MetaPersons" ("ExternalId", "MetaSource");
CREATE INDEX "IX_MetaPersons_Name" ON "MetaPersons" ("Name");
INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion")
VALUES ('20220309145323_MetaPerson.BirthDate Nullable', '6.0.2');
COMMIT;
正如我们所见,UPDATE
语句的顺序不正确
编辑 1
我尝试使用 MigrationOperation class 代替 Microsoft Custom Migration,但我得到...
'ChangeDateToNullable' 类型的操作将在待重建 table 'MetaPersons' 时尝试。数据库可能未处于预期状态。查看此迁移生成的 SQL 以帮助诊断任何故障。考虑将这些操作移至后续迁移。
这个特殊的解决方案是我认为的,进行第二次迁移,但这不是我想要的,因为这些更改是联系在一起的。
来自文档 Using MigrationBuilder.Sql():
Use the EXEC function when a statement must be the first or only one
in a SQL batch. It might also be needed to work around parser errors
in idempotent migration scripts that can occur when referenced columns
don't currently exist on a table.
所以最好创建两个迁移,一个用于 ALTER,另一个用于 UPDATE。
我解决了 MigrationOperations 方向的问题。我覆盖了 IMigrationsSqlGenerator
.
的生成方法
为了有一个通用的解决方案,我创建了一个新的 Database 项目,其中包括这个新的 MigrationsSqlGenerator
和 类 需要改变日期列值 from/to 为空。我还添加了两个 类 来执行任何遵守顺序的 SQL 代码,从而允许在更改列后更改列的值。而这一切,都在同一次迁移中。
请参阅 https://github.com/djon2003/com.cyberinternauts.csharp.Database 了解完整的解决方案。
和 https://www.codeproject.com/Articles/5327089/Executing-SQL-code-within-EntityFrameworkCore-migr 相关文章。
生成的最终 SQL 代码:
BEGIN TRANSACTION;
CREATE TABLE "ef_temp_MetaPersons" (
"Id" INTEGER NOT NULL CONSTRAINT "PK_MetaPersons" PRIMARY KEY AUTOINCREMENT,
"BirthDay" TEXT NULL,
"DeathDay" TEXT NULL,
"ExternalId" TEXT NULL,
"MetaSource" TEXT NULL,
"Name" TEXT COLLATE NOCASE NULL,
"Professions" TEXT NULL
);
INSERT INTO "ef_temp_MetaPersons" ("Id", "BirthDay", "DeathDay", "ExternalId", "MetaSource", "Name", "Professions")
SELECT "Id", "BirthDay", "DeathDay", "ExternalId", "MetaSource", "Name", "Professions"
FROM "MetaPersons";
COMMIT;
PRAGMA foreign_keys = 0;
BEGIN TRANSACTION;
DROP TABLE "MetaPersons";
ALTER TABLE "ef_temp_MetaPersons" RENAME TO "MetaPersons";
COMMIT;
PRAGMA foreign_keys = 1;
BEGIN TRANSACTION;
CREATE UNIQUE INDEX "IX_MetaPersons_ExternalId_MetaSource" ON "MetaPersons" ("ExternalId", "MetaSource");
CREATE INDEX "IX_MetaPersons_Name" ON "MetaPersons" ("Name");
UPDATE "MetaPersons" SET "BirthDay" = null WHERE "BirthDay" = '0001-01-01 00:00:00';
INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion")
VALUES ('20220309145323_MetaPerson.BirthDate Nullable', '6.0.2');
COMMIT;
可以看到,现在UPDATE语句在列修改之后
我决定将一列从不可空列更改为可空列。这里没问题。我将 属性 改为“?”在类型中 (DateTime
=> DateTime?
) 并且生成的迁移正常。
不过,我想将被视为“null”的值更改为真正的 null。意思是,将值 0001-01-01 00:00:00
更改为 null
.
我创建了一个扩展方法,可以正确生成 SQL 向上和向下代码。我在 Up
中的列更改之后和 Down
中之前执行了此操作。
执行 Up
时出现问题,因为我的扩展方法中的 SQL 在应用列更改的更改之前执行。
有没有办法在执行这条语句之前强制应用更改? (请参阅可以插入代码的代码中的 FIXME
。可能有两个位置。)
扩展名class
public static class MigrationsCorrections
{
public static OperationBuilder<SqlOperation> ChangeDateToNullable(this MigrationBuilder migrationBuilder, string table, string column)
{
//FIXME: Either having a call here this to force previous changes to be applied
return migrationBuilder.Sql($"UPDATE {table} SET {column} = null WHERE {column} = '0001-01-01 00:00:00';");
}
public static OperationBuilder<SqlOperation> ChangeDateToNotNullable(this MigrationBuilder migrationBuilder, string table, string column)
{
return migrationBuilder.Sql($"UPDATE {table} SET {column} = '0001-01-01 00:00:00' WHERE {column} = null;");
}
}
迁移class
// IMPORTATION OF THE EXTENSION CLASS
public partial class MetaPersonBirthDateNullable : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<DateTime>(
name: "BirthDay",
table: "MetaPersons",
type: "TEXT",
nullable: true,
oldClrType: typeof(DateTime),
oldType: "TEXT");
//FIXME: Either having a call here this to force previous changes to be applied
migrationBuilder.ChangeDateToNullable("MetaPersons", "BirthDay");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.ChangeDateToNotNullable("MetaPersons", "BirthDay");
migrationBuilder.AlterColumn<DateTime>(
name: "BirthDay",
table: "MetaPersons",
type: "TEXT",
nullable: false,
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified),
oldClrType: typeof(DateTime),
oldType: "TEXT",
oldNullable: true);
}
}
SQL 迁移生成
BEGIN TRANSACTION;
UPDATE MetaPersons SET BirthDay = null WHERE BirthDay = '0001-01-01 00:00:00';
CREATE TABLE "ef_temp_MetaPersons" (
"Id" INTEGER NOT NULL CONSTRAINT "PK_MetaPersons" PRIMARY KEY AUTOINCREMENT,
"BirthDay" TEXT NULL,
"DeathDay" TEXT NULL,
"ExternalId" TEXT NULL,
"MetaSource" TEXT NULL,
"Name" TEXT COLLATE NOCASE NULL,
"Professions" TEXT NULL
);
INSERT INTO "ef_temp_MetaPersons" ("Id", "BirthDay", "DeathDay", "ExternalId", "MetaSource", "Name", "Professions")
SELECT "Id", "BirthDay", "DeathDay", "ExternalId", "MetaSource", "Name", "Professions"
FROM "MetaPersons";
COMMIT;
PRAGMA foreign_keys = 0;
BEGIN TRANSACTION;
DROP TABLE "MetaPersons";
ALTER TABLE "ef_temp_MetaPersons" RENAME TO "MetaPersons";
COMMIT;
PRAGMA foreign_keys = 1;
BEGIN TRANSACTION;
CREATE UNIQUE INDEX "IX_MetaPersons_ExternalId_MetaSource" ON "MetaPersons" ("ExternalId", "MetaSource");
CREATE INDEX "IX_MetaPersons_Name" ON "MetaPersons" ("Name");
INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion")
VALUES ('20220309145323_MetaPerson.BirthDate Nullable', '6.0.2');
COMMIT;
正如我们所见,UPDATE
语句的顺序不正确
编辑 1
我尝试使用 MigrationOperation class 代替 Microsoft Custom Migration,但我得到...
'ChangeDateToNullable' 类型的操作将在待重建 table 'MetaPersons' 时尝试。数据库可能未处于预期状态。查看此迁移生成的 SQL 以帮助诊断任何故障。考虑将这些操作移至后续迁移。
这个特殊的解决方案是我认为的,进行第二次迁移,但这不是我想要的,因为这些更改是联系在一起的。
来自文档 Using MigrationBuilder.Sql():
Use the EXEC function when a statement must be the first or only one in a SQL batch. It might also be needed to work around parser errors in idempotent migration scripts that can occur when referenced columns don't currently exist on a table.
所以最好创建两个迁移,一个用于 ALTER,另一个用于 UPDATE。
我解决了 MigrationOperations 方向的问题。我覆盖了 IMigrationsSqlGenerator
.
为了有一个通用的解决方案,我创建了一个新的 Database 项目,其中包括这个新的 MigrationsSqlGenerator
和 类 需要改变日期列值 from/to 为空。我还添加了两个 类 来执行任何遵守顺序的 SQL 代码,从而允许在更改列后更改列的值。而这一切,都在同一次迁移中。
请参阅 https://github.com/djon2003/com.cyberinternauts.csharp.Database 了解完整的解决方案。
和 https://www.codeproject.com/Articles/5327089/Executing-SQL-code-within-EntityFrameworkCore-migr 相关文章。
生成的最终 SQL 代码:
BEGIN TRANSACTION;
CREATE TABLE "ef_temp_MetaPersons" (
"Id" INTEGER NOT NULL CONSTRAINT "PK_MetaPersons" PRIMARY KEY AUTOINCREMENT,
"BirthDay" TEXT NULL,
"DeathDay" TEXT NULL,
"ExternalId" TEXT NULL,
"MetaSource" TEXT NULL,
"Name" TEXT COLLATE NOCASE NULL,
"Professions" TEXT NULL
);
INSERT INTO "ef_temp_MetaPersons" ("Id", "BirthDay", "DeathDay", "ExternalId", "MetaSource", "Name", "Professions")
SELECT "Id", "BirthDay", "DeathDay", "ExternalId", "MetaSource", "Name", "Professions"
FROM "MetaPersons";
COMMIT;
PRAGMA foreign_keys = 0;
BEGIN TRANSACTION;
DROP TABLE "MetaPersons";
ALTER TABLE "ef_temp_MetaPersons" RENAME TO "MetaPersons";
COMMIT;
PRAGMA foreign_keys = 1;
BEGIN TRANSACTION;
CREATE UNIQUE INDEX "IX_MetaPersons_ExternalId_MetaSource" ON "MetaPersons" ("ExternalId", "MetaSource");
CREATE INDEX "IX_MetaPersons_Name" ON "MetaPersons" ("Name");
UPDATE "MetaPersons" SET "BirthDay" = null WHERE "BirthDay" = '0001-01-01 00:00:00';
INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion")
VALUES ('20220309145323_MetaPerson.BirthDate Nullable', '6.0.2');
COMMIT;
可以看到,现在UPDATE语句在列修改之后