Description
Everyone, we know some of you are (understandably) frustrated by the new PendingModelChangesWarning error that gets thrown when upgrading to EF 9. I wanted to share some information on the new error, why we did it and what you should do about it.
The main motivation behind this change was something that happens often: you make some change to your model (e.g. add a property), and then you forget to actually add a migration to apply it to the database. You run MigrateAsync()
(or similar) and can't figure out why your database hasn't been updated; the new error clearly tells you that your model contains changes with respect to the last migration snapshot. In other words, the point here is to help you avoid accidental error.
Now, there are some other cases where this error is thrown, even if you haven't done any changes; code that seemed to run just fine in EF 8 suddenly starts throwing in EF 9. A lot of people have assumed that this is a problem in EF, but in reality the error usually still indicates some problem in your code that was there before, but was not flagged. That's a good thing! Even here, the point is to help you clean up the problem and avoid problems. Of course, you always have the option of simply suppressing the warning (see below), so you should never be blocked for upgrading to EF 9; it's fine to do this temporarily, and then maybe come back and deal with it later, after upgrading, once you have more time to focus on it.
Here's an example: some people want to seed their database with some random Guids, so they put code such as the following in their model configuration:
modelBuilder.Entity<Blog>().HasData(new Blog { Id = Guid.NewGuid(), ... });
This looks simple enough, but it's actually quite problematic. For each migration, EF stores a snapshot of your model for that migration. When you create a new migration, EF compares your current model (represented by your code) with the latest snapshot, and the new migration represents the difference between these two. In 9.0, when you call MigrateAsync
, EF does the exact same thing, and if any changes are detected, throws the new PendingModelChangesWarning error.
Now, when you generated your last migration, Guid.NewGuid()
got evaluated at that point, and an actual random Guid gets integrated into the previous context snapshot; you can open up the XXXContextModelSnapshot.cs
in your migrations directory, and see that Guid. Now, when you call MigrateAsync
, Guid.NewGuid()
gets evaluated again, but because the random Guid it generates will differ from the one stored in the model snapshot, EF detects this as a pending model change (as if you forgot to add a migration!).
The solution to this is very simple: avoid using dynamic (or non-deterministic) APIs such as Guid.NewGuid()
, and embed specific GUIDs instead:
modelBuilder.Entity<Blog>().HasData(new Blog { Id = Guid.Parse("9e4f49fe-0786-44c6-9061-53d2aa84fab3"), ... });
Once you do this, generate a new migration once, and this whole thing will be behind you.
Why is this important? Previous versions of EF may have not thrown PendingModelChangesWarning for this, but they also didn't work well around this situation. For example, every time you added a migration, EF would see a model change in the Guid seed data, and generate a useless migration to change that value in the database - that's a bad state of affairs that the new error helps detect and fix. So while the new error is frustrating, it's there to help you get your code in better shape.
There are some other cases where this error will be thrown; we've recently published a breaking change note listing these, and explaining what to do. Also, since the error has been causing some confusion, we recently merged an improvement that will be released in 9.0.1: EF will now recognize some of the issues causing the "pending model changes" error, and throw more specific errors to let you know exactly what the problem is. For example, the case above with Guid.NewGuid()
will now be detected, and we'll tell you that you're using dynamic values in HasData
.
Hopefully the above clarifies things; we recommend looking into it and trying to fix the root cause - this way you end up with a better, more stable application. But if you choose to, you can suppress the warning, and revert back to the same situation as in previous versions of EF:
options.ConfigureWarnings(w => w.Ignore(RelationalEventId.PendingModelChangesWarning))