2012-06-07 14 views
6

Korzystanie z Entity Framework 4.3.1 Najpierw kod, a migracja danych.W jaki sposób można przesłonić skrypty SQL wygenerowane przez MigratorScriptingDecorator

Napisałem narzędzie do automatycznego generowania skryptów migracji dla docelowej bazy danych za pomocą MigratorScriptingDecorator.

Jednak czasami podczas generowania docelowej bazy danych od początku wygenerowany skrypt jest nieprawidłowy, ponieważ dwukrotnie deklaruje zmienną o tej samej nazwie.

Zmienna nazwa to @ var0.

Wydaje się, że dzieje się tak, gdy zastosowano wiele migracji, a co najmniej dwa powodują usunięcie domyślnego ograniczenia.

Problem występuje zarówno podczas generowania skryptu kod formularz, a po użyciu komendy konsoli Menedżer pakiet:

Update-Database -Script 

Oto fragmenty obrażając tworzą wygenerowany skrypt:

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

i

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

Chciałbym móc zastąpić punkt, w którym nerates SQL dla każdej migracji, a następnie dodać instrukcję "GO", aby każda migracja była w osobnej partii, która rozwiązałaby problem.

Ktoś ma jakieś pomysły, jak to zrobić, lub jeśli szczerzę złe drzewo, może mógłbyś zaproponować lepsze podejście?

Odpowiedz

4

Tak więc przy szerokim użyciu ILSpy i niektórych wskaźników w the answer to this question znalazłem sposób.

Szczegóły poniżej dla zainteresowanych.

Problem

The SqlServerMigrationSqlGenerator jest klasa ostatecznie odpowiedzialny za tworzenie zapytań SQL, który zostanie wykonany na bazie docelowej lub skryptów, gdy za pomocą przełącznika w konsoli Menedżer pakietów -Script lub podczas korzystania z MigratorScriptingDecorator.

Wyrobiska

badając metody Genearate w SqlServerMigrationSqlGenerator, który jest odpowiedzialny za DROP COLUMN, wygląda to tak:

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

można zobaczyć go śledzi nazwami zmiennych używanych, ale to tylko wydaje się śledzić w partii, tj. Pojedynczej migracji. Jeśli więc migratin zawiera więcej niż jeden DROP COLUM powyższe działa poprawnie, ale jeśli są dwie migracje, które powodują wygenerowanie DROP COLUMN, zmienna _variableCounter jest resetowana.

Podczas generowania skryptu nie występują żadne problemy, ponieważ każda instrukcja jest wykonywana bezpośrednio w bazie danych (sprawdziłem przy użyciu programu SQL Profiler).

Jeśli wygenerujesz skrypt SQL i chcesz go uruchomić tak, jak to tylko masz problem.

Rozwiązanie

stworzyłem nowy BatchSqlServerMigrationSqlGenerator dziedziczenie z SqlServerMigrationSqlGenerator w następujący sposób (pamiętać trzeba 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"); 
    } 
} 

teraz zmusić migracje używać niestandardowych generator masz dwie opcje:

  1. Jeśli chcesz, aby był zintegrowany z t że konsola Menedżer pakietu, należy dodać poniższy wiersz do klasy Configuration:

    SetSqlGenerator("System.Data.SqlClient", 
            new BatchSqlServerMigrationSqlGenerator()); 
    
  2. Jeśli generowania skryptu z kodu (jak ja), dodać podobną linię kodu, gdzie masz swój zespół Konfiguracja w kodzie:

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

myślisz, że podobna Rozwiązaniem mogłoby być możliwe dla SQL generowane dla normalnych bezpośrednich wkładek stole? –

Powiązane problemy