We have a project that uses ef-migrations containing multiple (read ~60) migrations created over a long period of development. Naturally, some of these migrations also involve:
All is unicorns and rainbows when we run
Update-Database
because each migration runs as a separate batch. But when creating SQL Scripts
for these migrations using
Update-Database -Script
we encountered a few issues as described below:
When dropping multiple constraints across multiple migration files, the script generated by EF tends to re-declare variables that it uses for dropping. This is because it ensures uniqueness of variable names within the same migration file, but on change of file, it resets the counter, thus overlapping the names.
SQL enforces that CREATE TRIGGER
is always the first statement in a batch. When the script is generated, EF is oblivious to the contents of Sql("CREATE TRIGGER ... ");
and thus doesn't treat it any specially. Thus the statement may appear right in the middle of a script file, and error out.
A common/common-sense solution to the two problems is to insert Begin/End the sql batch at the right places. Manually doing this would make me a very rich man, so that is not an efficient solution.
Instead, we used the technique provided by @DavidSette. Creating a new BatchSqlServerMigrationSqlGenerator
inheriting from SqlServerMigrationSqlGenerator
which effectively overrides dropColumnOperation
and sqlOperation
and then forcing a GO
statement around the sensitive ones as such:
protected override void Generate (System.Data.Entity.Migrations.Model.DropColumnOperation dropColumnOperation)
{
base.Generate(dropColumnOperation);
Statement("GO");
}
This solution breaks running Update-Database
without the -Script
flag with the following error:
System.Data.SqlClient.SqlException (0x80131904): Could not find stored procedure 'GO'.
Which was added by our custom generator. Now I'm not certain why, but there should be a pretty good reason EF doesn't recognise GO
!
More Info:
Full error:
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'.
So basically fixing the scripts breaks an essential command. Please help me decide which is the lesser of the two evils!
I put a Sql("--<GO>"); at the end of each migration. This runs OK as an applied migration and when I script the SQL I just do a find and replace on "--<GO>" to "GO". A bit manual but works for me. You could put Sql("--<GO>"); around your create trigger statements.
As per msdn
"GO is not a Transact-SQL statement; it is a command recognized by the sqlcmd and osql utilities and SQL Server Management Studio Code editor."
Since you are not using any of the above tools but SqlCommand class to execute your Sql statement Sql Server (and not EF - see the stack trace where the exception originated) is choking on it
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With