有没有办法从已发布的DLL运行EF Core RC2工具?

Tal*_*aul 13 entity-framework-core asp.net-core

发布.Net Core RC1应用程序后,project.json中指定的命令具有为其创建的相应.cmd文件,这些文件可在部署后执行(例如web.cmd和ef.cmd).在我的例子中,我想在我的部署目标上运行以下Entity Framework命令:

dotnet ef database update -c MyContext
Run Code Online (Sandbox Code Playgroud)

当我从包含源代码的文件夹运行它时,这工作正常,但是在发布后,它似乎没有在编译的DLL中找到命令.我对RC2命令变化的理解是"工具"可以编译为名为dotnet - *.dll的独立应用程序,并且可以通过CLI执行.如何将实体框架核心工具公开为已发布输出中的可执行DLL?

仅供参考,我的构建/部署工作流程如下:

TeamCity的

dotnet restore => dotnet build => dotnet test => dotnet publish

八达通部署

上传包=> EF更新数据库=>等

Maj*_*jor 20

不幸的是,EF Core 迁移 s*cks,很多......我已经看到了大量的解决方案,但让我们列出它们。因此,您可以在没有 Visual Studio 的情况下运行和部署 EF 迁移。以下都不是完美的解决方案都有一些警告:

  1. 在此处使用EF Core Tools 是 MS 官方站点的链接,该站点解释了如何安装和使用它。
  • 优点:MS 官方工具。所有版本的 .NET Core 均受支持。
  • 缺点:它似乎是 EF6“Migrate.exe”的继承者。但事实并非如此!目前,如果没有实际的源代码 (.csproj),则无法使用此工具。这不太适合 Live/Prod 部署。通常,您的数据库服务器上没有 C# 项目。
  1. dotnet exec我试图理解大量记录不佳的参数。并且在找到此脚本之前无法运行迁移。该名称暗示 .NET core 2.1,但我已将其与 3.0 一起使用并工作。更新:没有用 .NET 5 运行它
  • 优点:它可以像EF6“migrate.exe”一样使用。最后迁移工作无需源代码。并且很可能这是从脚本进行迁移并使用程序集的唯一方法。
  • 缺点:设置脚本非常困难,并且容易遗漏一个参数。没有真正记录的解决方案,可能会将 .NET Core 版本更改为版本。也很可能您还需要更改代码。您必须实现IDesignTimeDbContextFactory<DbContext>接口才能使其工作。还要确保您的部署服务器上有EF.dllMicrosoft.EntityFrameworkCore.Design.dll。链接的脚本正在寻找许多文件夹中的那些。最好是在构建期间将它从 .nuget 文件夹复制到您的工件。听起来很复杂,是的……但链接脚本有很大帮助。
  1. EF 迁移添加到您的 Startup.cs或您的代码开始运行并有权访问 DBContext 的任何点。用dbContext.Database.Migrate();
  • 优点:迁移每次都会自动发生,无需执行任何其他操作。
  • 缺点:每次都会自动发生迁移……您可能不希望这种情况发生。它还将在每个应用程序启动时运行。所以你的启动时间会很糟糕。
  1. 自定义应用程序。它类似于之前的解决方案(第 3 点)。因此,您使用 .NET 代码来运行迁移。但是,与其将其放入您的应用程序中,不如创建一个小型控制台应用程序并在该应用程序中调用 migrate。您必须构建此应用程序并将其放入 Artifact 以在部署期间运行它。
  • 优点:不涉及脚本。您可以在部署管道中随时调用它。因此,您的实际应用程序启动时间不会受到影响。
  • 缺点:您必须维护、构建和打包应用程序才能进行 EF Core 迁移。
  1. 如果你使用 Azure Devops 进行部署,你可以使用这样的扩展。或者只是在 Azure Devops Marketplace 中搜索一个。
  • 优点:它应该可以工作 :) 没有尝试过它们中的任何一个,也不知道它们是做什么的。(我很确定他们也在使用“dotnet exec”第 2 点。)
  • 缺点:并非每个人都可以从 Azure Devops 访问 Live/Prod。
  1. 生成 SQL 脚本:如果以上都不起作用,您可以生成迁移 SQL 并稍后运行。运行EF工具,“脚本”参数:dotnet ef migrations script --output <pathAndFile>.sql --context <DbContextName> --idempotent。输出是一个 SQL 文件,可以手动执行,也可以通过 CI/CD 管道中的脚本执行。
  • 优点:如果您没有访问或架构更改权限来访问仅限生产 DB 的 DBA,这是完美的解决方案。您可以为 DBA 提供一个“安全可靠”的 SQL 文件来运行...
  • 缺点:强调此解决方案必须在您的.csproj文件所在的工作目录中运行非常重要所以它需要源代码!此外,您必须稍微更改代码。需要执行IDesignTimeDbContextFactory<DbContext>

更新:在 .NET 5 中有一些改进。现在更容易实现和使用IDesignTimeDbContextFactory,但最重要的是微软修复了这个错误。现在可以将 SQL 连接字符串作为args. 因此,如果您实现了,IDesignTimeDbContextFactory<T>那么将它与 .NET CLI 和 EF 工具一起使用很简单:

dotnet ef database update --context <DbContextName> --project "**/<ProjectName>.csproj" -- "<SQL connection will be passed into args[0]>"

同样重要的是要强调这仅适用于 .NET 5 并且还需要源代码! 您还可以将它与选项 6(生成 SQL 脚本)一起使用。

第二个烦人的问题一旦实现IDesignTimeDbContextFactory<T>,所有ef命令都会发现(甚至在开发过程中从 Visual Studio 运行的命令)。如果您需要来自 args[0] 的 SQL 连接字符串,您必须在开发过程中migrations add或任何其他ef命令中传递它!

对不起,名单太长了。但希望它有所帮助。


nov*_*ver 10

我在项目中遇到了同样的问题,但由于多种原因,我不希望迁移在应用程序启动时自动运行.

为了解决它,我更新Program.cs了两个参数(完整代码列在下面)

  • --ef-migrate,以应用所有挂起的迁移,和
  • --ef-migrate-check,以验证是否已应用所有迁移

如果存在参数,则应用EF操作并退出程序,否则启动Web应用程序.

请注意,它取决于Microsoft.Extensions.CommandLineUtils软件包以简化命令行解析.

对于章鱼部署,然后可以将包发布两次到单独的位置 - 一个用于运行迁移,另一个用于webhosting.在我们的例子中,我们添加了一个带有内容的"post deploy powershell脚本"

$env:ASPNETCORE_ENVIRONMENT="#{Octopus.Environment.Name}"
dotnet example-app.dll --ef-migrate
Run Code Online (Sandbox Code Playgroud)

在docker环境中,它也可以完美地工作

docker run -it "example-app-container" dotnet example-app.dll --ef-migrate
Run Code Online (Sandbox Code Playgroud)

完整的Program.cs,不包括命名空间和使用:

//Remember to run: dotnet add package Microsoft.Extensions.CommandLineUtils
public class Program
{
    public static void Main(string[] args)
    {
        var commandLineApplication = new CommandLineApplication(false);
        var doMigrate = commandLineApplication.Option(
            "--ef-migrate",
            "Apply entity framework migrations and exit",
            CommandOptionType.NoValue);
        var verifyMigrate = commandLineApplication.Option(
            "--ef-migrate-check",
            "Check the status of entity framework migrations",
            CommandOptionType.NoValue);
        commandLineApplication.HelpOption("-? | -h | --help");
        commandLineApplication.OnExecute(() =>
        {
            ExecuteApp(args, doMigrate, verifyMigrate);
            return 0;
        });
        commandLineApplication.Execute(args);
    }

    private static void ExecuteApp(string[] args, CommandOption doMigrate, CommandOption verifyMigrate)
    {
        Console.WriteLine("Loading web host");
        var webHost = new WebHostBuilder()
            .UseKestrel()
            .UseContentRoot(Directory.GetCurrentDirectory())
            .UseIISIntegration()
            .UseStartup<Startup>()
            .Build();

        if (verifyMigrate.HasValue() && doMigrate.HasValue())
        {
            Console.WriteLine("ef-migrate and ef-migrate-check are mutually exclusive, select one, and try again");
            Environment.Exit(2);
        }

        if (verifyMigrate.HasValue())
        {
            Console.WriteLine("Validating status of Entity Framework migrations");
            using (var context = webHost.Services.GetService<DatabaseContext>())
            {
                var pendingMigrations = context.Database.GetPendingMigrations();
                var migrations = pendingMigrations as IList<string> ?? pendingMigrations.ToList();
                if (!migrations.Any())
                {
                    Console.WriteLine("No pending migratons");
                    Environment.Exit(0);
                }

                Console.WriteLine("Pending migratons {0}", migrations.Count());
                foreach (var migration in migrations)
                {
                    Console.WriteLine($"\t{migration}");
                }

                Environment.Exit(3);
            }
        }

        if (doMigrate.HasValue())
        {
            Console.WriteLine("Applyting Entity Framework migrations");
            using (var context = webHost.Services.GetService<DatabaseContext>())
            {
                context.Database.Migrate();
                Console.WriteLine("All done, closing app");
                Environment.Exit(0);
            }
        }

        // no flags provided, so just run the webhost
        webHost.Run();
    }
}
Run Code Online (Sandbox Code Playgroud)


小智 5

自最初的问题提出以来已经过去了很多时间,现在有更好的方法来处理 CI/CD 场景中的 ef 迁移。

有一个名为“bundles”的官方工具。

您所要做的就是构建一个捆绑包

dotnet ef migrations bundle
Run Code Online (Sandbox Code Playgroud)

稍后,您可以传递目标数据库连接字符串来执行捆绑包

.\efbundle.exe --connection 'Data Source=(local)\MSSQLSERVER;Initial Catalog=Blogging;User ID=myUsername;Password=myPassword'
Run Code Online (Sandbox Code Playgroud)

官方文档