Display Pending Automatic Migrations from Code - entity-framework

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.

Related

EF Code First Migrations running DbMigrator.Update fails

In my ASP.NET MVC Web App backed by EF Code First using EF Migrations, I need better control of DB Updates, making them semi-automatic. The goal is to run them from code only (e.g. by running DbMigrator.Update()) but if not run, the application should still execute, ignoring possible alterations.
To accomplish this, I'm setting DbMigrationsConfiguration.AutomaticMigrationsEnabled to false, but during the first database access, I'm hitting the following Exception:
System.Data.Entity.Migrations.Infrastructure.AutomaticMigrationsDisabledException: '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.'
The good part: I was able to omit the Exception by setting disableDatabaseInitialization="true" in Web.config as described at MSDN (I didn't find any other working way).
I'm running the following code on a custom trigger:
var config = new DbMigrationsConfiguration<MyContext>
{
AutomaticMigrationsEnabled = false,
AutomaticMigrationDataLossAllowed = false,
MigrationsAssembly = Assembly.GetAssembly(typeof(InitialCreate)),
MigrationsNamespace = typeof(InitialCreate).Namespace
};
var migrator = new DbMigrator(config);
migrator.Update(); // throws Exception
The Update call throws:
System.Data.SqlClient.SqlException: 'There is already an object named 'MyEntity' in the database.'
This might have something to do with the use of disableDatabaseInitialization. Also, the call to migrator.GetLocalMigrations() lists all the Migrations but migrator.GetDatabaseMigrations() lists none (empty), while there are already some Migrations applied.
Can you spot the error?
After lots of trying, I was able to find out myself, the missing ContextKey Property caused the issues:
var config = new DbMigrationsConfiguration<MyContext>
{
AutomaticMigrationsEnabled = false,
AutomaticMigrationDataLossAllowed = false,
MigrationsAssembly = Assembly.GetAssembly(typeof(InitialCreate)),
MigrationsNamespace = typeof(InitialCreate).Namespace,
ContextKey = nameof(MyContext) // Fixed the issue
};
Very interesting that I have to set all the fields myself. In the examples I found, people didn't need to setup anything.

Use Entity Framework Code-First Migration to update DB schema when application start

I am developing window application in which DB schema can be different between each release (update view, add/remove column). Therefore I am looking for way to let EF update schema when application start up in client machine. I read some article but they point to package manager command Add-Migration, Update-Database which I can not use in my case.
I used repository pattern so when I tried Database.SetInitializer<C>(new MigrateDatabaseToLatestVersion<C, Configuration<C>>());
I got 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. exception
But when I set AutomaticMigrationsEnabled = true;I got another error: Table 'TableName' already exists.
I found a workaround, create an extension method
public static void RunMigration(this DbContext context, DbMigration migration)
{
var prop = migration.GetType().GetProperty("Operations", BindingFlags.NonPublic | BindingFlags.Instance);
if (prop != null)
{
IEnumerable<MigrationOperation> operations = prop.GetValue(migration) as IEnumerable<MigrationOperation>;
var generator = new SqlServerMigrationSqlGenerator();
var statements = generator.Generate(operations, "2008");
foreach (MigrationStatement item in statements)
context.Database.ExecuteSqlCommand(item.Sql);
}
}
And then create class MyMigration which inherit DbMigration like normal migration, and when application start, I will check and force it to run like below:
var myMigration = new MyMigration();
myMigration.Up();
this.RunMigration(myMigration);

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?

DbMigrator trying to execute every migration, not detecting executed migrations

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.

The model backing the 'DataContext' context has changed since the database was created

I am trying to use Code First with Migrations. Even though there are no current changes to my model, I'm getting an exception. When I add a migration, the up and down are empty, but I get a runtime error with the message as follows:
An exception of type 'System.InvalidOperationException' occurred in
EntityFramework.dll but was not handled in user code
Additional information: The model backing the 'MyDataContext' context
has changed since the database was created. Consider using Code First
Migrations to update the database (http://go.microsoft.com/fwlink/?
My architecture is as follows:
DataAccess project that includes the context, fluid configurations and migrations code
Model project that contains the poco classes
Web API and MVC projects that each contain the connections string in their respective web.config files.
Additionally I have the following code:
DbInitializer
public static MyDataContext Create()
{
Database.SetInitializer(new MigrateDatabaseToLatestVersion<MyDataAccess.MyDataContext, MyDataAccess.Migrations.Configuration>());
return new MyDataContext(ConfigurationManager.ConnectionStrings["MyDataContext"].ConnectionString, null);
}
I started with AutomaticMigrationsEnabled = false; in the migration Configuration constructor, as it was my understanding that this would allow (and require) me to have more control over when migrations were applied. I have also tried setting this to true but with the same result.
I added a new migration upon receiving this error, and the Up method was empty. I updated the database to this new migration, and a record was created in the _migrationHistory table, but I still receive the error when I attempt to run the application. Also, the seed data was not added to the database.
protected override void Seed(MyDataAccess.MyDataContext context)
{
IdentityResult ir;
var appDbContext = new ApplicationDbContext();
var roleManager = new RoleManager<IdentityRole>(new RoleStore<IdentityRole>(appDbContext));
ir = roleManager.Create(new IdentityRole("Admin"));
ir = roleManager.Create(new IdentityRole("Active"));
ir = roleManager.Create(new IdentityRole("InActive"));
var userNamager = new UserManager<User>(new UserStore<User>(appDbContext));
// assign default admin
var admin = new User { UserName = "administrator", Email = "myAdmin#gmail.com" };
ir = userNamager.Create(admin, "myp#55word");
ir = userNamager.AddToRole(admin.Id, "Admin");
}
where
public class ApplicationDbContext : IdentityDbContext<User>
{
public ApplicationDbContext()
: base("MyDataContext", throwIfV1Schema: false)
{
}
...
The question: If Add-Migration isn't seeing any change in the model, why do I get this error when I run? Why isn't the seed code being hit? How do I fix this, or if that can't be determined, how do I further determine the root cause?
I am not sure if you found the answer to your problem, but this other answer I found here actually did it for me:
Entity Framework model change error
I actually ended up deleting the __MigrationHistory table in SQL Server which I didn't know it was being created automatically.
The article also talks about the option to not generate it I think by using this instruction: Database.SetInitializer<MyDbContext>(null); but I have not used it, so I am not sure if it works like that
This worked for me.
Go to Package Manager Console and Run - Update-Database -force
I bet your data context is not hooking up the connection string.
Check if it's not initialized with a localdb (something like (localdb)\v11.0) and not working with that when you might think it's set to something else.
My issue ended up being a conflict between Automatic Migrations being enabled and the initializer MigrateDatabaseToLatestVersion as described here.