Entity Framework: Datenbankmigration

Wenn Sie ein Abonnement des Magazins 'DATENBANKENTWICKLER' besitzen, können Sie sich anmelden und den kompletten Artikel lesen.
Anderenfalls können Sie das Abonnement hier im Shop erwerben.

Entity Framework: Datenbankmigration

Unter Access hatten Sie ein Problem, wenn Sie eine neue Version einer Datenbank ausliefern wollten, deren Datenmodell sich geändert hat. Dann war Handarbeit angesagt! Das Entity Framework bietet für das Übertragen von Änderungen am Datenmodell die sogenannten Migrationen an. Dieser Artikel zeigt, was es sich damit auf sich hat und wann Sie Migrationen gegenüber Datenbankinitialisierern nutzen sollten.

Wenn Sie eine Datenbank weiterentwickeln, die schon im produktiven Betrieb genutzt wird, gibt es verschiedene Änderungen, die unterschiedlich gehandhabt werden. Wenn Sie lediglich Änderungen an der Benutzeroberfläche oder an der Anwendungslogik vornehmen, haben Sie leichtes Spiel: Sie brauchen dann einfach nur die neue Version der Anwendung zu verteilen, die dann auf die vorhandene Datenbank zugreift.

Interessant wird es, wenn Sie nicht nur Änderungen an der Benutzeroberfläche oder der Anwendungslogik vornehmen, sondern wenn Sie das Entity Data Model manipulieren. Dann müssen nämlich auch die Tabellen in der zugrunde liegenden Datenbank angepasst werden, da sonst das Modell nicht mehr zur Datenbank passt. Hier wird es interessant, denn Entity Framework bietet die sogenannten Migrations, um Änderungen an den Entities zuverlässig in die Datenbank zu übertragen.

Während der Entwicklung der Anwendung, also bevor diese erstmals auf dem Rechner des Benutzers landet, ist der Umgang mit Aktualisierungen im Entity Data Model einfach: Man konnte dann einfach die komplette vorhandene Datenbank löschen und diese auf Basis des Entity Data Models neu aufbauen. Gegebenenfalls hat man dann noch mit der Seed-Methode einige Daten zu den frisch erstellten Tabellen hinzugefügt. Wie das im Detail funktioniert, erfahren Sie im Artikel Entity Framework: Datenbank-Initialisierer.

Sobald die Anwendung aber beim Kunden im Einsatz ist und dieser damit begonnen hat, Daten in die Tabellen zu schreiben oder diese zu ändern, kann man nicht mehr so einfach die komplette Datenbank löschen und neu erstellen – dann wäre die ganze Arbeit des Benutzers verloren. Alternativen wären das manuelle Schreiben von SQL-Anweisungen, mit denen die Änderungen im Entity Data Model feingranular in die Datenbank übertragen werde, ohne diese löschen und neu erstellen zu müssen. Oder man erstellt die Datenbank neu und erzeugt ein Skript, mit dem die Daten aus der Datenbank in der alten Version in die neue Version übertragen werden. Auch das ist mit viel Arbeit verbunden. Aber schauen wir uns doch an, was Entity Framework zu diesem Thema zu bieten hat.

Beispielprojekt

Das Beispielprojekt DatabaseMigrations auf Basis der Vorlage Visual Basic|WPF-App haben wir wieder mit einem neuen Element des Typs ADO.NET Entity Data Model namens ArticleContext ausgestattet, für das wir als Modellinhalt Leeres Code First-Modell gewählt haben. Dem Projekt haben wir dann einen neuen Ordner namens Data mit den beiden Klassen Article.cs und Category.cs hinzugefügt. Außerdem haben wir der durch Entity Framework erstellten Klasse ArticleContext.vb noch die beiden DbSet-Auflistungen Articles und Categories hinzugefügt (siehe Beispielprojekt).

Migrations

Dieses bietet nämlich eine Technik namens Migrations an. Dabei gibt es zwei verschiedene Funktionen:

  • Code-basierte Migration: Muss durch den Entwickler ausgelöst werden.
  • Automatische Migration: Erfolgt automatisch, hat aber Einschränkungen.

In den folgenden Abschnitten schauen wir uns die verschiedenen Funktionen an.

Code-basierte Migration

Die code-basierte Migration erfordert den Aufruf verschiedener Befehle über die Paket-Manager-Konsole. Diese starten Sie über den Menübefehl Extras|NuGet-Paket-Manager|Paket-Manager-Konsole. Hier können Sie die folgenden Anweisungen eingeben:

  • Enable-Migrations: Aktiviert die Migration für das aktuelle Projekt und erstellt einen neuen Ordner namens Migrations mit einer neuen Klasse namens Configuration.
  • Add-Migration: Erfasst die Unterschiede zwischen dem Entity Data Model im Vergleich zur Datenbank und trägt diese in jeweils eine neue Klasse in zwei Methoden namens Up und Down ein. Die Methoden enthalten die Anweisungen zum Erstellen, Löschen oder Ändern der Datenbankobjekte entsprechend den Änderungen im Entity Data Model.
  • Update-Database: Diese Methode überträgt alle noch nicht übertragenen Änderungen aus den mit Add-Migration erstellten Klassen in die Tabellen der Datenbank.

Um die Migrations-Funktionen zu nutzen, müssen sie diese mit der ersten Anweisung Enable-Migrations aktivieren. Diese Anweisung setzen Sie in der Paket-Manager-Konsole ab (siehe Bild 1):

Aktivieren der Datenbank-Migrationen

Bild 1: Aktivieren der Datenbank-Migrationen

Enable-Migrations

Dies quittiert die Paket-Manager-Konsole damit, dass Code First-Migrationen für das Projekt aktiviert sind. Was hat sich dadurch verändert? Im Projektmappen-Explorer finden wir nun einen neuen Ordner namens Migrations mit der Klasse Configurations.vb (siehe Bild 2).

Neuer Ordner Migrations

Bild 2: Neuer Ordner Migrations

Die Klasse stellt zwei Methoden bereit – die Konstruktor-Methode New sowie eine Methode namens Seed:

Imports System.Data.Entity.Migrations
Namespace Migrations
     Friend NotInheritable Class Configuration 
         Inherits DbMigrationsConfiguration(Of ArticleContext)
         Public Sub New()
             AutomaticMigrationsEnabled = False
         End Sub
         Protected Overrides Sub Seed(context As ArticleContext)
             '  This method will be called after migrating to the latest version.
             '  You can use the DbSet(Of T).AddOrUpdate() helper extension method 
             '  to avoid creating duplicate seed data.
         End Sub
     End Class
End Namespace

Die Konstruktor-Methode stellt den Wert der Variablen AutomaticMigrationsEnabled auf den Wert False ein. Was bedeutet das? Sie setzen AutomaticMigrationsEnabled auf True, wenn Sie keine Versionierung in dem Sinne planen, dass Sie nur Upgrades verwenden und keine Downgrades und keine strikte Versionierung planen. Das schauen wir uns weiter unten unter Automatische Migration aktivieren an. Wenn Sie die Variable auf dem Wert False belassen, müssen Sie die Migrationsschritte selbst definieren. Das schauen wir uns gleich im Anschluss an.

Änderungen in Klasse notieren

Danach können Sie erstmals die Anweisung Add-Migration aufrufen und dieser einen Parameter mitgeben, der später als Teil des Namens der durch diese Anweisung erstellten Klasse verwendet wird (der Rest besteht aus Datum und Zeit). Die erste Migration soll beispielsweise den Text Init im Namen der zu erstellenden Klasse tragen. Die folgende Anweisung setzen Sie ebenfalls in der Paket-Manager-Konsole ab. Die Ausführung kann einige Sekunden dauern:

Add-Migration Init

Die so erstellte Klasse heißt 201811070831578_init.vb und taucht im Ordner Migrations des Projektmappen-Explorers auf (siehe Bild 3). Sie sieht mit den beiden Methoden Up und Down in gekürzter Form wie folgt aus:

Neue Klasse, die auf init.vb endet

Bild 3: Neue Klasse, die auf init.vb endet

Imports System.Data.Entity.Migrations
Namespace Migrations
     Public Partial Class init
         Inherits DbMigration
         Public Overrides Sub Up()
             CreateTable("dbo.Articles",
                 Function(c) New With
                     {
                         .ID = c.Int(nullable := False, identity := True),
                         .Name = c.String(),
                         .CategoryID = c.Int(nullable := False),
                         .Price = c.Decimal(nullable := False, precision := 18, scale := 2),
                         .Description = c.String()
                     }) _
                 .PrimaryKey(Function(t) t.ID) _
                 .ForeignKey("dbo.Categories", Function(t) t.CategoryID, cascadeDelete := True) _
                 .Index(Function(t) t.CategoryID)
             CreateTable(

Dies war die Leseprobe dieses Artikels.
Melden Sie sich an, um auf den vollständigen Artikel zuzugreifen.

Bitte geben Sie die Zeichenfolge in das nachfolgende Textfeld ein

Die mit einem * markierten Felder sind Pflichtfelder.

Ich habe die Datenschutzbestimmungen zur Kenntnis genommen.