How to add a new code-first migration with a newly-generated database?

前端 未结 3 2173
花落未央
花落未央 2021-02-11 05:09

I\'ve enabled code-first migrations on my entity framework project, and have added several migrations which do things like rename tables. However, I have now deleted the databa

3条回答
  •  一生所求
    2021-02-11 05:59

    I've found a way to indicate that my database is up-to-date, and it's (unsurprisingly) based on modifying the __MigrationHistory table, which code-first migrations uses to determine which migrations to apply to the DB when you run Update-Database.

    By the way, whilst researching this answer I came across a very nice reference for code-first migrations commands, which can be found at: http://dotnet.dzone.com/articles/ef-migrations-command

    When the database is created from scratch automatically by EF, it will always put a single entry into the __MigrationHistory table, and that entry will have the MigrationId (currentDateTime)_InitialCreate. This represents the initial creation of the database that EF has just carried out. However, your migration history is not going to begin with that MigrationId because you will have started with something else.

    To "fool" code-first migrations into thinking that you're on the latest migration, you need to delete that (currentDateTime)_InitialCreate entry from the __MigrationHistory table of the newly-created DB, and insert what would have been there if you still had the old DB which had had the migrations applied to it.

    So, first delete everything from the newly-generated DB's __MigrationHistory table. Then, go into package manager console and run:

    PM> Update-Database -Script
    

    From the resulting SQL script, pull out all the lines beginning with:

    INSERT INTO [__MigrationHistory]...
    

    Then, run those INSERT statements within the context of the newly-created database. Check that each of those rows now exists in your __MigrationHistory table. When you next run:

    PM> Update-Database
    

    ... you should get a message saying "No pending code-based migrations." Congratulations - you've fooled code-first migrations into thinking you're now on the latest migration, and you can continue where you left off adding new migrations from here.

    I do think there should be some automated way of doing this built into EF code-first, though... maybe they should add something like:

    PM> Update-Database -MigrationsTableOnly
    

    ... whereby it would clobber the existing entries in the migrations table, and just insert the new entries into migrations history for each migration defined in your project, but not actually try and run the migrations. Ah well.

    UPDATE
    I've found a way to automate this nicely, using a custom initializer's Seed method. Basically the Seed method deletes the existing migration history data when the DB is created, and inserts your migrations history. In my database context constructor, I register the custom initializer like so:

    public class MyDatabaseContext : DbContext {
        public MyDatabaseContext() : base() {
            Database.SetInitializer(new MyDatabaseContextMigrationHistoryInitializer());
        }
    

    The custom initializer itself looks like this:

    /// 
    /// This initializer clears the __MigrationHistory table contents created by EF code-first when it first
    /// generates the database, and inserts all the migration history entries for migrations that have been
    /// created in this project, indicating to EF code-first data migrations that the database is
    /// "up-to-date" and that no migrations need to be run when "Update-Database" is run, because we're
    /// already at the latest schema by virtue of the fact that the database has just been created from
    /// scratch using the latest schema.
    /// 
    /// The Seed method needs to be updated each time a new migration is added with "Add-Migration".  In
    /// the package manager console, run "Update-Database -Script", and in the SQL script which is generated,
    /// find the INSERT statement that inserts the row for that new migration into the __MigrationHistory
    /// table.  Add that INSERT statement as a new "ExecuteSqlCommand" to the end of the Seed method.
    /// 
    public class MyDatabaseContextMigrationHistoryInitializer : CreateDatabaseIfNotExists {
        /// 
        /// Sets up this context's migration history with the entries for all migrations that have been created in this project.
        /// 
        /// The context of the database in which the seed code is to be executed.
        protected override void Seed(MyDatabaseContext context) {
            // Delete existing content from migration history table, and insert our project's migrations
            context.Database.ExecuteSqlCommand("DELETE FROM __MigrationHistory");
            context.Database.ExecuteSqlCommand("INSERT INTO __MigrationHistory (MigrationId, Model, ProductVersion) VALUES ('201210091606260_InitialCreate', 0x1F8B0800000000000400ECBD07601C499625262F6DCA7B7F4AF54AD7E074A..., '5.0.0.net40')");
            context.Database.ExecuteSqlCommand("INSERT INTO __MigrationHistory (MigrationId, Model, ProductVersion) VALUES ('201210102218467_MakeConceptUserIdNullable', 0x1F8B0800000000000400ECBD07601C499625262F6DCA7B7F4..., '5.0.0.net40')");
            context.Database.ExecuteSqlCommand("INSERT INTO __MigrationHistory (MigrationId, Model, ProductVersion) VALUES ('201210231418163_ChangeDateTimesToDateTimeOffsets', 0x1F8B0800000000000400ECBD07601C499625262F6D..., '5.0.0.net40')");
            context.Database.ExecuteSqlCommand("INSERT INTO __MigrationHistory (MigrationId, Model, ProductVersion) VALUES ('201210251833252_AddConfigSettings', 0x1F8B0800000000000400ECBD07601C499625262F6DCA7B7F4AF54AD7E..., '5.0.0.net40')");
            context.Database.ExecuteSqlCommand("INSERT INTO __MigrationHistory (MigrationId, Model, ProductVersion) VALUES ('201210260822485_RenamingOfSomeEntities', 0x1F8B0800000000000400ECBD07601C499625262F6DCA7B7F4AF5..., '5.0.0.net40')");
        }
    }
    

提交回复
热议问题