18

So I have an application with a ton of migrations made by Entity framework. We want to get a script for all the migrations at once and using the -Script tag does work fine.

However...it does not add GO statements in the SQL giving us problems like Alter view should be the first statement in a batch file...

I have been searching around and manually adding Sql("GO"); help with this problem but only for the entire script. When I use the package console manager again it returns an exception.

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

Is there a way to add these GO tags only when using the -Script tag? If not, what is a good approach for this?

Note: we have also tried having multiple files but since we have so many migrations, this is near impossible to maintain every time.

Tikkes
  • 4,599
  • 4
  • 36
  • 62

5 Answers5

19

If you are trying to alter your view using Sql("Alter View dbo.Foos As etc"), then you can avoid the should be the first statement in a batch file error without adding GO statements by putting the sql inside an EXEC command:

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

Colin
  • 22,328
  • 17
  • 103
  • 197
  • This thing allows to fill tables and columns right after creation. Great! – blazkovicz Nov 27 '14 at 13:25
  • Where is the EXEC function defined please? I am unable to find it. – MathuSum Mut Mar 13 '19 at 18:03
  • 3
    @MathuSumMut It is standard Sql. I had omitted the quotation marks needed around the Sql string. I've edited the answer to correct the error – Colin Mar 14 '19 at 12:57
  • This should be the accepted answers. It is also stated here: https://github.com/dotnet/efcore/issues/12911#issuecomment-831447488 – Niyo Aug 04 '21 at 14:05
12

In order to change the SQL Generated by entity framework migrations you can create a new SqlServerMigrationSqlGenerator

We have done this to add a GO statement before and after the migration history:

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

        base.Generate(insertHistoryOperation);

        Statement("GO");

    }
}

then add in the Configuration constructor (in the Migrations folder of the project where you DbContext is) so that it uses this new sql generator:

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

So now when you generate a script using the -Script tag, you can see that the insert into [__MigrationHistory] is surrounded by GO

Alternatively in your implementation of SqlServerMigrationSqlGenerator you can override any part of the script generation, the InsertHistoryOperation was suitable for us.

Skyp
  • 1,185
  • 11
  • 20
  • 2
    Works perfectly when using the `-Script` tag. I have to comment out the `SetSqlGenerator(".....` line however when I dont use it because otherwise I got the exception again. This however saves me a ton of time because it is just commenting out a line in stead of adding `Sql("GO");` everywhere. Thanks! – Tikkes Jun 14 '13 at 11:52
  • I can't find InsertHistoryOperation. Which assembly is it in? – Chris Nevill Jul 19 '13 at 15:49
  • 4
    Ah I think it's renamed in EF 6 to HistoryOperation – Chris Nevill Jul 19 '13 at 15:53
10

Turn out the concept exist deep in the SqlServerMigrationSqlGenerator as an optional argument for Statement(sql, batchTerminator). Here is something based on Skyp idea. It works both in -script mode or not. The GOs are for different operations than for Skyp only because our needs are a little different. You then need to register this class in the Configuration as per Skyp instructions.

    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;
        }
    }
Yepeekai
  • 2,545
  • 29
  • 22
  • 1
    This worked for me! I was looking for a way that works both with and without -script argument. Thanks! – ravinsp Dec 21 '16 at 09:02
  • 1
    This worked for me when EF6 was inexplicably removing a go statement from the end of my stored procedure creation script being inserted via SqlResource. The magic of EF is not something I'm fond of. – aaaaaa Sep 24 '17 at 15:34
  • hm, interesting, this works perfectly when using ``update-database -script`` however in an Azure Deployment Group Task ``Generate migration SQL script`` instead of ``GO`` terminator ``EXECUTE('')``-statements appear in the generated code – DrCopyPaste Sep 12 '19 at 08:43
3

The easiest way is to add /**/ before the GO statement.

Ivan Stus
  • 31
  • 2
-3

Just replace the current statement with a .Replace("GO", "");

Wouter Van Ranst
  • 521
  • 4
  • 14