2012-06-07 14 views
6

Utilizzo del codice Entity Framework 4.3.1 e Migrazioni dei dati.Come eseguire l'override degli script SQL generati da MigratorScriptingDecorator

Ho scritto un'utilità per generare automaticamente gli script di Migrazione per un database di destinazione, utilizzando il MigratorScriptingDecorator.

Tuttavia, a volte quando si ricrea il database di destinazione da zero, lo script generato non è valido, in quanto dichiara una variabile con lo stesso nome due volte.

Il nome della variabile è @ var0.

Questo sembra accadere quando sono applicate più migrazioni e quando almeno due risultano in un vincolo predefinito che viene eliminato.

Il problema si verifica sia durante la generazione del codice del modulo di script, e quando si utilizza il comando console Package Manager:

Update-Database -Script 

Qui ci sono i frammenti di offendere formare lo script generato:

DECLARE @var0 nvarchar(128) 
SELECT @var0 = name 
FROM sys.default_constraints 
WHERE parent_object_id = object_id(N'SomeTableName') 

e

DECLARE @var0 nvarchar(128) 
SELECT @var0 = name 
FROM sys.default_constraints 
WHERE parent_object_id = object_id(N'SomeOtherTableName') 

Mi piacerebbe essere in grado di scavalcare il punto in cui si ge annulla l'SQL per ogni migrazione, quindi aggiunge un'istruzione "GO" in modo che ogni migrazione sia in un batch separato, il che risolverebbe il problema.

Qualcuno ha qualche idea su come fare questo, o se sto abbaiando sull'albero sbagliato allora forse potresti suggerire un approccio migliore?

risposta

4

Quindi con ampio uso di ILSpy e alcuni puntatori in the answer to this question ho trovato un modo.

Dettagli sotto per quelli interessati.

Problema

Il SqlServerMigrationSqlGenerator è la classe in ultima analisi, responsabile della creazione delle istruzioni SQL che vengono eseguite sul database di destinazione o tramite script quando si utilizza l'interruttore -Script nella console Package Manager o quando si utilizza il MigratorScriptingDecorator.

Lavorazioni

Esaminare il metodo Genearate nel SqlServerMigrationSqlGenerator che è responsabile di un DROP COLUMN, sembra che questo:

protected virtual void Generate(DropColumnOperation dropColumnOperation) 
{ 
    RuntimeFailureMethods 
     .Requires(dropColumnOperation != null, null, "dropColumnOperation != null"); 
    using (IndentedTextWriter indentedTextWriter = 
     SqlServerMigrationSqlGenerator.Writer()) 
    { 
     string value = "@var" + this._variableCounter++; 
     indentedTextWriter.Write("DECLARE "); 
     indentedTextWriter.Write(value); 
     indentedTextWriter.WriteLine(" nvarchar(128)"); 
     indentedTextWriter.Write("SELECT "); 
     indentedTextWriter.Write(value); 
     indentedTextWriter.WriteLine(" = name"); 
     indentedTextWriter.WriteLine("FROM sys.default_constraints"); 
     indentedTextWriter.Write("WHERE parent_object_id = object_id(N'"); 
     indentedTextWriter.Write(dropColumnOperation.Table); 
     indentedTextWriter.WriteLine("')"); 
     indentedTextWriter.Write("AND col_name(parent_object_id, 
                 parent_column_id) = '"); 
     indentedTextWriter.Write(dropColumnOperation.Name); 
     indentedTextWriter.WriteLine("';"); 
     indentedTextWriter.Write("IF "); 
     indentedTextWriter.Write(value); 
     indentedTextWriter.WriteLine(" IS NOT NULL"); 
     indentedTextWriter.Indent++; 
     indentedTextWriter.Write("EXECUTE('ALTER TABLE "); 
     indentedTextWriter.Write(this.Name(dropColumnOperation.Table)); 
     indentedTextWriter.Write(" DROP CONSTRAINT ' + "); 
     indentedTextWriter.Write(value); 
     indentedTextWriter.WriteLine(")"); 
     indentedTextWriter.Indent--; 
     indentedTextWriter.Write("ALTER TABLE "); 
     indentedTextWriter.Write(this.Name(dropColumnOperation.Table)); 
     indentedTextWriter.Write(" DROP COLUMN "); 
     indentedTextWriter.Write(this.Quote(dropColumnOperation.Name)); 
     this.Statement(indentedTextWriter); 
    } 
} 

lo si può vedere registra i variabili nomi utilizzati, ma questo sembra solo per tenere traccia all'interno di un batch, vale a dire una singola migrazione. Pertanto, se una migrazione contiene più di uno DROP COLUM, quanto sopra funziona correttamente, ma se sono presenti due migrazioni che determinano la generazione di un , la variabile _variableCounter viene reimpostata.

Nessun problema si verifica quando non si genera uno script, poiché ogni istruzione viene eseguita immediatamente rispetto al database (ho controllato utilizzando SQL Profiler).

Se si genera uno script SQL e si desidera eseguirlo così com'è, si è verificato un problema.

Soluzione

Ho creato un nuovo BatchSqlServerMigrationSqlGenerator che eredita da SqlServerMigrationSqlGenerator come segue (nota è necessario using System.Data.Entity.Migrations.Sql;):

public class BatchSqlServerMigrationSqlGenerator : SqlServerMigrationSqlGenerator 
{ 
    protected override void Generate 
     (System.Data.Entity.Migrations.Model.DropColumnOperation dropColumnOperation) 
    { 
     base.Generate(dropColumnOperation); 

     Statement("GO"); 
    } 
} 

Ora per forzare le migrazioni di utilizzare il generatore personalizzato avete due opzioni:

  1. Se si desidera che sia integrato in t egli console Package Manager, aggiungere la riga seguente alla classe Configuration:

    SetSqlGenerator("System.Data.SqlClient", 
            new BatchSqlServerMigrationSqlGenerator()); 
    
  2. Se si sta generando lo script dal codice (come ero), aggiungere una linea simile di codice per cui avete il vostro assemblaggio di configurazione in codice:

    migrationsConfiguration.SetSqlGenerator(DataProviderInvariantName, 
            new BatchSqlServerMigrationSqlGenerator()); 
    
+0

pensi che una soluzione simile potrebbe essere possibile per l'SQL generato per le normali inserti tavolo diretti? –

Problemi correlati