2017-03-15 135 views
5

添加一個新的一對一關係到我的表之一後,我無法弄清楚如何爲我的數據庫中的現有行添加默認數據。在EF Core中添加一對一關係時遷移數據?

我的數據庫基本上都是這個樣子升級之前:

-- Team -- 
    Name: TEXT 

-- History -- 
    Id: INT 

...其中,歷史是有外鍵指出,它與其他不相關的表。

在我升級我基本上要一個團隊,有一個單一的歷史,所以我的新的數據庫看起來像:

-- Team -- 
    Name: TEXT 
    HistoryId: INT 

-- History -- 
    Id: INT 

我的問題了,但是是我存在於我的數據庫團隊,他們需要有獨特的歷史記錄行指向,所以我需要爲每個現有的團隊創建一個新的歷史記錄行。

我嘗試在我的遷移中的Up方法中手動添加條目,但由於我的模型與現有模式不匹配,因此失敗。

protected override void Up(MigrationBuilder migrationBuilder) 
{ 
    migrationBuilder.AddColumn<int>(
     name: "HistoryId", 
     table: "Team", 
     nullable: false, 
     defaultValue: 0); 

    using (var db = new XMDBContext()) 
    { 
     foreach (var team in db.Team) 
      team.History = new XMHistory(); 
     db.SaveChanges(); 
    } 

    migrationBuilder.CreateIndex(
     name: "IX_Team_HistoryId", 
     table: "Team", 
     column: "HistoryId", 
     unique: true); 

    migrationBuilder.AddForeignKey(
     name: "FK_Team_History_HistoryId", 
     table: "Team", 
     column: "HistoryId", 
     principalTable: "History", 
     principalColumn: "Id", 
     onDelete: ReferentialAction.Cascade); 
} 

回答

4

不幸的是,目前EF Core不支持從遷移種子數據。該問題在其存儲庫中被追蹤爲#629 - Seed Data

我目前看到的唯一解決方案是通過MigrationBuilder.Sql方法使用舊的優秀SQL。不幸的是,沒有訪問數據庫提供者服務,所以下一個適用於SQL Server(儘管我試圖只使用標準的SQL命令)。

讓原來的模式如下:

public class Team 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
} 

public class History 
{ 
    public int Id { get; set; } 
} 

加入FK後從TeamHistory它變成:產生

public class Team 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
    public int HistoryId { get; set; } 
    public History History { get; set; } 
} 

自動遷移:

protected override void Up(MigrationBuilder migrationBuilder) 
{ 
    migrationBuilder.AddColumn<int>(
     name: "HistoryId", 
     table: "Team", 
     nullable: false, 
     defaultValue: 0); 

    migrationBuilder.CreateIndex(
     name: "IX_Team_HistoryId", 
     table: "Team", 
     column: "HistoryId"); 

    migrationBuilder.AddForeignKey(
     name: "FK_Team_History_HistoryId", 
     table: "Team", 
     column: "HistoryId", 
     principalTable: "History", 
     principalColumn: "Id", 
     onDelete: ReferentialAction.Cascade); 
} 

現在,之前我們手動插入的CreateIndex命令如下:

migrationBuilder.AddColumn<int>(
    name: "TeamId", 
    table: "History", 
    nullable: true); 

migrationBuilder.Sql(@"insert into History (TeamId) select Id from Team"); 

migrationBuilder.Sql(@"update Team set HistoryId = (select Id from History where TeamId = Team.Id)"); 

migrationBuilder.DropColumn(
    name: "TeamId", 
    table: "History"); 

這個想法很簡單。我們建立History表的臨時空列TeamId,插入新記錄中,Team表中相應TeamId每個記錄,然後使用TeamId列從History表作爲密鑰更新的TeamHistoryId列,最後刪除臨時專欄。

此時數據轉換已完成且可創建FK約束。

遠不是良好的做法,但可以用作解決方法。

編輯:Gert Arnold's comments之後,看起來像使用SQL塊是正確的路要走。唯一引起我關注的是如何編寫數據庫不可知和/或特定的SQL。當然,如果一個目標是一個特定的數據庫類型,那麼這個問題就不存在了。無論如何,如果需要處理不同的數據庫類型,總是可以使用標準SQL命令支持的所有目標數據庫結合具體if塊基於MigrationBuilder.ActiveProvider屬性。

+0

看起來不錯伊萬!我認爲這是唯一的出路。即使有Seed方法,它也不會有幫助,因爲它在*數據庫升級期間不會運行*。此場景總是需要一些中間數據庫狀態,因爲您無法將所需的FK添加到尚未存在的東西。 –

+0

@GertArnold我想你是對的。我正在考慮將遷移分爲兩部分,比如僅向模型添加可空屬性,生成遷移,種子(使用數據填充字段),然後將可空(null)更改爲不可空,並將導航屬性添加到模型中,以生成第二次遷移。所有這些如果在每次遷移結束時運行'Seed'方法。但是現在我不確定這是否可能,因爲在遷移期間實體不會相同。 –

+0

...所以可能是SQL是這樣的,只是希望有一些信息/助手(如'MigrationSqlGenerator'中使用'ISqlGenerationHelper')以編寫與數據庫不相關的SQL。或者直接在'MigrationSqlBuilder'中掛鉤的方法。因爲我們現在所有的都是'MigrationBuilder.ActiveProvider'字符串(當然比沒有更好:)。 –