【问题标题】:Manually run Entity Framework migration Seed() method, including after Down() migration手动运行实体框架迁移 Seed() 方法,包括 Down() 迁移之后
【发布时间】:2016-03-05 19:51:22
【问题描述】:

是否有一种简单的方法可以从包管理器控制台手动运行实体框架迁移 Seed() 方法,包括在 Down() 迁移之后 (Update-Database -TargetMigration foo)? “Run Code First Migration Seed Method without a migration”的答案解决了如何手动运行 Seed() 方法,即调用Update-Database,但是当数据库处于较旧的迁移并且不应更新时,这不起作用。在“How to run Seed() method of Configuration class of migrations”中提出了相同的问题,但它是作为多部分问题的一部分提出的,这部分问题仍未得到解答(尽管整个问题现在都标记为已回答)。

我之所以问,是因为我有一些清理代码需要在应用迁移后运行。从 Seed() 调用它适用于 Up() 迁移。不过,我不知道如何轻松地将其称为 Down() 迁移。我想要一个可以从包管理器中工作的简单的一两个班轮。我知道我可以在为所有必要的 DLL 调用 [Reflection.Assembly]::LoadFile() 之后调用 c# 方法,但是有足够的依赖关系,这会很麻烦且容易出错。

我知道 Seed() 不是 Down() 迁移清理代码的理想位置(根据 DrewJordan 的回答添加一些上下文),但不幸的是,出于多种原因,使用 Down() 本身是不可行的。从实际的角度来看,它是不可行的,因为清理必须转换几 GB 的数据,当我在 Down() 中尝试一个简单的副本时,这导致 SQL Server Express 崩溃,可能是因为生成的事务大小非常大。其次,即使从假设的角度来看,我也不相信有办法将数据转换为 Down() 所需的范围,因为当前 SQL 行无法在 c# 中作为事务的一部分读取,并且数据的转换需要在 c# 中进行。有关该限制的详细信息,请参阅我的另一个问题:“Transform data using c# during Entity Framework migration”。需要对设计进行一些折衷以使清理工作正常进行,我使用 Seed() 作为折衷方案。清理代码本身不需要从 Seed() 运行,但我不知道在不更改当前迁移的情况下还可以从包管理器中调用什么。

还有另一种情况,在不更改当前迁移的情况下单独调用 Seed() 或其他清理代码会很有用。考虑 Seed()/cleanup 方法中存在错误的场景。您修复了该错误并希望在不更改当前迁移的情况下重新运行它。将清理逻辑放在 Down() 中并不能解决问题,因为当数据库已经在迁移时,不会调用 Down()。

【问题讨论】:

  • 用Seed方法恐怕无法实现。您可能想要分叉 EntityFramework 并在那里添加迁移拦截,但很可能您只需要考虑另一种实现方式。每次迁移都需要清理大量代码的原因是什么?
  • @raderick 哇——我没有意识到源代码可用于实体框架。谢谢!我将围绕它进行挖掘,看看是否可以找到另一种解决方法。 ...清理是针对一个特定的迁移,似乎没有办法在 Up()/Down() 中进行转换,因为转换是在 c# 中完成的。数据集是巨大的。
  • 是否可以通过将此迁移代码添加为单元测试并使用 [Ignore] 属性对其进行标记并手动运行来实现您的目标?
  • @raderick 单元测试听起来也可以,谢谢。顺便说一下,我确实从包管理器控制台中弄清楚了如何做到这一点 - 我在下面发布了我的方法:stackoverflow.com/a/35873514/1552934
  • 我看到了你的帖子,但遗憾的是,这种类型的代码可能会产生奇怪的副作用 - 可能性很小,但是,当我面对这样的代码并且没有与那个代码联系时,我个人并不觉得安全谁写的。

标签: c# entity-framework


【解决方案1】:

我知道你问过关于从 PM 控制台运行它的问题:AFAIK 这是不可能的。这个用例不是Seed 的用途:如果您要在Down 方法中进行更改,我建议您编写要运行的SQL 脚本,或者在Down 中创建上下文并在那里做任何事情。

这是一个坏主意,因为Seed 指望与最新迁移保持同步。例如,它将允许您编写在先前版本的上下文中不可能成功的语句,例如当列被添加到模型但尚未应用于数据库时。我还没有尝试过,但它至少会引发异常,并且可能无法写入 any 更改。

如果你真的,真的认为这是个好主意,你可以在你的配置中添加一个方法:

public void CallSeed()
{
    using (var c = new Context()) // create your context
    {
        Seed(c);
    }
}

并从 Down 中调用它:

var c = new Configuration();
c.CallSeed();

【讨论】:

  • 感谢您的回复。我本来希望使用 Down() 进行清理,但不幸的是,它不适用于必须在 c# 中进行转换的数据转换,也不适用于大型转换。我更新了我的问题以对此进行扩展。
【解决方案2】:

我找到了看起来可行的东西的基础。这是一个 hack,但我会在这里发布它,因为从我链接到的其他堆栈溢出问题来看,我知道其他人也想要类似的东西。

这个想法是搭载另一个可以从包管理器运行并且不会改变数据库本身的迁移命令 - 我将使用 Get-Migrations 。这将允许 package-manager 命令完成定位正确配置、加载正确程序集等的繁重工作。我将通过从我的 DbMigrationsConfiguration 子类的构造函数中调用我的清理代码来搭载该命令。我只想有意地运行清理代码,而不是每次构造配置时,所以我也会在实际运行清理代码之前检查哨兵环境变量。稍后,我打算将清理代码从 Seed() 中移出,以便将其与实际播种分开,但由于过去有人询问过手动运行 Seed(),所以我将在此处使用它作为示例。

我将 DbMigrationsConfiguration 子类更改如下:

internal sealed class Configuration : DbMigrationsConfiguration<TimsDB>
{

    public Configuration()
    {
        if ("true".Equals(Environment.GetEnvironmentVariable("RUN_SEED")))
            using (TimsDB db = new TimsDB())
            {
                Database.SetInitializer<TimsDB>(null);
                Seed(db);
            }
        }
    }

    ...

}

这可用于从包管理器手动调用 Seed() 方法:

PM> $Env:RUN_SEED = "true"
PM> Get-Migrations
PM> $Env:RUN_SEED = "false"

【讨论】:

    猜你喜欢
    • 2015-07-03
    • 1970-01-01
    • 1970-01-01
    • 2015-05-11
    • 1970-01-01
    • 2017-08-09
    • 1970-01-01
    相关资源
    最近更新 更多