2012-07-18 6 views
15

Come parte della pianificazione di una migrazione Entity Framework, per eseguire il debug dello spostamento dei dati, utilizzerei spesso il parametro -Script per generare lo script.Entity Framework Migrations: includendo istruzione Go solo in -Script output

Potrei quindi prendere questo script in Query Analyzer e metterlo in una transazione per testarlo manualmente.

Mi sono imbattuto in una situazione in cui avevamo bisogno di un'istruzione Go per eseguire correttamente lo script. Il codice seguente è stato aggiunto alla migrazione per generare un Go nella posizione corretta.

Sql("GO"); 

Questo aggiunge un'istruzione GO nella posizione corretta quando si utilizza -Script. Ma quando -Script non è usato. Ottengo l'eccezione ...

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

Esiste un modo sicuro per aggiungere un comando Vai allo script?

+0

Perché hai bisogno di GO? Non è un'istruzione SQL, è un comando per gli strumenti SQL. –

+0

@LadislavMrnka - Ho definito un caso d'uso perfettamente plausibile (ed è associato a problemi) in questa domanda: http://stackoverflow.com/q/13589986/476786 – bPratik

risposta

7
internal sealed class Configuration : DbMigrationsConfiguration<Context> 
{ 
    public Configuration() 
    { 
     AutomaticMigrationsEnabled = false; 
     const string providerInvariantName = "System.Data.SqlClient"; 
     SetSqlGenerator(providerInvariantName, new BatchingMigrationSqlGenerator(GetSqlGenerator(providerInvariantName))); 
    } 

    protected override void Seed(Context context) 
    { 
    } 

} 

internal class BatchingMigrationSqlGenerator : MigrationSqlGenerator 
{ 
    private readonly MigrationSqlGenerator migrationSqlGenerator; 

    public BatchingMigrationSqlGenerator(MigrationSqlGenerator migrationSqlGenerator) 
    { 
     this.migrationSqlGenerator = migrationSqlGenerator; 
    } 

    public override IEnumerable<MigrationStatement> Generate(IEnumerable<MigrationOperation> migrationOperations, string providerManifestToken) 
    { 
     var migrationStatements = migrationSqlGenerator.Generate(migrationOperations, providerManifestToken).ToArray(); 
     foreach (var migrationStatement in migrationStatements) 
     { 
      migrationStatement.BatchTerminator = "GO"; 
     } 
     return migrationStatements; 
    } 
} 
+0

Non sono più in grado di testarlo, ma mi sembra una soluzione fantastica. Sto accettando la risposta. –

+0

@BenTaylor posso suggerire l'uso della classe base? public override IEnumerable Genera (IEnumerable migrationOperations, stringa providerManifestToken) { foreach (var migrationStatement a base.Generate (migrationOperations, providerManifestToken)) { migrationStatement.BatchTerminator = "GO"; yield return migrationStatement; } } – regisbsb

+0

Mi dispiace per l'upvot. Non funziona con stored procedure – regisbsb

13

Ho colpito la stessa identica situazione di recente. Le migrazioni del mio codice EF spesso introducono una nuova tabella o colonna, e poi metto anche le migrazioni dei dati usando Sql (...) nelle migrazioni che a volte vogliono fare riferimento alla nuova tabella/colonna. Come hai sottolineato, quando viene eseguito come una migrazione del codice EF, ogni istruzione sembra essere emessa come batch discreto per il DB e quindi non ci sono problemi. Tuttavia, per soddisfare i vincoli di implementazione della produzione, trasformiamo un set di migrazioni di codice da uno sprint in un singolo script (utilizzando -Script) per presentare una migrazione di script SQL aggregata singola per il team di distribuzione. Questo file di script a volte non riesce, come hai sottolineato, a causa del tentativo di elaborare un singolo batch T SQL da una migrazione di un singolo codice in cui le istruzioni successive tentano di fare riferimento a una struttura definita solo in precedenza nel batch.

non mi piace particolarmente uno dei due approcci che ho preso per ora per attenuare questo, ma qui sono:

a. Se mi capita di pensarci al momento, ho diviso la migrazione del codice in due migrazioni, in modo che quando sono script, siano in due (o più) lotti separati. Non mi piace perché non ci sono feedback durante lo sviluppo della migrazione del codice che è necessario, e quindi sembra incline all'errore.

b. Quando genero gli script aggregati, li eseguo su un DB scratch per provarli, e finisco per iniettare manualmente le istruzioni "GO" laddove necessario in quello script. Questo è un processo fastidioso da dover tornare indietro e fare, e genera risultati in -Script che non riflettono al 100% le migrazioni del codice.

Non ho speso molto tempo a scavare nel codice sorgente delle migrazioni di codice EF ancora per vedere se riesco a capire perché interpreta "GO" come un processo memorizzato, e se c'è qualcosa nel codice sorgente che potrebbe puntare un modo per fornire una direttiva che eviterebbe questo.

+0

Sono contento di sapere che almeno un'altra persona è avendo questo problema. Alla fine ho finito per sentirmi più a mio agio quando EF gestiva le migrazioni e questa è la mia soluzione al problema. –

+0

+1 per il suggerimento a. È stato spiacevole modificare lo script (richiesto per la distribuzione in Prod), quindi sono tornato indietro e sono riuscito a suddividere le mie modifiche su diverse migrazioni. Grazie! – jkoreska

+0

Sono entrambi confortato e deluso da questa risposta. Sono confortato nel sapere che non sono solo io, e sono deluso dal fatto che il problema che sto vedendo sia reale. – bwerks

2

Ho finito per utilizzare due diverse classi Configuration quando eseguo le migrazioni con e senza il parametro -Script. In una delle mie classi Configuration impacco il suo MigrationSqlGenerator in un'implementazione personalizzata, che aggiunge le istruzioni GO.

+2

Avete perphaps avuto un esempio di questo? –

0

ho usato:

public class MigrationScriptBuilder : SqlServerMigrationSqlGenerator 
{ 
#if !DEBUG 
    protected override void Generate(System.Data.Entity.Migrations.Model.SqlOperation sqlOperation) 
    { 
     Statement("GO"); 

     base.Generate(sqlOperation); 

     Statement("GO"); 
    } 
#endif 
} 

Così, quando si tratta di eseguire il debug che non va in crash. E io script dalla modalità di rilascio.

0

Questo è il lavoro per me:

public class MigrationScriptBuilder : SqlServerMigrationSqlGenerator 
{ 
    public override IEnumerable<MigrationStatement> Generate(IEnumerable<MigrationOperation> migrationOperations, string providerManifestToken) 
    { 
     var statements = base.Generate(migrationOperations, providerManifestToken); 

     statements = statements.SelectMany(s => new[] { 
      s, 
      new MigrationStatement 
      { 
       Sql = "GO" 
      } 
     }).ToList(); 

     return statements; 
    } 
} 

che (come si vede in altre risposte) possono essere utilizzati (flusso di processo di migrazione), con questo tipo di metodo nella configurazione DbContext:

public Configuration() 
    { 
     SetSqlGenerator("System.Data.SqlClient", new MigrationScriptBuilder()); 
    } 
Problemi correlati