0

I recently upgraded from EF6 alpha 1-3 to EF6 beta 1. This meant that I had to recreate all the migrations created using the alpha version.

So I tried to roll back to a migration created using EF5. But I hit the error Introducing FOREIGN KEY constraint on table may cause cycles or multiple cascade paths. I figure this is because I had neglected to fix Down migrations when I was fixing Up migrations for exactly the same problem. (Should have read this before)

Anyway, rather than try to fix it all up I am trying to reset all the migrations - as described here. I deleted my migrations table in the database and all migration .cs files, then in package manager Enable-Migrations -EnableAutomaticMigrations -Force and Add-Migration Initial

When I tried to run my application with my existing database initialiser (which has automatic migrations false) it failed because it tried to create tables that were already there. So I changed my initialiser to Database.SetInitializer(new DropCreateDatabaseAlways<MyContext>())

This time I got the Introducing FOREIGN KEY constraint on table may cause cycles or multiple cascade paths problem again during initialisation

So I changed ALL the cascadeDelete: true to cascadeDelete: false in the migration file

But I still get the same error!

Update 1 I removed all but the creation of 1 table in the migration file but I got the same error. There must be some kind of cache somewhere or it's picking up a file I don't know about or it's generating its own migration in the background

Update 2 I figured that when using DropCreateDatabaseAlways that EF must always generate the migrations and also that changing cascadeDelete to false in the migration file is the wrong place to do it. It should be done in the FluentAPI. So I added this line modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>(); to onmodelcreating. And I also deleted the Initial migration file. Then I ran the application and it correctly generated a database. I thought I'd cracked it but....

I changed initialisation to use my original configuration file:

internal sealed class Configuration : DbMigrationsConfiguration<SID2013Context>
    {
        public Configuration()
        {
            AutomaticMigrationsEnabled = false;
            AutomaticMigrationDataLossAllowed = true;
        }

         protected override void Seed(etc..
}

Then I ran the application and it reported that the model had changed. So I did Add-Migration Update-Database and a migration file to create the database was created.

The problem now is that when I run the application it tries to run another update (even though AutomaticMigrationsEnabled = false). I get the "There is already an object named 'xxx' in the database" problem again. There is an entry in the migrations table that does not match the name of the configuration file.

Community
  • 1
  • 1
Colin
  • 22,328
  • 17
  • 103
  • 197
  • many change the DB to remove the cascade on delete on the existing index/es then try again. repeat in Code . ie add a csacade on delete false in code to make sure. – phil soady Jul 19 '13 at 07:34
  • I have dropped my existing database. I have changed the name of the Initial Catalog in the connection string. There must be something wrong with the foreign key constraint. Mybe it's time to look at the sql being generated.... – Colin Jul 19 '13 at 09:42

2 Answers2

2

If you would like to start work "code first migration" using an existing database, you can do the following:

  1. I run the add-migration "InitialMigrations".
  2. It explores the existing database and make it a migration step.
  3. temporarily delete the contents of the "InitialMigrations" step:

    public partial class InitialMigrations : DbMigration {
        public override void Up()
        {
            //...
            //...
        }
    
        public override void Down()
        {
            //...
            //...
        } 
    }
    
  4. I run the update-database

  5. This creates the appropriate entries in the table __MigrationHistory.
  6. Restores the contents of the "InitialMigration" Go to work properly on an empty database.

That's it.

update: initializer

As I understand it, the 'DropCreateDatabaseAlways' always delete and create the database. This can only be used for the very first installation. If you have a working installation launchpad, you erase all data. That is why I hazardous.

I based on this article: coding.abel.nu/2012/03/… my own Initializer I'm using. I'll write.

The seed is definitely executed, so this method yields the same result, but it works even if you do not run installation, but run upgrade.

public partial class MyEntities : DbContext
{
   static MyEntities()
   { 
       Database.SetInitializer<MyEntities>(new ValidateDatabase<MyEntities>());
   }
}
/// <summary>
/// http://coding.abel.nu/2012/03/prevent-ef-migrations-from-creating-or-changing-the-database/
/// </summary>
/// <typeparam name="TContext"></typeparam>
public class ValidateDatabase<TContext> : IDatabaseInitializer<TContext>
  where TContext : DbContext
{
    public void InitializeDatabase(TContext context)
    {
        if (!context.Database.Exists())
        {
            throw new ConfigurationErrorsException(
              "Database does not exist");
        }
        else
        {
            if (!context.Database.CompatibleWithModel(true))
            {
                //from:
                //http://stackoverflow.com/questions/11611322/ef-4-3-code-first-migrations-seed-per-migration
                var cfg = new Migrations.Configuration();
                var migrator = new DbMigrator(cfg);

                ///Last in the db, (first, the reverse order)
                var dbLast = migrator.GetDatabaseMigrations().FirstOrDefault();
                ///last in the app
                var appLast = migrator.GetLocalMigrations().LastOrDefault();
                ///what is missing
                var pendings = string.Join(", ", migrator.GetPendingMigrations());
                throw new InvalidOperationException(
                  string.Format("The database ({0}) is not compatible with the entity model ({1}). Pending migrations: {2}",
                    dbLast, appLast, pendings));
            }
        }
    }
}

update: setup

Installation I'm using something like this:

http://coding.abel.nu/2012/04/update-database-msi-custom-action/

Gábor Plesz
  • 1,203
  • 1
  • 17
  • 28
  • I have an Initial Migration created using Add-Migration Initial. But when I run it using DropCreateDatabaseAlways(), it fails. I assume that I will need that migration when I deploy to production so I should fix it now rather than pass over it shouldn't I? – Colin Jul 19 '13 at 09:07
  • As I understand it, the 'DropCreateDatabaseAlways' always delete and create the database. This can only be used for the very first installation. If you have a working installation launchpad, you erase all data. That is why I hazardous. – Gábor Plesz Jul 19 '13 at 09:16
  • (Sorry for enter) I based on this article: http://coding.abel.nu/2012/03/prevent-ef-migrations-from-creating-or-changing-the-database/ my own Initializer I'm using. I'll write. – Gábor Plesz Jul 19 '13 at 09:17
  • When I tried to run my application with my existing database initialiser it failed because it tried to create tables that were already there. So I changed my initialiser to Database.SetInitializer(new DropCreateDatabaseAlways()). This is OK because I have a Seed method to insert essential data. But maybe I could go back to this if I understood why it was trying to recreate tables even though Automatic migrations is disabled? – Colin Jul 19 '13 at 09:24
  • There is a missing of the rows of the initialmigration steps from __ MigrationHistory table in the db. Or miss the __MIgrationHistory table itself. So the migration of these steps want to run. – Gábor Plesz Jul 19 '13 at 09:32
  • Actually, I am not trying to create a migration from an existing database. I am trying to recreate a database from an existing model. – Colin Jul 19 '13 at 09:37
  • Yes, I think that for me. If the migration steps exist, then go to the database is created in the following manner: (1) the update-database command searches for __ MigrationHistory table in the database, which shows the ConnectionString. (2) If there is no __ MigrationHistory the table, each migration steps executes in the database. (at least try.) (3) If there is a __ MigrationHistory table, it compares the migration steps. (4) The missing steps execute in the database. – Gábor Plesz Jul 25 '13 at 08:24
0

I finally fixed this by dropping the database manually then running the application with the original MigrateDatabaseToLatestVersion initializer. I had not realised that this would create the database if it did not exist and there was no need to use a DropCreateDatabaseAlways initializer then change to MigrateDatabaseToLatestVersion

Colin
  • 22,328
  • 17
  • 103
  • 197