EF5 Migrations - Дубликат/переопределенная ошибка переменной при снижении ограничений | Проблема с использованием команды SQL GO
История:
У нас есть проект, который использует ef-migrations содержащий несколько (прочитанных ~ 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 будет работать плавно!:)