Does an equivalent to Database.CompatibleWithModel(bool) exist in EF Core
Asked Answered
B

1

7

I'm working on a project that uses EFCore 2.1.0-preview1-final code first approach. Like in EF6 (and previous versions) I want to ensure the compatibility of my DbContext (and models) to the database.

In EF6 it was enabled by default and it was possible to deactivate it with Database.CompatibleWithModel(false);. As far as I know EF uses the __MigrationHistory table where the model information was stored. EFCore has no such column in __EFMigrationsHistory table that could provide such information.

I cannot find any information about compatibility check in EFCore. But I want to ensure the compatibility, because after some tests it seems not to be enabled by default (or does exist). I tested it by adding and deleting some columns from database manually and executing the application after the modifications. I - against my expectation - received no exception.

Does anybody know how to achieve a compatibility check from model to database and vice versa like in EF6 for EFCore? Or could provide some helpful links for further information about it or why it doesn't exist in EFCore (because it is not necessary)?

Boylston answered 12/3, 2018 at 16:59 Comment(2)
EF6 didn't do that much. It just makes sure the model hasn't changed since the last time EF changed the database schema. It doesn't detect schema changes made outside of EF.Expectancy
Hm, okay. I have no idea how I could do exactly the same with EF Core. Maybe I have to check out the implementation in EF.Boylston
E
3

I strongly advise against doing this since it uses internal components and is error-prone, but here's one way to do it.

using (var db = new MyDbContext())
{
    var reporter = new OperationReporter(handler: null);
    var designTimeServiceCollection = new ServiceCollection()
        .AddSingleton<IOperationReporter>(reporter)
        .AddScaffolding(reporter);
    new SqlServerDesignTimeServices().ConfigureDesignTimeServices(designTimeServiceCollection);

    var designTimeServices = designTimeServiceCollection.BuildServiceProvider();

    var databaseModelFactory = designTimeServices.GetService<IScaffoldingModelFactory>();
    var databaseModel = (Model)databaseModelFactory.Create(
        db.Database.GetDbConnection().ConnectionString,
        tables: new string[0],
        schemas: new string[0],
        useDatabaseNames: false);

    var currentModel = db.Model;

    // Fix up the database model. It was never intended to be used like this. ;-)
    foreach (var entityType in databaseModel.GetEntityTypes())
    {
        if (entityType.Relational().Schema == databaseModel.Relational().DefaultSchema)
        {
            entityType.Relational().Schema = null;
        }
    }
    databaseModel.Relational().DefaultSchema = null;
    databaseModel.SqlServer().ValueGenerationStrategy =
        currentModel.SqlServer().ValueGenerationStrategy;
    // TODO: ...more fix up as needed

    var differ = db.GetService<IMigrationsModelDiffer>();

    if (differ.HasDifferences(databaseModel, currentModel))
    {
        throw new Exception("The database and model are out-of-sync!");
    }
}
Expectancy answered 13/3, 2018 at 19:1 Comment(4)
Thanks a lot. Well, I will not use it, because of your advice against it. I just wanted to know how it would have been possible. Do you have some links where I could read about the reason why EFCore doesn't support this functionality out of the box?Boylston
No documentation. We see two development options: 1) your domain model is the source of truth and you use migrations to manage the database schema. 2) your database schema is the source of truth and you use reverse engineering/update model from database (#831) to keep your domain model in sync. Not having a single source of truth gets too messy and requires tools like CompatibleWithModel to help keep everything in sync. This answer describes other synchronization checks you can do.Expectancy
Okay, I totally agree with you. Only a remark as following scenario: Another developer or administrator deletes an integer column in database. At this time the database is for reading data only - no inserting new data. You will not receive an exception because of schema modification. So you have all the time the default int value 0. If I use EF6 the compatibility check will throw an exception at creation of first DbContext object. (same behavior for every primitive data type) In past I already had such a issue. Thanks. :)Boylston
I asked an updated question here. Is there a "safe" way to do this in v5 without depending on assembly internals?Displume

© 2022 - 2024 — McMap. All rights reserved.