2012-07-18 16 views
15

W ramach planowania migracji Entity Framework, aby debugować ruch danych, często używałam parametru -Script do generowania skryptu.Migracje Entity Framework: w tym instrukcja Go tylko w wyjściu języka skryptowego

Mogę wtedy wziąć ten skrypt do Query Analyzer i zawinąć go w transakcji, aby przetestować go ręcznie.

Natknąłem się na sytuację, w której potrzebowaliśmy instrukcji Przejdź, aby poprawnie wykonać skrypt. Następujący kod został dodany do migracji w celu wyprowadzenia Go w odpowiednim miejscu.

Sql("GO"); 

Dodaje instrukcję GO w odpowiedniej pozycji, gdy używany jest -Script. Ale gdy -Script nie jest używany. Wyjątek ...

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

Czy istnieje bezpieczny sposób dodania polecenia Go do skryptu?

+0

Dlaczego potrzebujesz GO? Nie jest to instrukcja SQL - jest to polecenie dla narzędzi SQL. –

+0

@LadislavMrnka - Zdefiniowałem doskonale prawdopodobny przypadek użycia (i związane z nim problemy) w tym pytaniu: http://stackoverflow.com/q/13589986/476786 – bPratik

Odpowiedz

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

Nie jestem już w stanie tego przetestować, ale wygląda mi to na niesamowite rozwiązanie. Akceptuję odpowiedź. –

+0

@BenTaylor czy mogę zasugerować użycie klasy bazowej? public override IEnumerable Generowanie (IEnumerable migrationOperations, string providerManifestToken) { foreach (var migrationStatement w base.Generate (migrationOperations, providerManifestToken)) { migrationStatement.BatchTerminator = "GO"; powrót zwrotu migracjiStatement; } } – regisbsb

+0

Przepraszam za sprawę. Nie działa z procedurami przechowywanymi. – regisbsb

13

Trafiłem ostatnio dokładnie w tę samą sytuację. Moje migracje z użyciem kodu EF często wprowadzają nową tabelę lub kolumnę, a następnie przenoszę dane przy użyciu Sql (...) do tych migracji, które czasem chcą odwoływać się do nowej tabeli/kolumny. Jak zauważyłeś, podczas uruchamiania jako migracja kodu EF, każda instrukcja wydaje się być wydana jako dyskretna partia do DB, a zatem nie ma żadnych problemów. Jednak aby spełnić ograniczenia wdrażania produkcji, zamieniamy zestaw migracji kodu ze sprintu w pojedynczy skrypt (za pomocą -Script), aby przedstawić pojedynczą zagregowaną migrację skryptu SQL dla zespołu wdrożeniowego. Ten plik skryptowy czasami kończy się niepowodzeniem, jak zauważyłeś, ponieważ próbował przetworzyć pojedynczą partię T SQL z pojedynczej migracji kodu, gdzie późniejsze instrukcje próbują odnosić się do struktury, która została zdefiniowana tylko wcześniej w partii.

ja nie szczególnie jak jedno z dwóch podejść wziąłem teraz, aby złagodzić ten, ale oto one:

się. Jeśli zdarzy mi się, że o tym myślę, podzielę migrację kodu na dwie migracje, aby po skrypcie były w dwóch (lub więcej) oddzielnych partiach. Nie podoba mi się to, ponieważ nie ma sprzężenia zwrotnego podczas rozwoju Code Migration, że jest to konieczne, a zatem wydaje się być podatne na błędy.

b. Kiedy generuję skrypty agregujące, uruchamiam je na bazie DB w celu ich udowodnienia, a następnie ręcznie wstrzykiwuję instrukcje "GO" tam, gdzie jest to konieczne w tym skrypcie. Jest to irytujący proces, który trzeba cofnąć i wykonać, i daje wynik w języku -Script, który nie jest 100% odzwierciedleniem migracji kodu.

Nie spędziłem zbyt wiele czasu na wkopywanie się w kod źródłowy Migracji kodu EF, aby sprawdzić, czy rozumiem, dlaczego interpretuje "GO" jako przechowywany proc i czy w kodzie źródłowym jest coś, co wskazywałoby w celu zapewnienia dyrektywy, która pozwoliłaby na uniknięcie tego.

+0

Cieszę się, że przynajmniej jedna inna osoba jest mając ten problem. W końcu skończyło mi się wygodniej, gdy EF uruchomił migracje i to jest moje rozwiązanie tego problemu. –

+0

+1 za sugestię. Czułem się brudny edytując skrypt (wymagany do wdrożenia na Prod), więc wróciłem i mogłem podzielić moje zmiany na kilka migracji. Dzięki! – jkoreska

+0

Oboje jesteśmy pocieszeni i rozczarowani tą odpowiedzią. Jestem pocieszony wiedząc, że to nie tylko ja, i jestem rozczarowany, że problem, który widzę, jest prawdziwy. – bwerks

2

Skończyłem z wykorzystaniem dwóch różnych klas Configuration po uruchomieniu migracji z parametrem -Script i bez niego. W jednej z moich klas zawijam jego MigrationSqlGenerator w niestandardowej implementacji, która dodaje instrukcje GO.

+2

Czy mieliście już jakiś przykład? –

0

Użyłem:

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 
} 

Więc kiedy to debug to nie psuje. I skryptuję z trybu wydania.

0

To działa dla mnie:

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

Które (jak widać w innych odpowiedzi) można stosować (migracja proces przepływu) z tego rodzaju metody w konfiguracji DbContext:

public Configuration() 
    { 
     SetSqlGenerator("System.Data.SqlClient", new MigrationScriptBuilder()); 
    } 
Powiązane problemy