Detect whether model and database out of sync in EF Core 5
Asked Answered
S

1

0

I'm using the "Create and Drop" APIs since my model changes often.

After a model change, I call dotnet ef database drop, and on next run the system calls context.Database.EnsureCreated() to recreate the database. I could precede that with context.Database.EnsureDeleted() but I don't want to drop/recreate the database every time (it slows down the development "inner loop", and it'll hammer my drive).

There used to be an easy way (in old EF) to programmatically detect if the database and model are out of sync. There isn't such a built-in feature in EF Core. There are some ways (e.g., e.g.) but they are a few years old, unreliable (depend on internal features) and for older versions.

Is there a stable way - without using internal assembly features - to do this in v5?

Slavonic answered 10/11, 2021 at 3:26 Comment(7)
You could probably write your own hash function based on context.Model or context.Model.GetRelationalModel(). Otherwise you have to serialise the model somewhere and use IMigrationsModelDiffer, which creates the steps of a migration. Automatic migrations is a commonly requested feature (github.com/dotnet/efcore/issues/6214).Mimi
FYI .EnsureCreated() will create the scema if the database exists but has no tables. Not sure if dropping all tables would be quicker than dropping the database.Mimi
@JeremyLakeman That's an interesting (but laborious) idea. That function returns a string for the entire schema, so I could hash that and store it in a special single-field table using a direct sql call (ExecuteSqlRaw or whatever it's called). Then on next run compare, and if there are changes, call EnsureDeleted first. Would probably work! Thanks for the valuable tip!Slavonic
Actually, you could probably hash the string from context.Model.DebugView.LongViewMimi
I found it on context.ChangeTracker.DebugView.LongView. Why do you believe it's the better option?Slavonic
That DebugView is only related to the objects that are currently being tracked.Mimi
@JeremyLakeman Oh so you mean it ignores all the PG-internal tables and whatnot, and just considers my tables... Even better, thanks!Slavonic
M
2

You can get EF Core to serialise your schema (context.Model) into a string using .Model.DebugView.LongView.

Then I would calculate a hash value from that string, and compare that hash to a value stored in a table.

Since EF Core doesn't expose a method to run a raw select query, particularly if you can't depend on the schema being up-to-date. You would either need to use SqlConnection directly to query the table.

Or construct your comparison as an insert / update statement so you can decide what to do based on number of rows affected. Since you're going to destroy the database immediately, side effects of that sql can be ignored.

Mimi answered 10/11, 2021 at 4:27 Comment(3)
If you're using sql server, you could store the model hash as an extended property instead of inside a table (mssqltips.com/sqlservertip/5384/…)Mimi
I'm using postgres, but I'm sure that tip will help many others, thanks!Slavonic
Thanks, this answer helped me greatly. Only there's a problem that context.Model.DebugView.LongView won't compile in ef core 5.0.11. What does work is ((Microsoft.EntityFrameworkCore.Metadata.Internal.Model)context.Model).DebugView.LongView. But see here: https://github.com/dotnet/efcore/issues/24434... that the correct way to get the long debug view is: context.Model.ToDebugString(Microsoft.EntityFrameworkCore.Infrastructure.MetadataDebugStringOptions.LongDefault)Solnit

© 2022 - 2024 — McMap. All rights reserved.