I am currently in the progress of setting up a team environment for ASP.NET Core WebAPI development, using xUnit for unit tests in combination with GitLab CI. For database communication, we use EF Core.
For EF Core we are going to use Code First Migrations and we are worried that a developer might only update the model and not also create a migration for their model change. Thus, we want our CI to run all migrations that exist in the codebase, compare them with the current state of the code first model and fail when the code first model state is not equal to the state that results from running all the migrations.
Is there a way to do this? I cannot find anything about this in the EF Core documentation.
For EF Core 6, from @ErikEJ's excellent 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
If you are using EF (core) 5 you'll need a slightly different version (also adapted from @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 );
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With