Entity Framework 迁移 Azure DevOps 发布管道

13

我希望能在Azure DevOps Release Pipeline上运行迁移。因为我想要在每次发布时自动运行我的DB脚本。我的发布管道没有源代码,我只有编译后的DLL文件。

当我在本地计算机上执行此命令时,它可以成功运行。如何转换此命令以便我可以将其与DLL一起使用。

dotnet ef database update --project MyEntityFrameworkProject --context MyDbContext --startup-project MyStartupProject


嗨,近况如何?下面分享的解决方案怎么样?请考虑接受您认为适合的答案。 - Mengdi Liang
2个回答

20

另一种方法是在构建流水线期间生成迁移脚本(一个常规的SQL脚本),并将此脚本作为您的工件的一部分。要执行此操作,请运行以下命令:

dotnet ef migrations script --output $(build.artifactstagingdirectory)\sql\migrations.sql -i

请注意-i标志,它使此脚本可以在同一数据库上多次运行

将此脚本作为工件的一部分后,您可以通过使用内置任务Azure SQL Database Deployment在发布流水线上运行该脚本。

有关更多信息,请查看此链接

编辑:正如@PartickBorkowicz所指出的,与从Build / Release Agent角度以常规方式访问数据库相关的一些问题。以下是一些额外提示:如何在不将数据库和连接字符串存储在代码中的情况下继续生活。

1. 构建流水线

如果不做任何处理,构建代理将需要数据库连接才能运行dotnet ef migrations script脚本。但是有一个技巧可以让您在没有数据库和连接字符串的情况下工作:IDesignTimeDbContextFactory

您只需创建如下类即可:

public class YourDesignTimeContextFactory : IDesignTimeDbContextFactory<YourDbContext>
{
    public YourDbContext CreateDbContext(string[] args)
    {
        var databaseConnectionString = "Data Source=(LocalDB)\\MSSQLLocalDB;Initial Catalog=LocalDB;Integrated Security=True;Pooling=False";
        var builder = new DbContextOptionsBuilder<YourDbContext>();
        builder.UseSqlServer(databaseConnectionString);

        return new YourDbContext(builder.Options);
    }
}

一旦它出现在您的项目中(您不需要注册它),您的生成代理将能够生成带有迁移逻辑的 SQL 脚本,而无需访问实际数据库。

2. 发布流水线

现在你已经生成了 SQL 脚本,并将其作为构建流水线的一部分包含在工件中。现在,发布流水线是您想要在实际数据库上运行此脚本的时候 - 您需要以某种方式连接到此数据库。为了以安全的方式执行此操作,您不应将密码存储在代码的任何地方。一个好的方法是将密码保存在 Azure 密钥保管库中。Azure 发布流水线中有一个名为 Azure Key Vault 的内置任务。这将获取您的秘密,您可以在下一步中使用: Azure SQL 数据库部署。您只需要设置选项:

AuthenticationType: Connection String
ConnectionString: `$(SqlServer--ConnectionString)` - this value depends on your secret name in Key Vault
Deploy type: SQL Script File
SQL Script: $(System.DefaultWorkingDirectory)/YourProject/drop/migrations/script.sql - this depends how you setup your artifact in build pipeline.

通过这种方式,您可以在不访问数据库的情况下生成迁移,并且可以运行迁移SQL而无需在代码中存储连接字符串。


值得注意的是,构建管道代理需要您的 ef 连接字符串才能生成脚本,就像 Add-Migrations 需要它才能与数据库通信一样。如果您在源代码中硬编码了连接字符串(例如 appsettings.json),那么这是可以的,但如果您将凭据存储在目标服务器上(例如 Azure 中),则可能会成为问题。 - Patrick Borkowicz
@PatrickBorkowicz 这是正确的,但 Azure KeyVault 在这种情况下非常方便 - 您可以在其中存储连接字符串,并从管道中访问它,而无需将其存储在代码中的任何位置。 - Tomasz Madeyski
这是一个很棒的想法,我可能会尝试一下。据我所知,Add-Migrations 是只读操作,因此在构建时是可以接受的,但它需要数据库存在并在线。您甚至可以通过使用具有有限访问权限的专用数据库用户/连接字符串来限制其范围。我一直在考虑这种方法。目前,我们在运行时(启动时)使用 context.Database.Migrate() 部署迁移。这很好用,但确实需要应用程序重新启动。 - Patrick Borkowicz
@PatrickBorkowicz 请检查我的编辑,我添加了一些澄清,可能会很有用。 - Tomasz Madeyski
@TomaszMadeyski 我正在尝试实现这个,但不确定如何处理在生成构建管道期间需要的连接字符串。在您的示例中,IDesignTimeDbContextFactory的实现包含一个硬编码的连接字符串,但稍后您还写道Azure KeyVault可以用于“从管道中访问它,而无需将其存储在代码中的任何位置”。您能否详细说明如何避免使用您示例中的方法,而改为使用KeyVault获取连接字符串以生成脚本? - Boris Layvant
2
@BorisLayvant 的问题在于,对于脚本生成,您不需要正确的连接字符串(也不需要数据库)。它仅基于代码中的迁移生成。只有当您实际想要运行迁移(脚本)时,才需要连接字符串(和数据库)来针对您的数据库运行。在我的情况下,这是在发布管道(而不是构建管道)中完成的,并且连接字符串是从 KeyVault 中获取的。 - Tomasz Madeyski

4
如果您不想在构件中包含源代码,可以使用以下脚本:
set rootDir=$(System.DefaultWorkingDirectory)\WebApp\drop\WebApp.Web
set efPath=C:\Program Files\dotnet\sdk\NuGetFallbackFolder\microsoft.entityframeworkcore.tools\2.1.1\tools\netcoreapp2.0\any\ef.dll
dotnet exec --depsfile "%rootDir%\WebApp.deps.json" --additionalprobingpath %USERPROFILE%\.nuget\packages --additionalprobingpath "C:\Program Files\dotnet\sdk\NuGetFallbackFolder" --runtimeconfig "%rootDir%\WebApp.runtimeconfig.json" "%efpath%" database update --verbose --prefix-output --assembly "%rootDir%\AssemblyContainingDbContext.dll" --startup-assembly "%rootDir%\AssemblyContainingStartup.dll" --working-dir "%rootDir%"

事实证明,您可以像以下示例一样使用未记录的dotnet exec命令(假设Web应用程序称为WebApp):
请注意,此运行命令行任务的工作目录(在高级选项下隐藏)必须设置为构件所在的位置(上面的rootDir)。
另一个选择是安装Build & Release Tools扩展,并使用“从DLL部署Entity Framework Core迁移”任务。
您可以在这里, 这里这里阅读更多信息。

3
EF6有没有替代品? - Albert
谢谢您的回答,是否可以通过编程方式获取这些文件夹(Nuget Packages、Nuget Fallback 和 EF Dll),因为服务器可能使用不同的操作系统和不同的 EF Core 版本。 - suphero

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