我正在使用管道来管理 .net 核心项目。 在我的管道中,在构建阶段,我运行
dotnet ef migrations script --context "MyDbContext" --project "./MySolution/MyDataProject/" --output "./migrationScript.sql" --idempotent
来生成脚本,这些脚本稍后可以针对各种测试、登台和生产环境数据库运行以同步架构(我在非生产环境中使用与生产环境中相同的方法)以确保这些脚本经过测试)。
据我了解,这些脚本不是根据数据库上下文生成的,而是使用项目中已经定义的迁移;这些迁移是通过
add
命令定义的(例如 dotnet ef migrations add vNext --context "MyDbContext" --project "./MySolution/MyDataProject/"
)。 因此,开发人员可能会对数据模型进行更改,忘记运行 migrations add
命令来添加新的迁移,从而创建一个无法更新架构的解决方案。
有没有办法测试迁移是否与代码同步/是否需要新的迁移?
我希望在 dbContext 没有更改时运行
dotnet ef migrations add
不会有任何输出;所以我可以测试是否存在新文件,并在错过该文件时终止管道(在 git 中不检查任何内容;因此新的迁移不会保留/留给开发人员手动重新运行)。
查看可用选项,只有
add
、remove
、list
和script
;因此执行此检查没有任何明显的意义。
我可以在管道中的
add
命令之前运行 script
命令,以确保它已运行;但未来的运行可能会出现问题,因为如果添加的迁移未推送到 git,则下一次迭代将不会意识到此迁移。
如果我将这个新的迁移推送到 git 就可以解决这个问题;但随后会产生一个新问题,即每个构建都会产生新的迁移,因此我们将有很多多余的混乱/这种方法将不可持续。
我能想到的最好的解决方案是运行
add
命令,然后检查生成的脚本以查看 Up
或 Down
方法在函数体内是否有任何内容;但这感觉很老套;我不确定这是否足够(例如,.Designer.cs
文件中的方法是否会发生变化,而不会在.cs
文件中的向上/向下产生任何内容?)。
我确信微软会为此在工具集中创建一些东西;但我正在努力寻找它。预先感谢您的任何帮助。
似乎适用于我尝试过的一些简单测试用例的方法是添加测试迁移,然后检查迁移目录中的
<ContextName>ModelSnapshot.cs
文件是否已更改。
该文件仅在核心迁移被修改时才出现更改。
您可以在自动化测试中生成迁移并验证它是否为空。为此:
Microsoft.EntityFrameworkCore.Design
确保 csproj 中的引用如下所示:
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="VERSION" />
以下版本不正确:
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="VERSION">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
var startupAssembly = <..TODO..>;
var dbContextAssembly = <..TODO..>;
// Magic code deduced from dotnet-ef code
// similar things happen when you run 'dotnet ef migrations add'
#pragma warning disable EF1001
var reporter = new OperationReporter(new OperationReportHandler());
var designTimeServicesBuilder =
new DesignTimeServicesBuilder(dbContextAssembly, startupAssembly, reporter, Array.Empty<string>());
var services = designTimeServicesBuilder.CreateServiceCollection(DbContext);
services.AddEntityFrameworkDesignTimeServices();
var sp = services.BuildServiceProvider();
using var scope = sp.CreateScope();
var scaffolder = scope.ServiceProvider.GetRequiredService<IMigrationsScaffolder>();
#pragma warning restore EF1001
var migration = scaffolder.ScaffoldMigration(
"TestMigration",
"TestNamespace");
var match = Regex.Match(migration,
@"protected override void Up\(MigrationBuilder migrationBuilder\)\s*\{(.*)\}.*" + // catch Up method body
@"protected override void Down\(MigrationBuilder migrationBuilder\)\s*\{(.*)\}" + // catch down method body
@"\s*}\s*}", // catch closing class and namespace - it should not be included in down method body group
RegexOptions.Multiline |
RegexOptions.Singleline);
var upCode = match.Groups[1].Value;
var downCode = match.Groups[2].Value;
Assert.True(string.IsNullOrEmpty(upCode.Trim()));
Assert.True(string.IsNullOrEmpty(downCode.Trim()));
因此,您将永远不必记住迁移,也不需要修改管道。