2017-06-06 83 views
0

我已經啓動了一個EF6項目來存儲分析儀器的測量結果。每臺儀器都有一個內置的PC和它自己的結果數據庫。Entity Framework 6代碼優先遷移 - 從CreateDatabaseIfNotExists初始化程序開始

最初,使用了數據庫初始化程序CreateDatabaseIfNotExists。創建數據庫時,它會在__MigrationHistory表中創建一個條目,其中包含一個非唯一的MigrationId條目(時間戳因儀器而異,例如201706011336597_InitialCreate),ContextKey(如果我的派生DbContext的完全限定類型)。

過了一段時間,決定將更多的結果數據添加到數據庫中......幸運的是,只需要三個新表格。現有表格中沒有更改。

爲此,我想使用MigrateDatabaseToLatestVersion初始值設定項。但我必須支持以下兩種情況:

  1. 具有非唯一MigrationId的現有數據庫必須使用三個新表遷移到擴展版本。
  2. 沒有數據庫,用MigrateDatabaseToLatestVersion初始化器創建數據庫。

我該怎麼做?

我已經使用初始DbContext中的add-migration PM控制檯命令創建了初始遷移。這與方案2(沒有數據庫存在)很相稱。從這個起點,我可以更新我的DbContext並使用三個新表創建一個新的遷移。

但是如何支持場景1?初始遷移的Up()方法包含表創建代碼,這不是必要的,因爲這些表已經存在。是一個空的遷移(add-migration -IgnoreChanges)有幫助,也許有比初始遷移更晚的時間戳?

注意:我無法從PM控制檯訪問目標數據庫,只能在我的開發人員計算機上訪問測試數據庫。

感謝和問候

卡斯滕

更新: 我已經修改了創建初始遷移與靜態標誌TablesAlreadyCreated

public partial class InitialMigraCreate : DbMigration 
    { 
    /// <summary> 
    /// Set this field to true, if the tables are already created by the 
    /// CreateDatabaseIfNotExists database initializer. Then, the Up() 
    /// and Down() methods do nothing, but the 
    /// migration is added to the __MigrationHistory table. 
    /// </summary> 
    public static bool TablesAlreadyCreated = false; 

    public override void Up() 
    { 
     if (TablesAlreadyCreated) 
     return; 

     // several CreateTable calls here 
    } 

    /// <inheritdoc/> 
    public override void Down() 
    { 
     if (TablesAlreadyCreated) 
     return; 

     // several Drop... calls here 
    } 
    } 

我還實施了一個新的數據庫初始化類,如下所示:

public class MigrateDatabaseToLatestVersionEx<TContext, TMigrationsConfiguration> : IDatabaseInitializer<TContext> 
    where TContext : DbContext 
    where TMigrationsConfiguration : DbMigrationsConfiguration<TContext>, new() 

{ 
    ... 

    /// <inheritdoc /> 
    public virtual void InitializeDatabase(TContext context) 
    { 
     if (context == null) 
     throw new ArgumentNullException("context"); 

     // check whether a first migration exists from the CreateDatabaseIfNotExists database initializer 
     var firstConfig   = new ConfigurationAutoCreatedDatabase(); 
     firstConfig.TargetDatabase = _config.TargetDatabase; 
     var firstMigrator   = new DbMigrator(firstConfig); 
     var firstDbMigrations  = firstMigrator.GetDatabaseMigrations(); 

     // create the default migrator with the current configuration 
     var migrator = new DbMigrator(_config); 

     if (1 == firstDbMigrations.Count(migra => migra.EndsWith("_InitialCreate", StringComparison.InvariantCultureIgnoreCase))) 
     { // This database was created using the CreateDatabaseIfNotExists database initializer. 
     // That's an indication whether it's an old database 
     // Do the custom migration here! 
     InitialMigraCreate.TablesAlreadyCreated = true; 

     migrator.Update(); 
     } 
     else 
     { // do the default migration the database was created with this MigrateDatabaseToLatestVersionEx initializer 
     InitialMigraCreate.TablesAlreadyCreated = false; 

     migrator.Update(); 
     } 
    } 
} 

它檢查,初始遷移條目是從CreateDatabaseIfNotExists初始化和禁用表創建/刪除通話在這種情況下,Up()/ Down()方法。 ConfigurationAutoCreatedDatabase是手動創建的派生類DbMigrationsConfiguration:

internal sealed class ConfigurationAutoCreatedDatabase : DbMigrationsConfiguration<MyNamespace.MyDbContext> 
{ 
    /// <summary> 
    /// Creates a <c>ConfigurationAutoCreated</c> object (default constructor). 
    /// </summary> 
    public ConfigurationAutoCreatedDatabase() 
    { 
     this.AutomaticMigrationsEnabled  = false; 
     this.AutomaticMigrationDataLossAllowed = false; 
     this.ContextKey      = "MyNamespace.MyDbContext"; 
    } 
} 

所以,它適用於這兩種方案。我希望能幫助其他有類似問題的人。有趣的是,如果該任務有一個開箱即用的EF工作流程。

回答

0

這是EF處理得很好的一種非常常見的情況。非遷移初始值設定項(CreateDatabaseIfNotExists等)應該在非常早期的開發中使用(當你不關心除種子之外的數據)。

切換到遷移後,您應該生成一個基線遷移,並根據您的指示獲取當前模型的快照(add-migration MyStartPoint -IgnoreChanges)。這增加了一個沒有Up()代碼的遷移,並將代碼的當前狀態存儲在第一個模型中,以便在更改模型時只反映這些更改。您可以通過註釋Up()代碼中存在的項目來完成同樣的事情。

現在,當您針對現有數據庫運行時,它將檢查__MigrationHistory以查看應用了哪些遷移。如果數據庫不存在,它將被創建。有關更多信息,請參閱herehere

不確定你在說什麼與MigrationId。除非您更改名稱空間,否則EF會自動處理該名稱空間(還有一種解決方法)。

+0

感謝您的回覆。我再次測試了工作流程,發現在使用PM控制檯命令** Enable-Migrations **啓用遷移時指定** - ContextTypeName **參數非常重要。結果,生成的Configuration類的'ContextKey'屬性用我們派生的'DbContext'的完全限定名初始化。否則,生成的「Configuration」類的'ContextKey'屬性將使用生成的'Configuration'類本身的全限定名進行初始化。然後,遷移不起作用。 – KBO

+0

解決方法之一是在構造函數中明確地給出一個contextkey。見[這裏](https://msdn.microsoft.com/en-us/magazine/dn948104.aspx) –

+0

你是對的,解釋(見給出的鏈接)澄清。我最後使用的** - ContextTypeName **參數具有類似的結果,即'DbMigrationsConfiguration'中的右邊的'ContextKey'屬性。問題是,'MigrateDatabaseToLatestVersion'類的默認行爲不適用於以前使用的'CreateDatabaseIfNotExists'類。現在它工作...謝謝 – KBO