28

我有一个 MVC4 应用程序,我最近升级到了 Entity Framework 5,我正试图将我们的数据库转移到使用从删除和创建每次运行的开发风格的迁移。

这是我在应用程序启动功能中所做的。

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

我在我的存储库项目上运行了该Enable-Migrations命令,我认为这会创建一个初始迁移文件,但是它创建的唯一文件是Configuration

当我删除数据库时,它首先通过代码按预期创建它,并从配置文件中为数据库播种。在配置文件中,我将所有Add()功能更改为AddOrUpdate()

但是,Configuration每次网站启动并一次又一次地复制所有种子数据时,它都会在我的文件中运行种子功能。

我想象它会创建一个initial migration文件,因为我读过的博客建议它会,我可以把种子数据放在那里,但它没有

谁能解释我应该如何在代码中设置数据库以便它只播种一次?


链接:我关注的迁移博客文章


虽然这对于使用 EF migrate.exe 非常有趣,但我已经切换到使用Roundhouse来运行迁移。我仍然使用 EF 来基于模型构建我的迁移,但我编写了一个小控制台应用程序来将迁移写入 SQL 文件。然后我使用 Roundhouse 通过我的 rake 构建脚本自己执行迁移。涉及的过程要多一些,但它比在应用程序启动时使用 EF 动态执行迁移要稳定得多。

4

4 回答 4

39

这已被证明是一个受欢迎的帖子,所以我根据其他人的反馈对其进行了更新。要知道的主要事情是 Configuration 类中的 Seed 方法在每次应用程序启动时都会运行,这不是模板方法中的注释所暗示的。请参阅 Microsoft 某人对这篇文章的回答,了解为什么会这样 - 感谢 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});
    }
}

我应该指出,有些人建议将种子代码放在实际的“向上”迁移代码中。这可行,但意味着您需要记住将种子代码放入每个新的迁移中,并且很难记住,所以我不会这样做。但是,如果您的种子随着每次迁移而改变,那么这可能是一个好方法。

于 2013-01-04T12:24:47.747 回答
6

您可以手动添加迁移并使用您想要的任何种子代码填充它?在包管理器控制台中运行:

Add-Migration [Name]

然后,您可以编辑在迁移文件夹中为您创建的文件。

在我的项目中,我实际上是在上下文配置的 Seed 方法中像 Richard 一样进行播种。我真的没有偏好。但是迁移应该更有效,因为应用程序不需要在应用程序启动时检查数据库中是否存在行。只需要检查迁移是否已经运行,应该会更快。

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>()
        );
    }
}
于 2012-11-22T13:02:57.163 回答
2

这也是我过去一直想知道的事情。我的数据库中有某些表在我的 Seed 事件中填充,现在我只需检查其中一个表在 Seed 方法中是否为空。如果有行,则 Seed 方法不会运行。并非万无一失,但确实有效。

于 2012-11-22T10:18:29.587 回答
2

这个 SO question的答案解释了为什么每次应用程序运行时 Seed 都会运行。

我使用 Jon Smiths 方法,但我已将检查待处理迁移语句放在 #if 块中,如下所示:

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

这样,当我调试 Seed 方法时,总是会运行以重新填充我的种子数据 - 当我在测试期间进行删除等时很有用,但在发布时我没有得到性能命中。

于 2013-05-19T12:11:45.623 回答