簡體   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