以编程方式创建Code First迁移

14

我正在一个项目上使用Entity Framework中的Code First来处理数据库。我们想要将所有的持续集成改为在下游消耗生成的MSI包,但是使用EF会有一些问题:

我尝试了网上找到的各种方法,但大部分似乎要求将AutomaticMigrations设置为true并允许AutomaticMigrationDataLossAllowed (参见:http://romiller.com/2012/02/09/running-scripting-migrations-from-code/)。

我尝试模仿Add-Migration的做法,通过查阅.NET反编译工具来找到Powershell调用的System.Data.Entity.Migrations.AddMigrationCommand命令的方法,但是似乎找不到任何有效的方法。

有没有人有任何想法可以在不进行非常混乱的操作的情况下实现这一点?我认为很多人都会想要做/已经做过这件事...

非常感谢您的帮助!


你想让这个工具实际创建迁移文件并将它们添加到项目中吗?还是只是更新数据库以符合当前的代码架构?如果是后者,为什么不考虑自动迁移选项?如果是前者,你不能只添加一个构建事件来运行 Add-Migration 命令吗? - Rob
@Rob 我们正在尝试创建一个工具,使得我们的CI可以使用它并在提交后为我们生成迁移。这将使我们能够自动从提交中创建MSI,以便模型与迁移保持同步且MSI不会被破坏。由于我们数据库的规模,自动迁移几乎肯定会导致自动数据丢失。Add-Migration似乎无法从VS shell外部访问。 - LukeHennerley
你能解释一下“自动迁移几乎肯定会导致自动数据丢失”的意思吗?为什么你认为在运行Add-Migration时,你的脚本和EF运行Add-Migration之间有任何区别?还是说你只是想有时运行手动迁移并避免混合使用自动/手动迁移? - Thomas Boby
3个回答

9

首先,无法在Visual Studio外运行Nuget PowerShell(它使用DTE)。 此外,您在没有Visual Studio的情况下编写的所有内容都需要手动插入到csproj中(但这不是什么难事)。

我会发送给您一些代码行以展示其工作原理。 要测试它们,请创建MyDll dll(一个带有上下文和实体的项目测试),然后手动启用MyDll上的迁移,使用Enable-Migrations(只需创建Configuration.cs)。

之后,您可以使用此代码段生成源代码。

DbConnectionInfo connectionStringInfo = new DbConnectionInfo(
    "Server=.;Database=MigrationTest;User=sa;Password=dacambiare", "System.Data.SqlClient"); // We shoud retrieve this from App.config

ToolingFacade toolingFacade =  new ToolingFacade(
    "MyDll",   // MigrationAssemblyName. In this case dll should be located in "C:\\Temp\\MigrationTest" dir
    "MyDll",  // ContextAssemblyName. Same as above
    null,
    "C:\\Temp\\MigrationTest",   // Where the dlls are located
    "C:\\Temp\\MigrationTest\\App.config", // Insert the right directory and change with Web.config if required
    "C:\\Temp\\App_Data",
    connectionStringInfo)
{
    LogInfoDelegate = s => {Console.WriteLine(s);},
    LogWarningDelegate = s => { Console.WriteLine("WARNING: " + s); },
    LogVerboseDelegate = s => { Console.WriteLine("VERBOSE: " + s); }
};


ScaffoldedMigration scaffoldedMigration = toolingFacade.Scaffold("MyMigName", "C#", "MyAppNameSpace", false);

Console.WriteLine(scaffoldedMigration.DesignerCode);
Console.WriteLine("==================");
Console.WriteLine(scaffoldedMigration.UserCode);

// Don't forget the resource file that is in the scaffoldedMigration

编辑
我忘记了命名空间并且它们并不经常使用,所以在这里为您提供。

using System;
using System.Data.Entity.Infrastructure;
using System.Data.Entity.Migrations.Design;

这对我很有帮助,谢谢!我对 MyDll 部分有点困惑,但是意识到它只是包含实体框架的项目名称。使用上面的示例,我会将 MigrationTest 替换为 MyDll 以匹配我的情况。有趣的是,要创建 connectionStringInfo,我必须提供连接字符串的名称而不是连接字符串本身。因此,我的参数是 mysql,它来自于我的配置文件中的 <add name="mysql" connectionString="obfuscated details" providerName="MySql.Data.MySqlClient" /> - Geesh_SO
你有EF Core的类似代码吗?我正在尝试使用dbContext.Database.GetService<IMigrationsScaffolder>();来删除所选迁移,而不是像这段代码一样删除所有迁移dbContext.Database.GetService<IMigrator>("0");。 - Alok

4

这不是对您问题的真正回答,而是分享我的经验:我会非常小心地处理通过此方式为您生成的迁移。很多时候当我创建迁移(在VS中)时,我会审查它们。我会检查所建议的更改是否是我想要的,并且EF没有尝试做一些愚蠢的事情(例如通过删除表并创建一个新表来更改列/表名)。

有时候 EF 也会遗漏一些关键的更改-最近我在更改 nvarchar 字段的长度时遇到了一些麻烦,这也需要仔细审核迁移。

而您的迁移本身也是代码。在迁移到生产环境之前,我们会对迁移进行同行评审。自动生成迁移将使这些更改不那么显眼,可能会导致生产数据的丢失。

如果您在团队环境中遇到迁移问题-我们通过沟通解决了这个问题:每当开发人员检入新的迁移时,就会向该项目上的所有其他开发人员发送电子邮件。这解决了我们所有的问题。

更新刚刚与同事讨论了一下,另一个问题出现了 - 您如何进行本地开发?您会在开发人员的机器上创建迁移,确保一切正常吗?然后删除迁移并检入代码?然后您的 CI 将重新生成您的迁移?我认为当使用 EF 时,这将使开发人员非常困惑。


是的 - 你必须防止开发人员将迁移检入,否则会引起问题。 - GraemeMiller
1
@trailmax我们的开发人员(目前)使用自动迁移,每当我们在开发环境中进行模型更改时,我们会删除和重新创建数据库,以避免使用代码基础迁移时出现的问题,团队内部和源代码控制内。但您提出了一些我没有想到的有趣观点,我们在使用EF和基于代码的迁移的正式版本方面还处于初级阶段,如果我们再来一次,可能不会选择EF。出于兴趣,您如何处理团队内的基于代码的迁移? - LukeHennerley
@LukeHennerley 自动迁移对我们来说从未奏效 - 太多的巫术。我们的团队对基于代码的模型进行更改,发出 add-migration 命令,获取迁移脚手架 - 仔细检查是否是正确的迁移。将其应用于本地开发数据库,确保一切按预期工作。而且很少有脚手架迁移第一次就是正确的。然后执行集成测试,从头开始重新生成 DB。当一切正常时,这将被检入,随后开发人员会向团队发送有关新迁移的电子邮件。 - trailmax
@LukeHennerley 如果其他人正在进行迁移,他们将不得不重新构建他们的迁移。此外,我们的CI运行集成测试,从所有迁移中重新构建数据库,并在该数据库上运行集成测试。 - trailmax
@LukeHennerley - 即使 EF 团队的 Rowan Miller 也提到自动迁移在演示中效果很好,但在实际应用中并非如此。EF7 没有自动迁移功能。 - GraemeMiller

1
你可以使用System.Data.Entity.Migrations.Design.MigrationScaffolder类来编写程序生成迁移,代码如下:
[TestMethod]
public void GenerateTestMigration()
{
    var config = new MyDbMigrationsConfiguration();
    var scaffolder = new MigrationScaffolder(config);
    var pendingMigration = scaffolder.Scaffold("TestMigration");
    Trace.WriteLine(pendingMigration.UserCode);
}

我在构建服务器上使用这种和其他技术,如果有未完成的迁移或需要创建迁移,则会使构建失败,如下所示:

using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Data.Entity.Migrations;
using System.Data.Entity.Migrations.Design;
using System.Diagnostics;
using System.Linq;

namespace YabbaDabbaDoo
{
    [TestClass]
    public class MigrationTests
    {
        [TestMethod]
        [TestCategory("RunOnBuild")]
        public void VerifyThereAreNoPendingMigrations()
        {
            // Arrange
            var config = new MyDbMigrationsConfiguration();
            var dbMigrator = new DbMigrator(config);

            // Act
            var pendingMigrations = dbMigrator.GetPendingMigrations().ToList();

            // Visual Assertion
            Trace.WriteLine(pendingMigrations);

            // Assert
            Assert.AreEqual(0, pendingMigrations.Count(), "There are pending EF migrations that need to be ran.");
        }

        [TestMethod]
        [TestCategory("RunOnBuild")]
        public void VerifyDatabaseIsCompatibleWithModel()
        {
            // Arrange
            var context = new MyDbContext();

            // Act
            var isCompatible = context.Database.CompatibleWithModel(false);

            // Visual Assertion
            if (!isCompatible)
            {
                var config = new MyDbMigrationsConfiguration();
                var scaffolder = new MigrationScaffolder(config);
                var pendingMigration = scaffolder.Scaffold("MissingMigration");

                Trace.WriteLine("Missing Migration:");
                Trace.WriteLine("");
                Trace.WriteLine(pendingMigration.UserCode);
            }

            // Assert
            Assert.IsTrue(isCompatible, "The EF model is not compatible with the database. An EF migration needs to be created. See output for sample of missing migration.");
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接