Hallo! Heute habe ich eine neue Version von ThinkingHome.Migrator veröffentlicht - ein Tool für die versionierte Migration eines Datenbankschemas auf die .NET Core-Plattform.

In NuGet veröffentlichte Pakete, ausführliche Dokumentation geschrieben. Sie können den neuen Migrator bereits verwenden, und ich werde Ihnen sagen, wie er angezeigt wurde, warum er die Versionsnummer 3.0.0 hat (obwohl dies die erste Version ist) und warum er benötigt wird, wenn EF Migrations und FluentMigrator vorhanden sind .
Wie alles begann
Vor 9 Jahren, im Jahr 2009, arbeitete ich als ASP.NET-Entwickler. Als wir unser Projekt veröffentlichten, blieb eine spezielle Person lange wach und führte gleichzeitig die Dateien auf dem Server aus und führte SQL-Skripte mit seinen Händen aus, um die Datenbanken im Produkt zu aktualisieren. Wir haben nach einem Tool gesucht, das dies automatisch erledigt, und das Migrator.NET- Projekt gefunden.
Der Migrator schlug für diese Zeit eine neue Idee vor - Datenbankänderungen in Form von Migrationen festzulegen. Jede Migration enthält einen kleinen Teil der Änderungen und eine Versionsnummer, in die die Datenbank nach Abschluss verschoben wird. Der Migrator selbst verfolgte die Versionen und führte die erforderlichen Migrationen in der erforderlichen Reihenfolge durch. Besonders cool war die Tatsache, dass der Migrator für jede Migration umgekehrte Änderungen vornehmen durfte. Zu Beginn des Migrators war es möglich, eine niedrigere Version als die aktuelle festzulegen, und die Datenbank wurde automatisch auf diese Version zurückgesetzt, wobei die erforderlichen Migrationen in umgekehrter Reihenfolge durchgeführt wurden.
[Migration(1)] public class AddAddressTable : Migration { override public void Up() { Database.AddTable("Address", new Column("id", DbType.Int32, ColumnProperty.PrimaryKey), new Column("street", DbType.String, 50), new Column("city", DbType.String, 50) ); } override public void Down() { Database.RemoveTable("Address"); } }
Es gab viele Fehler in diesem Migrator. Er wusste nicht, wie er mit anderen Datenbankschemata als dem Standardschema arbeiten sollte. In einigen Fällen wurden falsche SQL-Abfragen generiert. Wenn Sie eine nicht vorhandene Versionsnummer angeben, fällt dies in eine Endlosschleife. Infolgedessen haben meine Kollegen und ich das Projekt gegabelt und dort Fehler behoben.
GitHub.com mit seinen Gabeln und Pull-Anfragen war damals nicht da (der Migrantencode war auf code.google.com ). Daher haben wir uns insbesondere nicht darum gekümmert, dass unsere Änderungen wieder in das ursprüngliche Projekt übernommen werden - wir haben nur unsere Kopie gesägt und selbst verwendet. Im Laufe der Zeit haben wir den größten Teil des Projekts umgeschrieben, und ich wurde dessen Hauptbetreuer. Dann habe ich den Code unseres Migrators auf Google Code gepostet und einen Artikel über Habr geschrieben . Es gab also ECM7.Migrator.
Während der Arbeit am Migrator haben wir ihn fast vollständig umgeschrieben. Gleichzeitig haben sie die API ein wenig vereinfacht und alles mit automatischen Tests abgedeckt. Ich persönlich habe es wirklich genossen, das zu nutzen, was passiert ist. Im Gegensatz zum ursprünglichen Migrator gab es ein Gefühl der Zuverlässigkeit und es gab kein Gefühl, dass seltsame Magie geschah.
Wie sich herausstellte, mochte nicht nur unser Migrator. Soweit ich weiß, wurde es in ziemlich großen Unternehmen eingesetzt. Ich kenne ABBYY, BARS Group und konzert.ru. Wenn Sie die Suchabfrage "ecm7 migrator" eingeben, sehen Sie in den Ergebnisartikeln darüber, Erwähnungen im Lebenslauf, Beschreibungen der Verwendung in der studentischen Arbeit. Manchmal erhielt ich Briefe von Fremden mit Fragen oder Dankesworten.
Nach 2012 hat sich das Projekt fast nicht entwickelt. Die aktuellen Funktionen deckten alle Aufgaben ab, die ich hatte, und ich sah keine Notwendigkeit, etwas zu erledigen.
ThinkingHome.Migrator
Letztes Jahr habe ich angefangen, an einem Projekt für .NET Core zu arbeiten. Dort musste es möglich sein, Plugins zu verbinden, und Plugins sollten in der Lage sein, die erforderliche Datenbankstruktur für sich selbst zu erstellen, um ihre Daten dort zu speichern. Dies ist eine solche Aufgabe, für die ein Migrator gut geeignet ist.
Ef Migrationen
Ich habe den Entity Framework Core verwendet, um mit der Datenbank zu arbeiten. Als erstes habe ich EF Migrations ausprobiert. Leider musste ich fast sofort die Idee aufgeben, sie zu benutzen.
Entity Framework-Migrationen ziehen eine Reihe von Abhängigkeiten in das Projekt und bewirken beim Start eine besondere Magie. Ein Schritt nach links / ein Schritt nach rechts - Sie stoßen auf Einschränkungen. Beispielsweise müssen Migrationen des Entity Framework aus irgendeinem Grund notwendigerweise in derselben Assembly wie DbContext
. Dies bedeutet, dass Migrationen nicht in Plugins gespeichert werden können.
Fluentmigrator
Als klar wurde, dass EF Migrations nicht passte, suchte ich in Google nach einer Lösung und fand mehrere Open-Source-Migratoren. Der am weitesten fortgeschrittene von ihnen, gemessen an der Anzahl der Downloads auf NuGet und der Stars auf GitHub, erwies sich als FluentMigrator . FM ist sehr gut! Er weiß viel und hat eine sehr praktische API. Zuerst entschied ich, dass dies das ist, was ich brauche, aber später wurden mehrere Probleme entdeckt.
Das Hauptproblem besteht darin, dass FluentMigrator nicht weiß, wie mehrere Versionssequenzen in derselben Datenbank berücksichtigt werden sollen. Wie ich oben geschrieben habe, musste ich einen Migrator in einer modularen Anwendung verwenden. Es ist notwendig, dass die Module (Plugins) unabhängig voneinander installiert und aktualisiert werden können. FluentMigrator verfügt über eine durchgängige Versionsnummerierung. Aus diesem Grund ist es unmöglich, die Migration eines Plugins aus der Datenbank auszuführen / zurückzusetzen, ohne die Datenbankstruktur der verbleibenden Plugins zu beeinflussen.
Ich habe versucht, das gewünschte Verhalten mithilfe von Tags zu organisieren, aber dies ist auch nicht ganz das, was Sie brauchen. FluentMigrator speichert keine Tag-Informationen für abgeschlossene Migrationen. Darüber hinaus sind Tags an Migrationen gebunden, nicht an Assemblys. Dies ist sehr seltsam, da der Einstiegspunkt für die Migration genau die Assembly ist. Im Prinzip war es wahrscheinlich möglich, auf diese Weise eine parallele Versionierung durchzuführen, aber Sie müssen einen ziemlich komplizierten Wrapper über den Migrator schreiben.
Port ECM7.Migrator auf .NET Core
Am Anfang wurde diese Option nicht einmal in Betracht gezogen. Zu diesem Zeitpunkt war die aktuelle Version von .NET Core 1.1 und die API war schlecht kompatibel mit .NET Framework, in dem ECM7.Migrator funktionierte. Ich war mir sicher, dass das Portieren auf .NET Core schwierig und zeitaufwändig sein würde. Als es keine Optionen gab, um „das fertige zu nehmen“, beschloss ich, es zu versuchen. Die Aufgabe war einfacher als ich erwartet hatte. Überraschenderweise funktionierte es fast sofort. Es waren nur geringfügige Änderungen erforderlich. Da die Logik des Migrators durch Tests abgedeckt wurde, waren alle Stellen, die kaputt gingen, sofort sichtbar und ich reparierte sie schnell.
Jetzt habe ich Adapter für nur vier DBMS portiert: MS SQL Server, PostgreSQL, MySQL, SQLite. Ich habe keine Adapter für Oracle (da es noch keinen stabilen Client für .NET Core gibt), MS SQL Server CE (da es nur unter Windows funktioniert und ich keinen Platz zum Ausführen habe) und Firebird (weil es vorhanden ist) portiert nicht sehr beliebt, Hafen später). Wenn Sie Anbieter für diese oder andere DBMS erstellen müssen, ist dies im Prinzip recht einfach.
Der Quellcode für den neuen Migrator befindet sich auf GitHub . Konfigurierte den Start von Tests für jedes DBMS in Travis CI. Es wurde ein Befehlszeilenprogramm ( .NET Core Global Tool ) geschrieben, das einfach über NuGet installiert werden kann. Die Dokumentation ist geschrieben - ich habe mich sehr bemüht, detailliert und klar zu schreiben, und es scheint, dass es passiert ist. Sie können nehmen und verwenden!
Ein wenig über den Namen ...
Der neue Migrator ist nicht abwärtskompatibel mit dem alten. Sie arbeiten auf verschiedenen Plattformen und haben eine andere API. Daher wird das Projekt unter einem anderen Namen veröffentlicht.
Der Name wurde vom ThinkingHome- Projekt gewählt, für das ich einen Migrator portiert habe. Eigentlich ist ECM7.Migrator auch nach dem Projekt benannt, an dem ich gerade gearbeitet habe.
Vielleicht war es besser, einen neutralen Namen zu wählen, aber mir fiel es nicht ein, gute Optionen zu haben. Wenn Sie das wissen - schreiben Sie in die Kommentare. Es ist nicht zu spät, alles umzubenennen.
Die Versionsnummer gab 3.0.0 an, weil Der neue Migrator ist eine logische Fortsetzung des alten.
Schnellstart
Versuchen wir also, einen Migrator zu verwenden.
Alle Datenbankänderungen werden im Migrationscode aufgezeichnet - Klassen, die in einer Programmiersprache (z. B. in C #) geschrieben sind. Migrationsklassen erben von der Basisklasse Migration
vom ThinkingHome.Migrator.Framework- Paket. In ihnen müssen Sie die Methoden der Basisklasse überschreiben: Apply
(Änderungen übernehmen) und Revert
(Änderungen Revert
). Innerhalb dieser Methoden beschreibt der Entwickler mithilfe einer speziellen API die Aktionen, die für die Datenbank ausgeführt werden müssen.
Außerdem muss die Migrationsklasse mit dem Attribut [Migration]
und die Version angeben, in die die Datenbank nach diesen Änderungen verschoben wird.
Migrationsbeispiel
using ThinkingHome.Migrator.Framework; [Migration(12)] public class MyTestMigration : Migration { public override void Apply() { // : Database.AddTable("CustomerAddress", new Column("customerId", DbType.Int32, ColumnProperty.PrimaryKey), new Column("addressId", DbType.Int32, ColumnProperty.PrimaryKey)); } public override void Revert() { // : Database.RemoveTable("CustomerAddress"); // , // Revert } }
Wie man läuft
Migrationen werden in eine DLL-Datei kompiliert. Danach können Sie Datenbankänderungen mit dem Dienstprogramm zur migrate-database
. Installieren Sie es zunächst von NuGet .
dotnet tool install -g thinkinghome.migrator.cli
Führen Sie migrate-database
und geben Sie den erforderlichen DBMS-Typ, die Verbindungszeichenfolge und den Pfad zur DLL-Datei mit Migrationen an.
migrate-database postgres "host=localhost;port=5432;database=migrations;" /path/to/migrations.dll
Führen Sie die API aus
Sie können Migrationen über die API von Ihrer eigenen Anwendung aus durchführen. Sie können beispielsweise eine Anwendung schreiben, die beim Start selbst die gewünschte Datenbankstruktur erstellt.
Verbinden Sie dazu das ThinkingHome.Migrator- Paket von NuGet und das Paket mit dem Transformationsanbieter für das erforderliche DBMS mit Ihrem Projekt. Erstellen Sie anschließend eine Instanz der ThinkingHome.Migrator.Migrator
Klasse, rufen Sie die Migrate
Methode auf und übergeben Sie die erforderliche Version der Datenbank als Parameter.
var version = -1; // -1 var provider = "postgres"; var connectionString = "host=localhost;port=5432;database=migrations;"; var assembly = Assembly.LoadFrom("/path/to/migrations.dll"); using (var migrator = new Migrator(provider, connectionString, assembly)) { migrator.Migrate(version); }
Übrigens können Sie mit dem FluentMigrator-Startbeispiel vergleichen.
Fazit
Ich habe versucht, ein einfaches Werkzeug ohne Sucht und komplexe Magie zu entwickeln. Es scheint ganz gut geklappt zu haben. Das Projekt war lange Zeit nicht grob, alles wird in Tests behandelt, es gibt eine detaillierte Dokumentation in russischer Sprache. Wenn Sie .NET Core 2.1 verwenden, versuchen Sie es mit einem neuen Migrator . Höchstwahrscheinlich wird es Ihnen auch gefallen.