DbMigrator trying to execute every migration, not detecting executed migrations - entity-framework

I am trying to create a service call that will update an EF code first database to the latest version. However, it is always trying (and failing obviously) to execute all migrations, whether they were executed already or not.
Here's my code:
var config = new DbMigrationsConfiguration<MyContext>();
config.MigrationsAssembly = typeof (MyContext).Assembly;
config.MigrationsNamespace = "Context.Migrations";
//This had no effect
//config.SetHistoryContextFactory(connectionString.ProviderName, (connection, s) => new HistoryContext(context.Database.Connection, "dbo"));
var migrator = new DbMigrator(config);
//This gets every single migration, even the ones already executed before
var migrations = migrator.GetPendingMigrations();
//This gets 0 migrations
var existing = migrator.GetDatabaseMigrations();
//This gets all migrations
var other = migrator.GetLocalMigrations();
//This returns true! So it knows all the migrations are already executed
var differ = context.Database.CompatibleWithModel(true);
//This will throw an exception because it's trying to create existing tables!
migrator.Update();
I can confirm that the migration history table contains [dbo].[__MigrationHistory] references all the old migrations.
I checked the connection strings in the migrator. I also tried setting the history context factory manually in case it is looking somewhere else with no result. Also, running update-database directly from the console works and says no pending updates.
Any help is appreciated

Make sure DbMigrationsConfiguration.ContextKey is set correctly.
The default value after this line
var config = new DbMigrationsConfiguration<MyContext>();
will be like this
"System.Data.Entity.Migrations.DbMigrationsConfiguration`1[MyContextNamespace.MyContext]"
You need to add the following line
config.ContextKey = "{Your_Context_Key}";
If you don't know the context key, open your [dbo].[__MigrationHistory] table and look at the ContextKey column.
Once you set it up correctly, if your database is up-to-date, migrator.GetPendingMigrations() should return empty and migrator.GetDatabaseMigrations() should return all migrations.

If your database is all updated, and you have no need for the old migration tables, then what I would suggest that you do is delete the migrations folder and recreate it using
enable-migrations
I do it all the time when the migrations mess up. I never need the historical migrations - once they have done their job - they are DONE! Be sure to save your seed method if you are seeing through the configuration. You can look at my earlier post on this, maybe that will help too.
Migrations
Good luck.

Related

Entity Framework Code First post migration step?

Is there some way to add a post migration method to Code First EF migration?
All the stored proc's are in the Visual Studio project. Right now there is an approach to load the stored proc resource from the file and put it into it's own migration:
protected override void Up(MigrationBuilder migrationBuilder)
{
var script = ScriptMgr.LoadStoredProc( "StoredProcThatChanged.sql" );
migrationBuilder.Sql( script );
}
There is a weak link in this process: Each time the script changes (StoredProcThatChanged.sql) a new migration needs to be created to make sure it executes again. The problem is the previous migration is also loading the same file. When generating a new script, the process reads in the one file both times, effectively changing the previous migration. Which is a classic no-no.
This would be resolved if there is a post migration method where ALL stored proc's can be reapplied to the DB. Is such a step possible? If so, now is it done?
I have been digging into the efcore source code and it looks like it is possible, not ideal, but there might be a way...
It looks like efcore has an interface called IMigrator. It contains the method string GenerateScript(...). The implementation of it, class Migrator, has comments all over the place saying that it's implementation of GenerateScript is internal and subject to change. But... It looks to me like I can achieve my end goal:
class MyMigrator : Microsoft.EntityFrameworkCore.Migrations.Internal.Migrator
{
public string GenerateScript(
string? fromMigration = null,
string? toMigration = null,
MigrationsSqlGenerationOptions options = MigrationsSqlGenerationOptions.Default)
{
var result = base.GenerateScript( fromMigration, toMigration, options);
results += MyPostSteps(...);
return results;
}
}
Will this work and does anyone know how I might go about replacing the default Migrator with the MyMigrator?

Creating new database code first when project contains migrations

I have an EF6 project that is supposed to create its own database. So in the constructor of the database context, I set the Initializer
Database.SetInitializer<AudmDatabaseContext>(new AudmDatabaseInitializer());
AudmDatabaseInitializer is a class of type System.Data.Entity.CreateDatabaseIfNotExists that implements my seed method to fill some default data after creation.
I found that if I open a new context and try accessing some data, the DB would automatically be created, so my program can be started with a special parameter that creates a new db. That parameter triggers this bit of code
using (var db = new AudmDatabaseLib.Model.AudmDatabaseContext())
{
if (!db.Database.Exists())
{
bool created = db.Database.CreateIfNotExists();
}
}
And here's the weird thing: this works perfectly as long as my project contains no migrations. If there's any migrations present, the migrations start to be executed.. and of course they fail because there's no database. If I remove the migration .cs files (Configuration.cs can be left.. it seems to have no impact), then things work as expected, and then upon the first regular normal run, the migrations are performed.
It seems that in case there are migrations, those come first.. even though I see that db.Database.CreateIfNotExists is being executed, so I know it knows the DB isn't there and executing migrations make no sense.
Here's my migration config btw
public Configuration()
{
AutomaticMigrationsEnabled = true;
ContextKey = "AudmDatabaseLib.AudmDatabaseContext";
AutomaticMigrationDataLossAllowed = true;
}
How can I ensure that the DB creation runs prior to the automatic migrations? Or is there a way to temporarily disable automatic migrations before I call CreateIfNotExists?

Display Pending Automatic Migrations from Code

Background
We work in a geographically distributed team. Every so often, a team on the other side of the world creates a build for the integration server that fails due to
Unable to update database to match the current model because there are pending changes and automatic migration is disabled. Either write the pending model changes to a code-based migration or enable automatic migration. Set DbMigrationsConfiguration.AutomaticMigrationsEnabled to true to enable automatic migration.
We use code based migrations.
To simplify trouble shooting, I'm looking for a method to display what automatic automatic migration would have been applied, were automatic migrations allowed, from code. The environment to script new migrations does not exist on the integration server, where the problem manifests.
Attempted Solution
I attempted to use DbMigrator as outlined here, but that seems to only display code based migrations that have not yet been applied.
Question
Is there a code-based method that will display the changes that would be applied, were automatic migrations enabled?
Here is a solution that worked for me
public string GetAutomaticMigrationsScript<T>() where T : DbMigrationsConfiguration, new()
{
var configuration = new T();
configuration.AutomaticMigrationDataLossAllowed = true;
configuration.AutomaticMigrationsEnabled = true;
var migrator = new DbMigrator(configuration);
var scriptor = new MigratorScriptingDecorator(migrator);
var script = scriptor.ScriptUpdate(sourceMigration: null, targetMigration: null);
return script;
}
Usage:
Console.WriteLine(GetAutomaticMigrationsScript<MyConfiguration>());
The variable script will contain the SQL Script that would be run to perform an automatic migration. This code will not actually perform the migration, but you can paste the contents of script into SSMS and run the migration there.

Entity Framework telling me the model backing the context has changed

I have a weird problem with Entity Framework code first migrations. I've been using EF and code first migrations on a project for months now and things are working fine. I recently created a new migration and when running Update-Database a restored backup of my database I get this error:
The model backing the context has changed since the database was
created. Consider using Code First Migrations to update the database
The migration does something like the following:
public override void Up()
{
using (SomeDbContext ctx = new SomeDbContext())
{
//loop through table and update rows
foreach (SomeTable table in ctx.SomeTables)
table.SomeField = DoSomeCalculation(table.SomeField);
ctx.SaveChanges();
}
}
I'm not using the Sql() function because DoSomeCalculation must be done in C# code.
Usually when I get something like this is means that I have updated my model somehow and forgot to create a migration. However that's not the case this time. The weird thing is that the error isn't even occurring on a migration that I created a few days ago and had been working fine.
I looked a quite a few articles about this and they all seems to say call
Database.SetInitializer<MyContext>(null);
Doing that does seem to work, but my understanding (based on this article) is that doing that will remove EF's ability to determine when the database and model are out of sync. I don't want to do that. I just want to know why it thinks they are out of sync all of a sudden.
I also tried running Add-Migration just to see if what it thought changed about the model but it won't let me do that stating that I have pending migrations to run. Nice catch 22, Microsoft.
Any guesses as to what's going on here?
I'm wondering if maybe the fact that migration listed above is using EntityFramework is the problem. Seems like maybe since it's not the latest migration anymore, when EF gets to it tries to create a SomeDbContext object it checks the database (which is not fully up to date yet since we're in the middle of running migrations) against my current code model and then throws the "context has changed" error.
It's possibly related to your using EF within the migration. I'm not sure how you're actually managing this, unless you've set a null database initialiser.
If you need to update data within a migration, use the Sql function, e.g.
Sql("UPDATE SomeTable SET SomeField = 'Blah'");
You should note that the Up() method is not actually running at the time of doing the migration, it's simply used to set up the migration which is then run later. So although you may think you've done something in the migration above the bit where you're using EF, in reality that won't have actually run yet.
If you cannot refactor your calculation code so it can be written in SQL, then you would need to use some mechanism other than migrations to run this change. One possibility would be to use the Seed method in your configuration, but you would need to be aware that this does not keep track of whether the change has been run or not. For example...
internal sealed class Configuration : DbMigrationsConfiguration<MyContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
}
protected override void Seed(MyContext context)
{
// Code here runs any time ANY migration is performed...
}
}
I tried replacing the EntityFramework code with regular ADO.NET code and it seems to work. Here is what it looks like:
public override void Up()
{
Dictionary<long, string> idToNewVal = new Dictionary<long, string>();
using (SqlConnection conn = new SqlConnection("..."))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand("SELECT SomeID, SomeField FROM SomeTable", conn))
{
SqlDataReader reader = cmd.ExecuteReader();
//loop through all fields, calculating the new value and storing it with the row ID
while (reader.Read())
{
long id = Convert.ToInt64(reader["SomeID"]);
string initialValue = Convert.ToString(reader["SomeField"]);
idToNewVal[id] = DoSomeCalculation(initialValue);
}
}
}
//update each row with the new value
foreach (long id in idToNewVal.Keys)
{
string newVal = idToNewVal[id];
Sql(string.Format("UPDATE SomeTable SET SomeField = '{0}' WHERE SomeID = {1}", newVal, id));
}
}

Entity framework attach update not working

I'm trying to update a POCO object using entity framework in the following way:
context.Jobs.Attach(job);
context.SaveChanges();
That does not work. No error is thrown, it just isn't updating the values in the database.
I tried:
context.Jobs.AttachTo("Jobs", job);
context.SaveChanges();
Nothing wrongs, still no error and no updates.
What about changing the ObjectState?
context.ObjectStateManager.ChangeObjectState(job, System.Data.EntityState.Modified);
From MSDN: ObjectStateManager.ChangeObjectState Method.
I guess you are working with detached object - check second part of this answer.
another reason that this may not work is when the corresponding Jobs.cs file has been committed but the .edmx file has not. This means that the property is present but not mapped and therefore EF does not consider the object modified. For example:
...
using (var dao = new DbContext())
{
dao.Jobs.Attach(job);
job.SomeProperty = 1234; // SomeProperty exists but is not in the .edmx
dao.SaveChanges();
}
if SomeProperty is present in Jobs.cs but missing from the .edmx file, this code will compile and execute without a hint that anything is wrong but SomeProperty will not be updated in the Database. Took me the best part of a day to find this one.
you have to get the job first then you could successfully update it, chk below snippet
var job = context.Jobs.Where(p => p.Id == id).FirstOrDefault();
//apply your changes
job.Title = "XXXX";
///....
context.SaveChanges();
My issue was that I was attaching after I updated the object, when in-fact, you have to attach BEFORE you update any properties
context.Table.Attach(object);
object.MyProperty = "new value";
context.Table.SaveChanges();