有没有一种方法可以通过编程检查实体框架核心中挂起的模型更改



我目前正在为ASP.NET Core WebAPI开发建立一个团队环境,使用xUnit进行单元测试,并结合GitLab CI。对于数据库通信,我们使用EF Core。

对于EF Core,我们将使用代码优先迁移,我们担心开发人员可能只更新模型,而不会为他们的模型更改创建迁移。因此,我们希望我们的CI运行代码库中存在的所有迁移,将它们与代码优先模型的当前状态进行比较,并在代码优先模型状态不等于运行所有迁移所产生的状态时失败。

有办法做到这一点吗?我在EF Core文档中找不到任何关于这方面的信息。

对于EF Core 6,来自@ErikEJ出色的EF Core Power Tools:

var migrationsAssembly = _ctx.GetService<IMigrationsAssembly>();
var hasDifferences = false;
if (migrationsAssembly.ModelSnapshot != null) {
var snapshotModel = migrationsAssembly.ModelSnapshot?.Model;
if (snapshotModel is IMutableModel mutableModel) {
snapshotModel = mutableModel.FinalizeModel();
}
snapshotModel = _ctx.GetService<IModelRuntimeInitializer>().Initialize(snapshotModel);
hasDifferences = _ctx.GetService<IMigrationsModelDiffer>().HasDifferences(
snapshotModel.GetRelationalModel(),
_ctx.GetService<IDesignTimeModel>().Model.GetRelationalModel());
}

https://github.com/ErikEJ/EFCorePowerTools/blob/5a16c37c59be854605f3e81d3131011d96c96704/src/GUI/efpt30.core/EFCoreMigrationsBuilder.cs#L98

如果你使用EF(core(5,你需要一个稍微不同的版本(也改编自@ErikEJ示例代码(

[Fact]
public void ModelDoesNotContainPendingChanges()
{
// Do not use the test database, the SQL Server model provider must be
// used as that is the model provider that is used for scaffolding migrations.
using var ctx = new DataContext(
new DbContextOptionsBuilder<DataContext>()
.UseNpgsql(DummyConnectionString)
.Options);
var modelDiffer = ctx.GetService<IMigrationsModelDiffer>();
var migrationsAssembly = ctx.GetService<IMigrationsAssembly>();
var dependencies = ctx.GetService<ProviderConventionSetBuilderDependencies>();
var relationalDependencies = ctx.GetService<RelationalConventionSetBuilderDependencies>();
var typeMappingConvention = new TypeMappingConvention(dependencies);
typeMappingConvention.ProcessModelFinalizing(((IConventionModel)migrationsAssembly.ModelSnapshot.Model).Builder, null);
var relationalModelConvention = new RelationalModelConvention(dependencies, relationalDependencies);
var sourceModel = relationalModelConvention.ProcessModelFinalized(migrationsAssembly.ModelSnapshot.Model);
var finalSourceModel = ((IMutableModel)sourceModel).FinalizeModel().GetRelationalModel();
var finalTargetModel = ctx.Model.GetRelationalModel();
var hasDifferences = modelDiffer.HasDifferences(finalSourceModel, finalTargetModel);
if(hasDifferences)
{
var changes = modelDiffer.GetDifferences(finalSourceModel, finalTargetModel);
Assert.True(false, $"{changes.Count} changes between migrations and model. Debug this test for more details");
}
Assert.False( hasDifferences );
}

感谢@ErikEJ的示例代码,我能够编写以下测试,它正是我想要的:

using FluentAssertions;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Xunit;
/// <summary>
/// Contains a test that verifies that the
/// model does not contain any changes that are not included
/// in the migrations.
/// </summary>
public class NoPendingModelChangesTest
{
private static readonly string DummyConnectionString = @"Server=localhost;Database=DoesNotExist;Trusted_Connection=True;";
/// <summary>
/// Tests that the current model does not contain any changes
/// that are not contained in the database migrators.
/// In other words: tests that the current model state equals the
/// state that results from all the migrations combined.
/// </summary>
[Fact]
public void ModelDoesNotContainPendingChanges()
{
// Do not use the test database, the SQL Server model provider must be
// used as that is the model provider that is used for scaffolding migrations.
using var ctx = new MyDatabase(
new DbContextOptionsBuilder<MyDatabase>()
.UseSqlServer(DummyConnectionString)
.Options);
var modelDiffer = ctx.GetService<IMigrationsModelDiffer>();
var migrationsAssembly = ctx.GetService<IMigrationsAssembly>();
var pendingModelChanges = modelDiffer
.GetDifferences(
migrationsAssembly.ModelSnapshot?.Model,
ctx.Model);
pendingModelChanges
.Should()
.BeEmpty(
because:
"the current model state should be equal to the state that results from all the migrations combined (try scaffolding a migration)");
}
}

最新更新