Как добавить новую миграцию кода в первую очередь для вновь созданной базы данных?

Я включил миграцию вначале кода в моем проекте инфраструктуры сущностей и добавил несколько миграций, которые делают такие вещи, как переименование таблиц. Однако теперь я удалил базу данных и заставил инфраструктуру сущностей генерировать совершенно новую базу данных на основе моей последней модели данных. Если я попытаюсь бежать:

PM> Add-Migration TestMigration

... он говорит мне, что мне нужно сначала применить существующие миграции. Итак, я бегу:

PM> Update-Database

... но проблема в том, что он пытается обновить базу данных, которая не нуждается в обновлении; это уже основано на последней модели данных. Поэтому я получаю сообщение об ошибке при попытке переименовать таблицу, которой сейчас не существует.

Есть ли какой-нибудь способ, которым я могу указать переносу данных, что моя база данных обновлена и не требует выполнения каких-либо переносов на ней? Что я должен делать?

 Jcl27 окт. 2012 г., 21:32
Ты пробовалаUpdate-Database -Force? (это просто дикая догадка)
 Jez27 окт. 2012 г., 22:10
@Jcl не имеет никакого значения.

Ответы на вопрос(3)

__MigrationHistory, Миграции определяются из указанной сборки.

Может быть, это помогает.

Я благодарю @Jez за первоначальную идею.

/// <summary>
/// An implementation of IDatabaseInitializer that will:
/// 1. recreate database only if the database does not exist 
/// 2. actualize __MigrationHistory to match current model state (i.e. latest migration)
/// </summary>
/// <typeparam name="TContext">The type of the context.</typeparam>
public class CreateDatabaseIfNotExistsAndMigrateToLatest<TContext> : CreateDatabaseIfNotExists<TContext>
    where TContext : DbContext
{
    private readonly Assembly migrationsAssembly;

    /// <summary>
    /// Gets the migration metadata for types retrieved from <paramref name="assembly"/>. Types must implement <see cref="IMigrationMetadata"/>.
    /// </summary>
    /// <param name="assembly">The assembly.</param>
    /// <returns></returns>
    private static IEnumerable<IMigrationMetadata> GetMigrationMetadata(Assembly assembly)
    {
        var types = assembly.GetTypes().Where(t => typeof(IMigrationMetadata).IsAssignableFrom(t));
        var migrationMetadata = new List<IMigrationMetadata>();
        foreach (var type in types)
        {
            migrationMetadata.Add(
                (IMigrationMetadata)Activator.CreateInstance(type));
        }

        return migra,tionMetadata.OrderBy(m => m.Id);
    }

    /// <summary>
    /// Gets the provider manifest token.
    /// </summary>
    /// <param name="db">The db.</param>
    /// <returns></returns>
    private static string GetProviderManifestToken(TContext db)
    {
        var connection = db.Database.Connection;
        var token = DbProviderServices.GetProviderServices(connection).GetProviderManifestToken(connection);

        return token;
    }

    /// <summary>
    /// Gets the migration SQL generator. Currently it is <see cref="SqlServerMigrationSqlGenerator"/>.
    /// </summary>
    /// <returns></returns>
    private static MigrationSqlGenerator GetMigrationSqlGenerator()
    {
        return new SqlServerMigrationSqlGenerator();
    }

    /// <summary>
    /// Creates the operation for inserting into migration history. Operation is created for one <paramref name="migrationMetadatum"/>.
    /// </summary>
    /// <param name="migrationMetadatum">The migration metadatum.</param>
    /// <returns></returns>
    private static InsertHistoryOperation CreateInsertHistoryOperation(IMigrationMetadata migrationMetadatum)
    {
        var model = Convert.FromBase64String(migrationMetadatum.Target);

        var op = new InsertHistoryOperation(
            "__MigrationHistory",
            migrationMetadatum.Id,
            model,
            null);

        return op;
    }

    /// <summary>
    /// Generates the SQL statements for inserting migration into history table.
    /// </summary>
    /// <param name="generator">The generator.</param>
    /// <param name="op">The operation.</param>
    /// <param name="token">The token.</param>
    /// <returns></returns>
    private static IEnumerable<MigrationStatement> GenerateInsertHistoryStatements(
        MigrationSqlGenerator generator,
        InsertHistoryOperation op,
        string token)
    {
        return generator.Generate(new[] { op }, token);
    }

    /// <summary>
    /// Runs the SQL statements on database specified by <paramref name="db"/> (<see cref="DbContext.Database"/>).
    /// </summary>
    /// <param name="statements">The statements.</param>
    /// <param name="db">The db.</param>
    private static void RunSqlStatements(IEnumerable<MigrationStatement> statements, TContext db)
    {
        foreach (var statement in statements)
        {
            db.Database.ExecuteSqlCommand(statement.Sql);
        }
    }

    /// <summary>
    /// Initializes a new instance of the <see cref="CreateDatabaseIfNotExistsAndMigrateToLatest{TContext}"/> class.
    /// </summary>
    /// <param name="migrationsAssembly">The migrations assembly.</param>
    public CreateDatabaseIfNotExistsAndMigrateToLatest(Assembly migrationsAssembly)
    {
        this.migrationsAssembly = migrationsAssembly;
    }

    protected override void Seed(TContext context)
    {
        base.Seed(context);

        // Get migration metadata for migrationAssembly
        var migrationMetadata = GetMigrationMetadata(migrationsAssembly);

        // Crate DbContext
        var db = Activator.CreateInstance<TContext>();
        // Remove newly created record in __MigrationHistory
        db.Database.ExecuteSqlCommand("DELETE FROM __MigrationHistory");

        // Get provider manifest token
        var token = GetProviderManifestToken(db);
        // Get sql generator
        var generator = GetMigrationSqlGenerator();

        foreach (var migrationMetadatum in migrationMetadata)
        {
            // Create history operation
            var op = CreateInsertHistoryOperation(migrationMetadatum);
            // Generate history insert statements
            var statements = GenerateInsertHistoryStatements(generator, op, token);
            // Run statements (SQL) over database (db)
            RunSqlStatements(statements, db);
        }
    }
}
 WillEllis24 сент. 2015 г., 19:07
Кто-нибудь знает, как этот подход может быть принят в EF6? похоже, что они изменили классы HistoryOperation / MigrationOperation таким образом, чтобы остановить этот вид ручной вставки.
 goodface8712 дек. 2014 г., 21:57
Я столкнулся с этой проблемой с EF6. Я пытался реализовать это решение, но оказалось, что InsertHistoryOperation больше не доступен. Есть ли другой способ, которым мы можем сгенерировать операцию для вставки истории без InsertHistoryOperation?
Решение Вопроса

что моя база данных обновлена, и (что неудивительно) основана на изменении__MigrationHistory таблица, которая использует миграции с кодом в первую очередь, чтобы определить, какие миграции применить к БД при запускеUpdate-Database.

Кстати, изучая этот ответ, я наткнулся на очень хороший справочник по командам миграции кода, который можно найти по адресу:http://dotnet.dzone.com/articles/ef-migrations-command

Когда EF автоматически создает базу данных, она всегда помещает одну запись в__MigrationHistory таблица, и эта запись будет иметь MigrationId(currentDateTime)_InitialCreate, Это представляет собой первоначальное создание базы данных, которую EF только что выполнил. Однако ваша история миграции не будет начинаться с этого MigrationId, потому что вы начнете с чего-то другого.

Чтобы «одурачить» миграции кода в первую очередь, думая, что вы находитесь на последней миграции, вам нужно удалить это(currentDateTime)_InitialCreate запись от__MigrationHistory таблицу вновь созданной БД и вставьте то, что было бы там, если бы у вас все еще была старая БД, к которой применены миграции.

Итак, сначала удалите все из вновь созданных БД__MigrationHistory Таблица. Затем зайдите в консоль диспетчера пакетов и запустите:

PM> Update-Database -Script

Из полученного сценария SQL вытащите все строки, начинающиеся с:

INSERT INTO [__MigrationHistory]...

Затем запустите теINSERT заявления в контексте вновь созданной базы данных. Проверьте, что каждая из этих строк теперь существует в вашем__MigrationHistory Таблица. При следующем запуске:

PM> Update-Database

... вы должны получить сообщение «Нет отложенных миграций на основе кода». Поздравляю - вы обманули миграцию вначале кода, думая, что вы сейчас в последней миграции, и вы можете продолжить с того места, где остановились, добавив новые миграции отсюда.

Я действительно думаю, что должен быть какой-то автоматизированный способ сделать это встроенным в код EF сначала, хотя ... возможно, они должны добавить что-то вроде

PM> Update-Database -MigrationsTableOnly

... в результате чего он будет захламлять существующие записи в таблице миграций и просто вставлять новые записи в историю миграций для каждой миграции, определенной в вашем проекте, но на самом деле не пытаться запускать миграции. Ах хорошо.

ОБНОВИТЬ
Я нашел способ красиво это автоматизировать, используя метод Seed пользовательского инициализатора. По сути, метод Seed удаляет существующие данные истории миграции при создании БД и вставляет историю ваших миграций. В моем конструкторе контекста базы данных я регистрирую пользовательский инициализатор следующим образом:

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

Сам пользовательский инициализатор выглядит так:

/// <summary>
/// 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.
/// </summary>
public class MyDatabaseContextMigrationHistoryInitializer : CreateDatabaseIfNotExists<MyDatabaseContext> {
    /// <summary>
    /// Sets up this context's migration history with the entries for all migrations that have been created in this project.
    /// </summary>
    /// <param name="context">The context of the database in which the seed code is to be executed.</param>
    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')");
    }
}
 Mzn04 мар. 2014 г., 09:33
Я просто хотел добавить вычисляемый столбец ... Возможно, ADO .NET проще, чем Entity Framework, в конце концов ... viva new SqlCommand ()
 Isaac Abraham08 февр. 2013 г., 15:21
Спасибо за это. Это немного дерьмо на самом деле - должна быть лучшая история для создания чистой базы данных, основанной на последней схеме.

MigrationHistory таблица в sql server (подsystem папка) в нем есть строка для вашей миграции и хэш базы данных, который должен быть таким же, как и в вашем файле миграции, просто скопируйте его из базы данных в файл.

Другими словами, вам нужно синхронизироватьMigrationHistory таблица с актуальными миграциями.

Ваш ответ на вопрос