目录
管理有状态数据通常是DevOps策略中较棘手的部分之一。实体框架的迁移功能可以极大地帮助其小型、独立可执行的、对PR友好的增量。从理论上讲,如果部署进展不佳,它甚至可以授予还原迁移的能力。如果使用得当,该技术可以带来巨大的帮助。
但是,在实践中,有很多不同的方法可以自动执行这些增量。每种方法都有优点和缺点,可能适用于一个项目的方法可能不适用于另一个项目。
在这篇文章中,我将展示运行EF数据库迁移的六种方法,说明每种方法在哪些情况下最有帮助,然后演示如何在从生成服务器运行迁移时设置Active Directory(AD)身份验证并正确设置连接字符串。
通过应用迁移
何时运行迁移的问题是...有趣。问题是,当应用程序首次使用DbContext.Database.MigrateAsync()命令启动时,运行迁移非常容易。
在启动时运行迁移既方便又节省时间,因为它搭载了现有的数据库连接和防火墙规则。但它也有一些缺点:
- 减慢应用启动速度
- 难以还原
- 长时间运行的迁移的超时问题
- 违反最小特权原则
最后一点值得解释。它引用了以下安全最佳做法:
最小特权的主体
为了尽量减少安全事件造成的损害,应向系统授予所需的最低访问级别。
换句话说,不要向系统授予任何不需要的权限。
例如,作为日常操作的一部分,应用通常不需要删除数据库表,因此不应授予它们该权限。但是,这正是应用在运行数据库迁移时所需的权限类型。因此,通过让应用运行数据库迁移,我们无意中赋予攻击者造成比通过应用访问数据库所需的更多混乱的能力。
更具体地说:如果应用程序正在连接到SQL Server,则可以授予运行应用程序的帐户db_datareader和db_datawriter,但不能是db_ddladmin,绝对不能是db_owner。这样做将在安全中心审核中生成一个标志。
通过生成服务器迁移
如果您仍在开发中,有意推迟安全风险,或者只是总体上对风险感到满意,则通过应用程序迁移可能已经足够了。但是,准备就绪后,另一种方法是让生成服务器运行EF迁移。有几种方法可以解决这个问题,每种方法都有优点和缺点。
命令行工具
作为开发人员,你可能已经熟悉dotnet ef数据库更新命令。此选项在DevOps世界中不方便,因为它需要源代码。由于各种原因,源代码是一种痛苦,其中最重要的是您需要获取您尝试运行的部署的确切代码版本。下面的其他方法通常更可取。
SQL脚本
如果让生成服务器运行命令dotnet ef migrations脚本[oldmigration] [newmigration],它将生成一个文件,你可以将其另存为生成管道中的项目,然后为每个环境执行。与其他一些选项不同,此方法生成可由DBA查看的资产。但是,确定哪些值用于旧迁移和新迁移将很棘手。更糟糕的是,如果每个环境都处于不同的版本,就像不是每个PR部署到生产环境时经常发生的那样,那么可能不可能生成适用于所有环境的单个项目,因为这些值对于每个环境都是不同的。因此,我通常不推荐这种方法。
幂等SQL脚本
幸运的是,如果将该--idempotent参数传递给dotnet ef迁移脚本,则它将生成一个脚本,该脚本将仅运行需要运行的迁移。生成的文件看起来像一堆if语句,如下所示:
BEGIN
CREATE INDEX [IX_AbpAuditLogActions_AuditLogId] _
ON [AbpAuditLogActions] ([AuditLogId]);
END;
GO
虽然此选项不授予还原错误部署的选项,但它非常适合作为已发布的构建工件,可以由DBA查看,并且在每个环境处于不同版本时工作。但是,此方法不是事务性的,因此迁移失败可能会使数据库处于不一致状态。
捆绑包![](https://i-blog.csdnimg.cn/blog_migrate/1b038dfb4dbaece7223051a280628263.png)
捆绑包很棒。它们解决了将事务应用于迁移的问题。只需让生成服务器运行dotnet ef迁移捆绑包--self-contained -r [linux-x64|win-x64],即可获得单个文件二进制文件(默认情况下称为 efbundle.exe),你可以将其发布为项目,在事务中运行迁移,并且仅运行需要应用的迁移。您甚至可以指定特定的迁移,从而可以还原迁移。生成的文件如下所示:E
Entity Framework Core Migrations Bundle 7.0.1
Usage: efbundle [arguments] [options] [[--] <arg>...]]
Arguments:
<MIGRATION> The target migration. If '0', all migrations will be reverted.
Defaults to the last migration.
Options:
--connection <CONNECTION> The connection string to the database.
Defaults to the one specified in AddDbContext or OnConfiguring.
--version Show version information
-h|--help Show help information
-v|--verbose Show verbose output.
--no-color Don't colorize output.
--prefix-output Prefix output with level.</span></code>
这个选项几乎是我的最爱。它唯一缺少的是能够在迁移之外运行自定义代码。
命令行应用程序
曾几何时,我的一个项目将加密数据存储在数据库的字段中。在我们投入生产后(当然),我们意识到加密算法太强了,导致了性能问题。我们需要降低加密的强度,但这意味着我们需要执行涉及用C#编写的方法的复杂数据迁移。
我们必须运行一个查询来读取每一行,使用C#中的旧算法对其进行解密,使用C#中的新算法重新加密它,然后将其更新回数据库中。对于EF迁移来说应该足够简单吧?
令人惊讶的是,事实证明EF迁移无法完成此类工作。它们旨在运行insert或update SQL语句或create或drop DDL语句,但不检索数据。自己检查一下:看看是否可以在MigrationBuilder上找到一种检索数据的单一方法。
因此,由于该算法位于C#方法中,并且由于EF迁移无法访问它,因此我们必须在EF迁移之外运行代码。
幸运的是, 因为我们使用的是ASP.NET Boilerplate(ABP框架的前身),它包含一个运行我们迁移的命令行应用程序.这提供了在迁移之前或之后运行代码的灵活性,从而解决了我们的问题。
从生成服务器运行DbContext.Database.MigrateAsync()的命令行应用(或带有命令行选项的主应用)可以在事务中运行,并且使用--self-contained -r [linux-x64|win-x64]编译时使用几乎是单个文件(必须包含 Microsoft.Data.SqlClient.SNI.dll)。缺点是它们不允许还原迁移。但是,它们是我个人的最爱,因为它们在面对艰难的迁移时提供了最大的灵活性。此外,它们在每租户数据库多租户方案中特别有效。
如果您对此方法的详细信息感兴趣,请查看我创建的 DbMigrator示例项目,该项目在多阶段生成管道的一个阶段编译和发布命令行应用,然后在另一个阶段执行它。
认证
是时候承认房间里的大象了。如果不在应用启动时运行迁移,如何从生成服务器获取与数据库的连接?此外,假设你在Azure上使用SQL Server,并遵循仅使用AD身份验证的最佳做法,如何在无外设环境中使用Active Directory帐户进行身份验证?
一般来说,有四个步骤。首先,创建应用注册。其次,使用CREATE USER [{userName}] FROM EXTERNAL PROVIDER;将应用程序注册添加到数据库,并使用EXEC sp_addrolemember 'ddl_admin', '{userName}'授予其运行DDL的权限。第三,为应用注册创建一个机密:
最后,添加防火墙规则以允许从自定义生成代理访问数据库,或者,如果使用的是托管代理,则添加“Allow Azure services and resources to access this server”,在 Bicep 中如下所示:
resource firewallRule_AzureIps 'Microsoft.Sql/servers/firewallRules@2021-11-01' = {
name: 'AllowAzureIps'
parent: sql
properties: {
startIpAddress: '0.0.0.0'
endIpAddress: '0.0.0.0'
}
}
最后,您应该能够使用如下所示的连接字符串:"Server=tcp:{sqlServerName}.database.windows.net,1433; "Database={dbName}; Encrypt=True; User Id={servicePrincipalAppId}; Password={servicePrinicpalSecret}; Authentication='Active Directory Service Principal';"
仅此而已。输入应用注册应用ID和生成的机密,即可开始比赛。
总结
在本文中,我讨论了运行迁移的六种方法,并随后介绍了如何完成身份验证。下表将有助于总结优缺点:
正如我所提到的,我个人最喜欢的是最后一个命令行应用,因为即使它不支持自动还原,它也将在事务中运行,并且可以灵活地在EF之外执行复杂的迁移。
您尝试过哪些方法?我错过了什么吗?随意在评论中插话。
本文最初发表于 http://www.leerichardson.com/feeds/posts/default
https://www.codeproject.com/Articles/5355897/How-to-Deploy-Entity-Framework-Database-Migrations