Добавление операторов "GO" в миграцию Entity Framework

Итак, у меня есть приложение с тонной миграцией, созданной инфраструктурой Entity. Мы хотим получить script для всех перемещений сразу, и использование тега -Script работает нормально.

Однако... он не добавляет в SQL инструкции GO, дающие нам такие проблемы, как Alter view should be the first statement in a batch file...

Я искал и вручную добавлял Sql("GO"); справку с этой проблемой, но только для всего script. Когда я снова использую диспетчер консоли пакета, он возвращает исключение.

System.Data.SqlClient.SqlException (0x80131904): Could not find stored procedure 'GO'.

Есть ли способ добавить эти теги GO только при использовании тега -Script? Если нет, то для этого хороший подход?

Примечание. Мы также пытались иметь несколько файлов, но поскольку у нас так много миграций, это почти невозможно поддерживать каждый раз.

Ответ 1

Чтобы изменить миграцию SQL Generated by entity, вы можете создать новый SqlServerMigrationSqlGenerator

Мы сделали это, чтобы добавить инструкцию GO до и после истории миграции:

public  class MigrationScriptBuilder: SqlServerMigrationSqlGenerator
{
    protected override void Generate(System.Data.Entity.Migrations.Model.InsertHistoryOperation insertHistoryOperation)
    {
        Statement("GO");

        base.Generate(insertHistoryOperation);

        Statement("GO");

    }
}

затем добавьте конструктор Configuration (в папку Migrations проекта, где вы находитесь DbContext), чтобы он использовал этот новый генератор sql:

[...]
internal sealed class Configuration : DbMigrationsConfiguration<PMA.Dal.PmaContext>
{
    public Configuration()
    {
        SetSqlGenerator("System.Data.SqlClient", new MigrationScriptBuilder());
        AutomaticMigrationsEnabled = false;
    }
[...]

Итак, теперь, когда вы создаете script с помощью тега Script, вы можете видеть, что insert into [__MigrationHistory] окружен GO

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

Ответ 2

Если вы пытаетесь изменить свое представление с помощью Sql('Alter View dbo.Foos As etc'), вы можете избежать ошибки should be the first statement in a batch file без добавления операторов GO, поставив sql внутри команды EXEC:

Sql(EXEC('Alter View dbo.Foos As etc'))

Ответ 3

Выключите концепцию в глубину SqlServerMigrationSqlGenerator как необязательный аргумент для Statement(sql, batchTerminator). Вот что-то, основанное на идее Skyp. Он работает как в режиме script, так и без него. GO для разных операций, чем для Skyp, только потому, что наши потребности немного отличаются. Затем вам необходимо зарегистрировать этот класс в Configuration в соответствии с инструкциями Skyp.

    public class MigrationScriptBuilder : SqlServerMigrationSqlGenerator
    {
        private string Marker = Guid.NewGuid().ToString(); //To cheat on the check null or empty of the base generator

        protected override void Generate(AlterProcedureOperation alterProcedureOperation)
        {
            SqlGo();
            base.Generate(alterProcedureOperation);
            SqlGo();
        }
        protected override void Generate(CreateProcedureOperation createProcedureOperation)
        {
            SqlGo();
            base.Generate(createProcedureOperation);
            SqlGo();
        }
        protected override void Generate(SqlOperation sqlOperation)
        {
            SqlGo();
            base.Generate(sqlOperation);
        }

        private void SqlGo()
        {
            Statement(Marker, batchTerminator: "GO");
        }

        public override IEnumerable<MigrationStatement> Generate(IEnumerable<MigrationOperation> migrationOperations, string providerManifestToken)
        {
            var result = new List<MigrationStatement>();
            var statements = base.Generate(migrationOperations, providerManifestToken);

            bool pendingBatchTerminator = false;
            foreach (var item in statements)
            {
                if(item.Sql == Marker && item.BatchTerminator == "GO")
                {
                    pendingBatchTerminator = true;
                }
                else
                {
                    if(pendingBatchTerminator)
                    {
                        item.BatchTerminator = "GO";
                        pendingBatchTerminator = false;
                    }
                    result.Add(item);
                }
            }

            return result;
        }
    }

Ответ 4

Просто замените текущую инструкцию на .Replace( "GO", "");