26

我有一個MVC4應用程序,我最近升級到實體框架5,我試圖超過我們的移動數據庫使用遷移從開發風格的初始遷移和單一的種子刪除並創建每個運行。實體框架5遷移:建立數據庫

這是我在我的應用程序啓動功能中所做的。

protected void Application_Start() 
{ 
    Database.SetInitializer(
     new MigrateDatabaseToLatestVersion< MyContext, Configuration >()); 
    ... 
} 

我跑我的庫項目Enable-Migrations命令,我認爲這將創建一個初始遷移文件,但它創建的唯一文件是Configuration

當我刪除它創建它通過預期數據庫先編碼,然後從配置文件中播種數據庫。在配置文件中我改變了所有的Add()功能AddOrUpdate()

但是它的網站啓動,並重復所有的種子數據一次又一次,每次運行種子功能在我Configuration文件。

我想到的是,當我讀博客暗示,它將和我可以把在那裏的種子數據將創建一個initial migration文件,但它並沒有

任何人都可以解釋我是如何應建立DB在代碼中,它只能種一次?


LINK: The migrations blog post I followed


雖然這是使用EF migrate.exe因爲我已經轉向使用roundhouse運行遷移挺有意思的。我仍然使用EF來支持基於模型的遷移,但是我編寫了一個控制檯應用程序來將遷移寫入SQL文件。然後我使用roundhouse通過我的rake構建腳本執行遷移。還有一點過程涉及到,但它比使用EF在應用程序啓動時即時執行遷移時更穩定。

回答

38

這已被證明是一個受歡迎的帖子,所以我根據其他人的反饋進行了更新。要知道的主要事情是Configuration類中的Seed方法在應用程序每次啓動時運行,這不是模板方法中的註釋意味着什麼。從微軟的人那裏得到的答案是post,這是爲什麼 - 這要歸功於Jason Learmouth的發現。

如果你像我一樣只想運行數據庫更新,如果有任何掛起的遷移,那麼你需要做更多的工作。您可以通過調用migrator.GetPendingMigrations()來發現是否存在掛起的遷移,但是您必須在ctor中執行此操作,因爲在調用Seed方法之前清除掛起的遷移列表。要實現這一點,它出現在Migrations.Configuration類的代碼如下:

internal sealed class Configuration : DbMigrationsConfiguration<YourDbContext> 
{ 
    private readonly bool _pendingMigrations; 

    public Configuration() 
    { 
     // If you want automatic migrations the uncomment the line below. 
     //AutomaticMigrationsEnabled = true; 
     var migrator = new DbMigrator(this); 
     _pendingMigrations = migrator.GetPendingMigrations().Any(); 
    } 

    protected override void Seed(MyDbContext context) 
    { 
     //Microsoft comment says "This method will be called after migrating to the latest version." 
     //However my testing shows that it is called every time the software starts 

     //Exit if there aren't any pending migrations 
     if (!_pendingMigrations) return; 

     //else run your code to seed the database, e.g. 
     context.Foos.AddOrUpdate(new Foo { bar = true}); 
    } 
} 

我應該指出的是,有些人建議把種子代碼在實際的「向上」遷移代碼。這是有效的,但意味着您需要記住將種子代碼放入每個新的遷移中,並記住它很難記住,所以我不會那樣做。但是,如果你的種子隨着每次遷移而改變,那麼這可能是一個好的途徑。

+0

我猜它每次都在運行,因爲那是他們唯一可以做到的,而實際上並不需要在實際遷移中實施種子的方法。如果你的種子代碼位於遷移的Up()內部,它只會運行一次,這對我來說更直觀,因爲通常你做了一些數據庫更改,並希望將一些數據種入模式的新部分 – FRoZeN

+0

如果您沒有任何遷移,您的種子方法永遠不會使用此方法運行。我現在也更喜歡在遷移中進行播種 - 這種方式我知道它只會運行一次。 – oldwizard

+0

嗨pcguru。實際上,您始終進行遷移,因爲初始數據庫被認爲是遷移,即從無數據庫遷移到擁有數據庫。但是我可以看到在移民中擁有種子的好處。 –

2

這也是我過去想過的。我在我的數據庫中有一些表被填充到我的Seed事件中,現在我只是檢查它們中的一個在Seed方法中是否爲空。如果有行,則Seed方法不會運行。不是絕對可靠的,但有訣竅。

+1

這工作,以防止數據重複,但似乎有點冒險的解決方案添加退出條款。我會把這個問題留給我幾天,看看有沒有更好的方法去做 – Neil

+0

我同意這是一個hacky的解決方案 - 它不是退出,更多的是「如果沒有行,然後插入一些」。然後,您可以將其重複用於其他表格,這些表格可能會在遷移中填充。 – Richard

6

您可以手動添加遷移並使用任何您想要的種子代碼來填充它。在包管理器控制檯運行:

Add-Migration [Name] 

然後,您可以編輯在您的migrations文件夾中爲您創建的文件。

在我的項目中,我實際上像理查德一樣接種,儘管在上下文配置的Seed方法中。我真的沒有偏好。但遷移應該更有效率,因爲應用程序啓動時應用程序不需要檢查數據庫中是否存在行。只需要檢查遷移是否已經運行,這應該更快。

internal sealed class Configuration : DbMigrationsConfiguration<MyContext> 
{ 
    public Configuration() 
    { 
     // If you want automatic migrations as well uncomment below. 
     // You can use both manual and automatic at the same time, but I don't recommend it. 
     //AutomaticMigrationsEnabled = true; 
     //AutomaticMigrationDataLossAllowed = true; 
    } 

    protected override void Seed(MyContext context) 
    { 
     // This method will be called after migrating to the latest version. 

     // You can use the DbSet<T>.AddOrUpdate() helper extension method 
     // to avoid creating duplicate seed data. 

     context.FontFamilies.AddOrUpdate(
      f => f.Id, 
      new FontFamily { Id = 1, PcName = "Arial" }, 
      new FontFamily { Id = 2, PcName = "Times New Roman" }, 
     }); 

我在Global中使用了這個。asax:

public class MvcApplication : System.Web.HttpApplication 
{ 
    protected void Application_Start() 
    { 
     // Any migrations that haven't been applied before will 
     // automatically be applied on Application Pool restart 

     Database.SetInitializer<MyContext>(
      new MigrateDatabaseToLatestVersion<MyContext, 
      MyApp.Migrations.Configuration>() 
     ); 
    } 
} 
+0

這應該被標記爲答案。提示是帶有主鍵檢查的「AddOrUpdate」重載。我第一次沒有認出它.. – Sven

2

this SO question的答案解釋了爲什麼Seed每次運行應用程序時都會運行。

我用喬恩·史密斯的方法,但我已經把檢查未決遷移語句的#if塊這樣的:

#if (!DEBUG) 
      if (!_pendingMigrations) return; 
#endif 

當我調試種子法這樣,總是迫不及待地重新填充我的種子數據 - 當我在測試過程中刪除時很有用,但是在發佈時我沒有獲得perf perf。