EF5 Migrations - Дубликат/переопределенная ошибка переменной при снижении ограничений | Проблема с использованием команды SQL GO

История:

У нас есть проект, который использует содержащий несколько (прочитанных ~ 60) миграций, созданных в течение длительного периода разработки. Естественно, некоторые из этих миграций также включают:

  • сбрасывающие ограничения 1, 2
  • создание триггеров

Все единороги и радуги, когда мы запускаем

Update-Database

потому что каждая миграция выполняется как отдельная партия. Но при создании SQL Scripts для этих миграций с использованием

Update-Database -Script

мы столкнулись с несколькими проблемами, описанными ниже:

Проблема 1:

При отбрасывании нескольких ограничений для нескольких файлов миграции script, сгенерированный EF, имеет тенденцию повторно объявлять переменные, которые он использует для удаления. Это связано с тем, что он обеспечивает уникальность имен переменных в одном файле миграции, но при изменении файла он сбрасывает счетчик, тем самым перекрывая имена.

Проблема 2:

SQL устанавливает, что CREATE TRIGGER всегда является первым оператором в пакете. Когда генерируется script, EF не обращает внимания на содержимое Sql("CREATE TRIGGER ... "); и поэтому не относится к нему специально. Таким образом, оператор может отображаться прямо в середине файла script и ошибки.

Решение: (или так мы думали!)

Общим/здравым смыслом решения двух проблем является вставка Begin/End пакет sql в нужном месте. Вручную это сделало бы меня очень богатым человеком, так что это не эффективное решение.

Вместо этого мы использовали технику предоставленную @DavidSette. Создание нового BatchSqlServerMigrationSqlGenerator, наследующего от SqlServerMigrationSqlGenerator, который эффективно переопределяет dropColumnOperation и sqlOperation, а затем заставляет оператор GO вокруг чувствительных как таковых:

protected override void Generate (System.Data.Entity.Migrations.Model.DropColumnOperation dropColumnOperation)
{
    base.Generate(dropColumnOperation);
    Statement("GO");
}

Boo Boo:

Это решение прерывает запуск Update-Database без флага -Script со следующей ошибкой:

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

Что было добавлено нашим настраиваемым генератором. Теперь я не уверен, почему, но должна быть хорошая причина, по которой EF не распознает GO!

Дополнительная информация:

Полная ошибка:

Applying code-based migration: 201205181406363_AddTriggerForOverlap.
GO
System.Data.SqlClient.SqlException (0x80131904): Could not find stored procedure 'GO'.
    at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
    at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
    at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)
    at System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady)
    at System.Data.SqlClient.SqlCommand.RunExecuteNonQueryTds(String methodName, Boolean async, Int32 timeout)
    at System.Data.SqlClient.SqlCommand.InternalExecuteNonQuery(TaskCompletionSource`1 completion, String methodName, Boolean sendToPipe, Int32 timeout, Boolean asyncWrite)
    at System.Data.SqlClient.SqlCommand.ExecuteNonQuery()
    at System.Data.Entity.Migrations.DbMigrator.ExecuteSql(DbTransaction transaction, MigrationStatement migrationStatement)
    at System.Data.Entity.Migrations.Infrastructure.MigratorLoggingDecorator.ExecuteSql(DbTransaction transaction, MigrationStatement migrationStatement)
    at System.Data.Entity.Migrations.DbMigrator.ExecuteStatements(IEnumerable`1 migrationStatements)
    at System.Data.Entity.Migrations.Infrastructure.MigratorBase.ExecuteStatements(IEnumerable`1 migrationStatements)
    at System.Data.Entity.Migrations.DbMigrator.ExecuteOperations(String migrationId, XDocument targetModel, IEnumerable`1 operations, Boolean downgrading, Boolean auto)
    at System.Data.Entity.Migrations.DbMigrator.ApplyMigration(DbMigration migration, DbMigration lastMigration)
    at System.Data.Entity.Migrations.Infrastructure.MigratorLoggingDecorator.ApplyMigration(DbMigration migration, DbMigration lastMigration)
    at System.Data.Entity.Migrations.DbMigrator.Upgrade(IEnumerable`1 pendingMigrations, String targetMigrationId, String lastMigrationId)
    at System.Data.Entity.Migrations.Infrastructure.MigratorLoggingDecorator.Upgrade(IEnumerable`1 pendingMigrations, String targetMigrationId, String lastMigrationId)
    at System.Data.Entity.Migrations.DbMigrator.Update(String targetMigration)
    at System.Data.Entity.Migrations.Infrastructure.MigratorBase.Update(String targetMigration)
    at System.Data.Entity.Migrations.Design.ToolingFacade.UpdateRunner.RunCore()
    at System.Data.Entity.Migrations.Design.ToolingFacade.BaseRunner.Run()
ClientConnectionId:ac53af4b-1f9b-4849-a0da-9eb33b836caf
Could not find stored procedure 'GO'.

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

Ответы

Ответ 1

Я положил Sql ( "- <GO> " ); в конце каждой миграции. Это работает нормально как прикладная миграция, и когда я script SQL, я просто нахожу и заменяю на "- <GO> " идти". Немного ручной, но работает для меня. Вы можете поместить Sql ( "- <GO> " ); вокруг ваших созданных операторов триггеров.

Ответ 2

По msdn

"GO не является оператором Transact-SQL, это команда, признанная утилитами sqlcmd и osql и редактором кода SQL Server Management Studio."

Поскольку вы не используете какой-либо из вышеперечисленных инструментов, но класс SqlCommand для выполнения вашего Sql-оператора Sql Server (а не EF - см. трассировку стека, где возникло исключение), задыхается от него

Ответ 3

После того, как я испытал проблема из первых рук.

Мы решили создать меньшие миграции. Если изменение было достаточно большим, чтобы требовать команду GO, разработчик пытался слишком сильно изменить процесс миграции. К сожалению, единственный способ контролировать, что происходит в миграции, - это прокомментировать изменения.

Я также задумался, почему я так сильно хотел script. Это не похоже на то, что я не доверял EF, чтобы правильно выполнить миграцию (при первом тестировании). В идеале я бы никогда и (к счастью) еще не имел причины его модифицировать. Я только посмотрел на него несколько раз, когда начал использовать EF Code First.

Мне не нравится этот ответ, но я не думаю, что он служит цели, кроме отладки миграции для Entity Framework Code First.

Ответ 4

Просто удалите все повторения этой строки:

DECLARE @var0 nvarchar(128)

Достаточно только первое объявление. Ваш script будет работать плавно!:)