繁体   English   中英

实体框架核心迁移:将列从不可空更改为可空,然后将值设置为 null

[英]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 ,但我得到...

表“MetaPersons”的重建挂起时,将尝试“ChangeDateToNullable”类型的操作。 数据库可能不在预期的 state 中。查看此迁移生成的 SQL 以帮助诊断任何故障。 考虑将这些操作移至后续迁移。

这个特定的解决方案是我认为的,进行第二次迁移,但这不是我想要的,因为这些更改是链接在一起的。

从文档Using MigrationBuilder.Sql()

当语句必须是 SQL 批处理中的第一个或唯一一个时,使用 EXEC function。 可能还需要解决幂等迁移脚本中的解析器错误,这些错误可能在表中当前不存在引用的列时发生。

所以最好创建两个迁移,一个用于 ALTER,另一个用于 UPDATE。

我解决了 MigrationOperations 方向的问题。 我覆盖了IMigrationsSqlGenerator的生成方法。

为了有一个通用的解决方案,我创建了一个新的数据库项目,其中包括这个新的MigrationsSqlGenerator以及将日期列值从 null 更改为/更改为 null 所需的类。我还添加了两个类来执行任何 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 语句在列更改之后

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM