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。