2012-06-07 4 views
6

Verwenden von Entity Framework 4.3.1 Code zuerst und Datenmigrationen.Wie kann ich SQL-Skripts überschreiben, die von MigratorScriptingDecorator generiert wurden

Ich habe ein Dienstprogramm geschrieben, um die Migrationsskripts für eine Zieldatenbank mithilfe von MigratorScriptingDecorator automatisch zu generieren.

Manchmal jedoch, wenn die Zieldatenbank von Grund auf neu generiert wird, ist das generierte Skript ungültig, da es eine Variable mit demselben Namen zweimal deklariert. Der Name der Variablen lautet @ var0.

Dies tritt auf, wenn mehrere Migrationen angewendet werden und wenn mindestens zwei dazu führen, dass eine Standardbedingung gelöscht wird.

Das Problem tritt sowohl bei der Erzeugung des Formularcode Skript, und bei der Verwendung des Paket-Manager-Konsole Befehl:

Update-Database -Script 

Hier sind die beanstandeten Schnipsel das generierte Skript Form:

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

und

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

Ich möchte in der Lage sein, den Punkt zu überschreiben, wo er ge Nimmt die SQL für jede Migration neu auf und fügt dann eine "GO" -Anweisung hinzu, so dass jede Migration in einem separaten Batch erfolgt, wodurch das Problem gelöst wird.

Jeder hat irgendwelche Ideen, wie man das macht, oder wenn ich den falschen Baum belle, dann könnten Sie vielleicht einen besseren Ansatz vorschlagen?

Antwort

4

Also mit umfangreicher Verwendung von ILSpy und einige Zeiger in the answer to this question fand ich einen Weg.

Details unten für diejenigen interessiert.

Problem

Die SqlServerMigrationSqlGenerator ist die Klasse in letzter Instanz verantwortlich für die SQL-Anweisungen erstellen, die gegen die Zieldatenbank oder Skript aus ausgeführt werden, wenn die -Script Schaltern im Paket-Manager-Konsole verwenden oder wenn die MigratorScriptingDecorator verwenden.

Workings

Untersuchung der Genearate Methode in der SqlServerMigrationSqlGenerator, die für eine DROP COLUMN verantwortlich ist, sieht es dies wie:

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); 
    } 
} 

Sie können sehen, es den Überblick über die Variablennamen hält verwendet, aber Dies scheint nur innerhalb eines Stapels zu verfolgen, dh eine einzige Migration. Wenn also ein Migratin mehr als ein DROP COLUM enthält, funktioniert das oben, aber wenn es zwei Migrationen gibt, die dazu führen, dass DROP COLUMN generiert wird, dann wird die _variableCounter Variable zurückgesetzt.

keine Probleme zu erwarten sind, wenn sie nicht ein Skript zu erzeugen, da jede Anweisung ausgeführt wird sofort mit der Datenbank (Ich habe die Verwendung von SQL Profiler).

Wenn Sie ein SQL-Skript generieren und wollen es laufen, wie sie ist, obwohl Sie ein Problem haben.

Lösung

habe ich ein neues BatchSqlServerMigrationSqlGenerator von SqlServerMigrationSqlGenerator vererben wie folgt (beachten Sie benötigen 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"); 
    } 
} 

nun die Migration zu zwingen, Ihre benutzerdefinierten Generator zu verwenden, haben Sie zwei Möglichkeiten:

  1. Wenn Sie möchten, dass es in t integriert wird er Package Manager-Konsole, fügen Sie die folgende Zeile in Configuration Klasse:

    SetSqlGenerator("System.Data.SqlClient", 
            new BatchSqlServerMigrationSqlGenerator()); 
    
  2. Wenn Sie das Skript von Code sind zu erzeugen (wie ich war), fügen Sie ein ähnliches Codezeile, wo Sie Ihre Konfiguration Montag haben in Code:

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

glauben Sie, eine ähnliche Problemumgehung für das sQL möglich sein könnte, für die normale direkte Tabelleneinsätze erzeugt? –

Verwandte Themen