有没有办法以编程方式检查 Entity Framework Core 中的待定模型更改?
Posted
技术标签:
【中文标题】有没有办法以编程方式检查 Entity Framework Core 中的待定模型更改?【英文标题】:Is there a way to programmatically check pending model changes in Entity Framework Core? 【发布时间】:2020-06-26 08:25:33 【问题描述】:我目前正在为 ASP.NET Core WebAPI 开发设置团队环境,使用 xUnit 和 GitLab CI 进行单元测试。对于数据库通信,我们使用 EF Core。
对于 EF Core,我们将使用 Code First 迁移,我们担心开发人员可能只会更新模型,而不会为他们的模型更改创建迁移。 因此,我们希望我们的 CI 运行代码库中存在的所有迁移,将它们与代码优先模型的当前状态进行比较,并在代码优先模型状态不等于运行所有迁移所产生的状态时失败。
有没有办法做到这一点?我在 EF Core 文档中找不到任何关于此的内容。
【问题讨论】:
AFAIK 无法在代码中比较模型状态,但您可以使用 CLI 生成 SQL 命令并进行比较?不是那种干净,但你有一个基本的比较。 我在这里有一些代码可以做到这一点:github.com/ErikEJ/EFCorePowerTools/blob/master/src/GUI/efpt/… - 但为什么不首先考虑数据库呢? @ErikEJ 因为 EF Core 不首先支持数据库,并且迁移提供了很多好处。感谢您的示例代码,我会检查一下:)。 @hyvte 感谢您的建议,我确实想到了这一点,但我更喜欢更“干净”的方法。 EF Core 优先支持数据库! 【参考方案1】:如果您使用的是 EF (core) 5,则需要稍微不同的版本(也改编自 @ErikEJ sample code)
[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 );
【讨论】:
如何将其迁移到 EF Core 6?【参考方案2】:对于 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
【讨论】:
【参考方案3】:感谢@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)");
【讨论】:
以上是关于有没有办法以编程方式检查 Entity Framework Core 中的待定模型更改?的主要内容,如果未能解决你的问题,请参考以下文章