12

Ich verwende das TFS-Versionsmanagement für die fortlaufende Integration und Bereitstellung.Erste Migration des EF-Codes für die Bereitstellung einer älteren Version

Ich verwende migrate.exe, um die Datenbankmigration während der Bereitstellung durchzuführen. Dies funktioniert sehr gut, wenn Sie von einer älteren Version auf eine neuere Version wechseln. Wenn Sie eine ältere Version der Anwendung bereitstellen möchten, wird es jedoch matschiger. Die Assembly, die Ihre Migrationen für einen Kontext enthält, muss wissen, wie Sie von Version 3 zu Version 2 wechseln können. Normalerweise verwenden Sie die Assemblys, die Sie als Quelle für Ihre Migrationen bereitstellen möchten, aber in diesem Fall Fall, Sie müssen die bereits bereitgestellten Assemblies verwenden, da sie die einzigen sind, die wissen, wie man von v3 auf v2 herunterkommt. (Version 2 hat keine Ahnung, dass v3 überhaupt existiert.)

Mein aktueller Plan ist es, die beiden Baugruppen während der Bereitstellung irgendwie zu vergleichen. Wenn die Montag im Installationsverzeichnis „neuer“ Migrationen als die im Deployment-Direktor enthält, würde ich zuerst die „neueste“ verfügbar Migration in der Versammlung im Bereitstellungsverzeichnis erhalten muß, und dann auszuführen:

migrate.exe AssemblyInInstallationDir /targetMigration NewestFromAssemblyInDeploymentDir 

wo, wie in einem „normalen“ Einsatz-Szenario, wo Sie auf eine neuere Version aktualisieren, können Sie einfach tun:

migrate.exe AssemblyInDeploymentDir 

Ist dies ein legit Ansatz? Ich habe noch zu prüfen, mit EF-Bibliotheken zu bewerten, welche Migrationen in jeder Baugruppe verfügbar sind. Es besteht auch die Herausforderung, dass jede dieser Baugruppen die "gleichen" nur unterschiedlichen Versionen sind. Ich muss sie wahrscheinlich in separate App-Domains laden und dann die Cross-App-Domain-Kommunikation nutzen, um die benötigten Informationen zu erhalten.

EDIT

habe ich ein Proof of Concept-App, die mir die verfügbaren Migrationen auf zwei verschiedene Versionen der gleichen Baugruppe auflisten können. Dies war für den gesamten Prozess von entscheidender Bedeutung, daher dachte ich, dass es sich lohnt, zu dokumentieren.

Die Anwendung verwendet Reflektion, um jede Assembly zu laden, und verwendet dann die DbMigrator-Klasse von System.Data.Entity.Migrations, um die Migrationsmetadaten aufzuzählen. Den Namen der Migrationen sind die Zeitstempelinformationen vorangestellt. Dadurch kann ich sie ordnen und sehen, welche Assembly den "neueren" Satz von Migrationen enthält.

static void Main(string[] args) 
{ 
    const string dllName = "Test.Data.dll"; 
    var assemblyCurrent = Assembly.LoadFile(Path.Combine(System.Environment.CurrentDirectory, string.Format("Current\\{0}", dllName))); 
    var assemblyTarget = Assembly.LoadFile(Path.Combine(System.Environment.CurrentDirectory, string.Format("Target\\{0}", dllName))); 

    Console.WriteLine("Curent Version: " + assemblyCurrent.FullName); 
    Console.WriteLine("Target Version: " + assemblyTarget.FullName); 

    const string contextName = "Test.Data.TestContext"; 
    const string migrationsNamespace = "Test.Data.Migrations"; 
    var currentContext = assemblyCurrent.CreateInstance(contextName); 
    var targetContext = assemblyTarget.CreateInstance(contextName); 

    var currentContextConfig = new DbMigrationsConfiguration 
    { 
     MigrationsAssembly = assemblyCurrent, 
     ContextType = currentContext.GetType(), 
     MigrationsNamespace = migrationsNamespace 
    }; 

    var targetContextConfig = new DbMigrationsConfiguration 
    { 
     MigrationsAssembly = assemblyTarget, 
     ContextType = targetContext.GetType(), 
     MigrationsNamespace = migrationsNamespace 
    }; 

    var migrator = new DbMigrator(currentContextConfig); 
    var localMigrations = migrator.GetLocalMigrations(); //all migrations 

    Console.WriteLine("Current Context Migrations:"); 
    foreach (var m in localMigrations) 
    { 
     Console.WriteLine("\t{0}", m); 
    } 

    migrator = new DbMigrator(targetContextConfig); 
    localMigrations = migrator.GetLocalMigrations(); //all migrations 

    Console.WriteLine("Target Context Migrations:"); 
    foreach (var m in localMigrations) 
    { 
     Console.WriteLine("\t{0}", m); 
    } 

    Console.ReadKey(); 
} 

}

Die Ausgabe der Anwendung wie folgt aussieht:

Curent Version: Test.Data, Version=1.3.0.0, Culture=neutral, PublicKeyToken=null 
Target Version: Test.Data, Version=1.2.0.0, Culture=neutral, PublicKeyToken=null 

Current Context Migrations: 
    201403171700348_InitalCreate 
    201403171701519_AddedAddresInfoToCustomer 
    201403171718277_RemovedStateEntity 
    201403171754275_MoveAddressInformationIntoContactInfo 
    201403181559219_NotSureWhatIChanged 
    201403181731525_AddedRowVersionToDomainObjectBase 
Target Context Migrations: 
    201403171700348_InitalCreate 
    201403171701519_AddedAddresInfoToCustomer 
    201403171718277_RemovedStateEntity 
+1

Ich weiß nicht, die Antwort, ob dies eine akzeptierte Möglichkeit, dies zu tun, aber ein dickes Lob für die Dokumentation und Ihren Ansatz zu teilen. –

Antwort

2

Wir haben dieses Problem gelöst und verwenden seit über einem Jahr unsere Werkzeuge, um vollständig kontinuierliche Datenbankbereitstellungen in die Produktion durchzuführen. Keine Menschen beteiligt. :)

Wir haben einige dieser Öffentlichkeit über GitHub gemacht: https://github.com/GalenHealthcare/Galen.Ef.Deployer

Sie können „Brechen“ Änderungen vornehmen, aber in der Regel vermeiden wir, dass auch - aber vor allem, weil unsere Anwendungen bleiben Live bei Upgrades. Wir behandeln die Datenebene als eine unabhängig einsetzbare Komponente - und als Ergebnis hat sie eine "Schnittstelle", die kompatibel bleiben muss. Wir verwenden häufig einen mehrstufigen Upgrade-Ansatz, bei dem wir eine zwischengeschaltete Version verwenden, die rückwärts/vorwärts kompatibel ist, unsere verschiedenen Anwendungsdienste aktualisiert und schließlich die Datenbankstufe aktualisiert, um die veraltete Kompatibilität zu entfernen.

Auch in diesem Szenario haben wir die Möglichkeit, automatisch von/ Version unseres Schemas und Daten zu gehen. Tatsächlich haben wir Komponententests hinzugefügt, die dies bei jedem einzelnen Build für jede einzelne Datenbankversion bestätigen. Es läuft im Grunde die Kette von Schema-Iterationen auf und ab und validiert, dass die Aufwärts- und Abwärtsmigrationen immer funktionieren und Datenkonsistenz und -kompatibilität aufrechterhalten. Sie können diese Tests im GitHub-Projekt sehen. Hier ein Beispiel:

https://github.com/GalenHealthcare/Galen.Ef.Deployer/blob/master/Galen.Ci.EntityFramework.Deployer/Galen.Ci.EntityFramework.Testing/MigrationTestRunner.cs

0

Die Art, wie ich dies in der Regel Ansatz ist es, (fast) nie Bruch mein Datenbank-Schema Änderungen vornehmen. Es ist im Grunde eine kontrollierte Form der technischen Schulden.

Angenommen, ich ersetze ColumnX durch ColumnY. Der typische Ansatz besteht darin, "alle Daten von ColumnX nach ColumnY zu kopieren und ColumnX aus dem Schema zu entfernen". Dadurch wird Ihre Fähigkeit zum Zurücksetzen auf die vorherige Version verloren, da ColumnX nicht mehr vorhanden ist.

Der Rollback-freundliche Weg, dies anzugehen, ist das Hinzufügen von ColumnY, das Kopieren der Daten und das Hinzufügen von Triggern, um beide Spalten synchron zu halten. Dies ist kein Dauerzustand!Eine User Story für "Remove ColumnX und zugehörige Trigger" wird sofort im Backlog für eine zukünftige Iteration ausgeführt, wenn wir sicher sind, dass wir nie wieder auf eine Version zurückrollen, die von ColumnX abhängt.

Rollback kann immer noch die Veröffentlichung der vorherigen Version des DACPAC mit dem Vorbehalt beinhalten, dass Sie sicherstellen müssen, dass Sie keine in der Datenbank vorhandenen Elemente löschen, die sich nicht im Schema befinden. Auf diese Weise können Sie, wenn Sie eine Reihe gespeicherter Prozeduren aktualisiert haben, die aus ColumnY abgerufen werden sollen, die alte Version veröffentlichen, die ColumnX abruft, und die alte Version weiß glücklicherweise nicht, dass das Schema geändert wurde.