簡體   English   中英

有沒有辦法以編程方式檢查 Entity Framework Core 中掛起的模型更改?

[英]Is there a way to programmatically check pending model changes in Entity Framework Core?

我目前正在為 ASP.NET Core WebAPI 開發設置團隊環境,結合 GitLab CI 使用 xUnit 進行單元測試。 對於數據庫通信,我們使用 EF Core。

對於 EF Core,我們將使用 Code First Migrations,我們擔心開發人員可能只會更新模型,而不會為其模型更改創建遷移。 因此,我們希望 CI 運行代碼庫中存在的所有遷移,將它們與代碼優先模型的當前狀態進行比較,並在代碼優先模型狀態不等於運行所有遷移所產生的狀態時失敗。

有沒有辦法做到這一點? 我在 EF Core 文檔中找不到任何關於此的信息。

如果您使用的是 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)");
        }
    }

對於 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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM