{"id":55000147,"date":"2018-10-01T00:00:00","date_gmt":"2020-03-27T19:34:02","guid":{"rendered":"http:\/\/access-im-unternehmen.aix-dev.de\/aiu\/?p=147"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-30T00:00:00","slug":"Entity_Framework_Datenbankmigration","status":"publish","type":"post","link":"https:\/\/vbentwickler.de\/Entity_Framework_Datenbankmigration\/","title":{"rendered":"Entity Framework: Datenbankmigration"},"content":{"rendered":"<p><b>Unter Access hatten Sie ein Problem, wenn Sie eine neue Version einer Datenbank ausliefern wollten, deren Datenmodell sich ge&auml;ndert hat. Dann war Handarbeit angesagt! Das Entity Framework bietet f&uuml;r das &Uuml;bertragen von &Auml;nderungen am Datenmodell die sogenannten Migrationen an. Dieser Artikel zeigt, was es sich damit auf sich hat und wann Sie Migrationen gegen&uuml;ber Datenbankinitialisierern nutzen sollten.<\/b><\/p>\n<p>Wenn Sie eine Datenbank weiterentwickeln, die schon im produktiven Betrieb genutzt wird, gibt es verschiedene &Auml;nderungen, die unterschiedlich gehandhabt werden. Wenn Sie lediglich &Auml;nderungen an der Benutzeroberfl&auml;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.<\/p>\n<p>Interessant wird es, wenn Sie nicht nur &Auml;nderungen an der Benutzeroberfl&auml;che oder der Anwendungslogik vornehmen, sondern wenn Sie das Entity Data Model manipulieren. Dann m&uuml;ssen n&auml;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 &Auml;nderungen an den Entities zuverl&auml;ssig in die Datenbank zu &uuml;bertragen.<\/p>\n<p>W&auml;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&ouml;schen und diese auf Basis des Entity Data Models neu aufbauen. Gegebenenfalls hat man dann noch mit der <b>Seed<\/b>-Methode einige Daten zu den frisch erstellten Tabellen hinzugef&uuml;gt. Wie das im Detail funktioniert, erfahren Sie im Artikel <b>Entity Framework: Datenbank-Initialisierer<\/b>.<\/p>\n<p>Sobald die Anwendung aber beim Kunden im Einsatz ist und dieser damit begonnen hat, Daten in die Tabellen zu schreiben oder diese zu &auml;ndern, kann man nicht mehr so einfach die komplette Datenbank l&ouml;schen und neu erstellen &#8211; dann w&auml;re die ganze Arbeit des Benutzers verloren. Alternativen w&auml;ren das manuelle Schreiben von SQL-Anweisungen, mit denen die &Auml;nderungen im Entity Data Model feingranular in die Datenbank &uuml;bertragen werde, ohne diese l&ouml;schen und neu erstellen zu m&uuml;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 &uuml;bertragen werden. Auch das ist mit viel Arbeit verbunden. Aber schauen wir uns doch an, was Entity Framework zu diesem Thema zu bieten hat.<\/p>\n<h2>Beispielprojekt<\/h2>\n<p>Das Beispielprojekt <b>DatabaseMigrations <\/b>auf Basis der Vorlage <b>Visual Basic|WPF-App <\/b>haben wir wieder mit einem neuen Element des Typs <b>ADO.NET Entity Data Model <\/b>namens <b>ArticleContext <\/b>ausgestattet, f&uuml;r das wir als Modellinhalt <b>Leeres Code First-Modell <\/b>gew&auml;hlt haben. Dem Projekt haben wir dann einen neuen Ordner namens <b>Data <\/b>mit den beiden Klassen <b>Article.cs <\/b>und <b>Category.cs <\/b>hinzugef&uuml;gt. Au&szlig;erdem haben wir der durch Entity Framework erstellten Klasse <b>ArticleContext.vb <\/b>noch die beiden <b>DbSet<\/b>-Auflistungen <b>Articles <\/b>und <b>Categories <\/b>hinzugef&uuml;gt (siehe Beispielprojekt).<\/p>\n<h2>Migrations<\/h2>\n<p>Dieses bietet n&auml;mlich eine Technik namens <b>Migrations<\/b> an. Dabei gibt es zwei verschiedene Funktionen:<\/p>\n<ul>\n<li>Code-basierte Migration: Muss durch den Entwickler ausgel&ouml;st werden.<\/li>\n<li>Automatische Migration: Erfolgt automatisch, hat aber Einschr&auml;nkungen.<\/li>\n<\/ul>\n<p>In den folgenden Abschnitten schauen wir uns die verschiedenen Funktionen an.<\/p>\n<h2>Code-basierte Migration<\/h2>\n<p>Die code-basierte Migration erfordert den Aufruf verschiedener Befehle &uuml;ber die Paket-Manager-Konsole. Diese starten Sie &uuml;ber den Men&uuml;befehl <b>Extras|NuGet-Paket-Manager|Paket-Manager-Konsole<\/b>. Hier k&ouml;nnen Sie die folgenden Anweisungen eingeben:<\/p>\n<ul>\n<li><b>Enable-Migrations<\/b>: Aktiviert die Migration f&uuml;r das aktuelle Projekt und erstellt einen neuen Ordner namens <b>Migrations <\/b>mit einer neuen Klasse namens <b>Configuration<\/b>.<\/li>\n<li><b>Add-Migration<\/b>: Erfasst die Unterschiede zwischen dem Entity Data Model im Vergleich zur Datenbank und tr&auml;gt diese in jeweils eine neue Klasse in zwei Methoden namens <b>Up <\/b>und <b>Down <\/b>ein. Die Methoden enthalten die Anweisungen zum Erstellen, L&ouml;schen oder &Auml;ndern der Datenbankobjekte entsprechend den &Auml;nderungen im Entity Data Model.<\/li>\n<li><b>Update-Database<\/b>: Diese Methode &uuml;bertr&auml;gt alle noch nicht &uuml;bertragenen &Auml;nderungen aus den mit <b>Add-Migration <\/b>erstellten Klassen in die Tabellen der Datenbank.<\/li>\n<\/ul>\n<p>Um die Migrations-Funktionen zu nutzen, m&uuml;ssen sie diese mit der ersten Anweisung <b>Enable-Migrations <\/b>aktivieren. Diese Anweisung setzen Sie in der Paket-Manager-Konsole ab (siehe Bild 1):<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2018_05\/pic_147_001.png\" alt=\"Aktivieren der Datenbank-Migrationen\" width=\"549,6265\" height=\"238,786\"\/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 1: Aktivieren der Datenbank-Migrationen<\/span><\/b><\/p>\n<pre>Enable-Migrations<\/pre>\n<p>Dies quittiert die Paket-Manager-Konsole damit, dass Code First-Migrationen f&uuml;r das Projekt aktiviert sind. Was hat sich dadurch ver&auml;ndert Im Projektmappen-Explorer finden wir nun einen neuen Ordner namens <b>Migrations <\/b>mit der Klasse <b>Configurations.vb<\/b> (siehe Bild 2).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2018_05\/pic_147_002.png\" alt=\"Neuer Ordner Migrations\" width=\"349,7625\" height=\"298,1027\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 2: Neuer Ordner Migrations<\/span><\/b><\/p>\n<p>Die Klasse stellt zwei Methoden bereit &#8211; die Konstruktor-Methode <b>New <\/b>sowie eine Methode namens <b>Seed<\/b>:<\/p>\n<pre>Imports System.Data.Entity.Migrations\r\nNamespace Migrations\r\n     Friend NotInheritable Class Configuration \r\n         Inherits DbMigrationsConfiguration(Of ArticleContext)\r\n         <span style=\"color:blue;\">Public <\/span>Sub New()\r\n             AutomaticMigrationsEnabled = <span style=\"color:blue;\">False<\/span>\r\n         End Sub\r\n         Protected Overrides Sub Seed(context<span style=\"color:blue;\"> As <\/span>ArticleContext)\r\n             ''  This method will be called after migrating to the latest version.\r\n             ''  You can use the DbSet(Of T).AddOrUpdate() helper extension method \r\n             ''  to avoid creating duplicate seed data.\r\n         End Sub\r\n     End Class\r\nEnd Namespace<\/pre>\n<p>Die Konstruktor-Methode stellt den Wert der Variablen <b>AutomaticMigrationsEnabled <\/b>auf den Wert <b>False <\/b>ein. Was bedeutet das Sie setzen <b>AutomaticMigrationsEnabled <\/b>auf <b>True<\/b>, 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 <b>Automatische Migration aktivieren <\/b>an. Wenn Sie die Variable auf dem Wert <b>False <\/b>belassen, m&uuml;ssen Sie die Migrationsschritte selbst definieren. Das schauen wir uns gleich im Anschluss an.<\/p>\n<h2>&Auml;nderungen in Klasse notieren<\/h2>\n<p>Danach k&ouml;nnen Sie erstmals die Anweisung <b>Add-Migration <\/b>aufrufen und dieser einen Parameter mitgeben, der sp&auml;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 <b>Init <\/b>im Namen der zu erstellenden Klasse tragen. Die folgende Anweisung setzen Sie ebenfalls in der Paket-Manager-Konsole ab. Die Ausf&uuml;hrung kann einige Sekunden dauern:<\/p>\n<pre>Add-Migration Init<\/pre>\n<p>Die so erstellte Klasse hei&szlig;t <b>201811070831578_init.vb <\/b>und taucht im Ordner <b>Migrations <\/b>des Projektmappen-Explorers auf (siehe Bild 3). Sie sieht mit den beiden Methoden <b>Up <\/b>und <b>Down <\/b>in gek&uuml;rzter Form wie folgt aus:<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2018_05\/pic_147_003.png\" alt=\"Neue Klasse, die auf init.vb endet\" width=\"424,7115\" height=\"321,9441\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 3: Neue Klasse, die auf init.vb endet<\/span><\/b><\/p>\n<pre>Imports System.Data.Entity.Migrations\r\nNamespace Migrations\r\n     <span style=\"color:blue;\">Public <\/span>Partial Class init\r\n         Inherits DbMigration\r\n         <span style=\"color:blue;\">Public <\/span>Overrides Sub Up()\r\n             CreateTable(\"dbo.Articles\",\r\n                 Function(c) <span style=\"color:blue;\">New<\/span> <span style=\"color:blue;\">With<\/span>\r\n                     {\r\n                         .ID = c.Int(nullable := False, identity := <span style=\"color:blue;\">True<\/span>),\r\n                         .Name = c.String(),\r\n                         .CategoryID = c.Int(nullable := <span style=\"color:blue;\">False<\/span>),\r\n                         .Price = c.Decimal(nullable := False, precision := 18, scale := 2),\r\n                         .Description = c.String()\r\n                     }) _\r\n                 .PrimaryKey(Function(t) t.ID) _\r\n                 .ForeignKey(\"dbo.Categories\", Function(t) t.CategoryID, cascadeDelete := <span style=\"color:blue;\">True<\/span>) _\r\n                 .Index(Function(t) t.CategoryID)\r\n             CreateTable(\r\n                 \"dbo.Categories\",\r\n                 Function(c) <span style=\"color:blue;\">New<\/span> <span style=\"color:blue;\">With<\/span>\r\n                     {\r\n                         .ID = c.Int(nullable := False, identity := <span style=\"color:blue;\">True<\/span>),\r\n                         .Name = c.String()\r\n                     }) _\r\n                 .PrimaryKey(Function(t) t.ID)\r\n         End Sub\r\n         \r\n         <span style=\"color:blue;\">Public <\/span>Overrides Sub Down()\r\n             DropForeignKey(\"dbo.Articles\", \"CategoryID\", \"dbo.Categories\")\r\n             DropIndex(\"dbo.Articles\", <span style=\"color:blue;\">New<\/span> String() { \"CategoryID\" })\r\n             DropTable(\"dbo.Categories\")\r\n             DropTable(\"dbo.Articles\")\r\n         End Sub\r\n     End Class\r\nEnd Namespace<\/pre>\n<p>Die meisten der Methoden, die hier zum Einsatz kommen, stammen aus dem Namespace <b>System.Data.Entity.Migrations<\/b>. Die Methode <b>Up <\/b>enth&auml;lt zwei <b>CreateTable<\/b>-Anweisungen, mit denen die Tabellen <b>Articles <\/b>und <b>Categories <\/b>erstellt werden sollen. Wir haben also hier eine M&ouml;glichkeit, Tabellen und ihre Felder sowie die Restriktionen objektorientiert anzulegen statt mit T-SQL-Anweisungen. Letztendlich werden die Befehle nat&uuml;rlich auch wieder in T-SQL &uuml;bersetzt, aber f&uuml;r manchen Entwickler, der sich vielleicht nicht mit T-SQL anfreunden m&ouml;chte, bietet sich hier eine Alternative. Die Methode <b>Down <\/b>enth&auml;lt vier Anweisungen, mit denen nacheinander die Restriktionen und die Tabellen gel&ouml;scht werden.<\/p>\n<h2>&Auml;nderungen in die Datenbank &uuml;bertragen<\/h2>\n<div class=\"rcp_restricted\"><p><span style=\"color: #ff0000;\">M&ouml;chten Sie weiterlesen? Dann l&ouml;sen Sie Ihr Ticket!<\/span><br \/>\n<span style=\"color: #ff0000;\">Hier geht es zur Bestellung des Jahresabonnements des Magazins <strong>Visual Basic Entwickler<\/strong>:<\/span><br \/>\n<span style=\"color: #ff0000;\"><a style=\"color: #ff0000;\" href=\"https:\/\/shop.minhorst.com\/magazine\/363\/visual-basic-entwickler-jahresabonnement?c=77\">Zur Bestellung ...<\/a><\/span><br \/>\n<span style=\"color: #ff0000;\">Danach greifen Sie sofort auf <strong>alle rund 200 Artikel<\/strong> unseres Angebots zu - auch auf diesen hier!<\/span><br \/>\n<span style=\"color: #000000;\">Oder haben Sie bereits Zugangsdaten? Dann loggen Sie sich gleich hier ein:<\/span><\/p>\n<\/div>\n\n\t\n\t<form id=\"rcp_login_form\"  class=\"rcp_form\" method=\"POST\" action=\"https:\/\/vbentwickler.de\/data\/wp\/v2\/posts\/55000147\/\">\n\n\t\t\n\t\t<fieldset class=\"rcp_login_data\">\n\t\t\t<p>\n\t\t\t\t<label for=\"rcp_user_login\">Username or Email<\/label>\n\t\t\t\t<input name=\"rcp_user_login\" id=\"rcp_user_login\" class=\"required\" type=\"text\"\/>\n\t\t\t<\/p>\n\t\t\t<p>\n\t\t\t\t<label for=\"rcp_user_pass\">Password<\/label>\n\t\t\t\t<input name=\"rcp_user_pass\" id=\"rcp_user_pass\" class=\"required\" type=\"password\"\/>\n\t\t\t<\/p>\n\t\t\t\t\t\t<p>\n\t\t\t\t<input type=\"checkbox\" name=\"rcp_user_remember\" id=\"rcp_user_remember\" value=\"1\"\/>\n\t\t\t\t<label for=\"rcp_user_remember\">Remember me<\/label>\n\t\t\t<\/p>\n\t\t\t<p class=\"rcp_lost_password\"><a href=\"\/data\/wp\/v2\/posts\/55000147?rcp_action=lostpassword\"><\/a><\/p>\n\t\t\t<p>\n\t\t\t\t<input type=\"hidden\" name=\"rcp_action\" value=\"login\"\/>\n\t\t\t\t<input type=\"hidden\" name=\"rcp_redirect\" value=\"https:\/\/vbentwickler.de\/data\/wp\/v2\/posts\/55000147\/\"\/>\n\t\t\t\t<input type=\"hidden\" name=\"rcp_login_nonce\" value=\"123b777de9\"\/>\n\t\t\t\t<input id=\"rcp_login_submit\" class=\"rcp-button\" type=\"submit\" value=\"Login\"\/>\n\t\t\t<\/p>\n\t\t\t\t\t<\/fieldset>\n\n\t\t\n\t<\/form>\n<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Unter Access hatten Sie ein Problem, wenn Sie eine neue Version einer Datenbank ausliefern wollten, deren Datenmodell sich ge&auml;ndert hat. Dann war Handarbeit angesagt! Das Entity Framework bietet f&uuml;r das &Uuml;bertragen von &Auml;nderungen am Datenmodell die sogenannten Migrationen an. Dieser Artikel zeigt, was es sich damit auf sich hat und wann Sie Migrationen gegen&uuml;ber Datenbankinitialisierern nutzen sollten.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"om_disable_all_campaigns":false,"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"_uf_show_specific_survey":0,"_uf_disable_surveys":false,"footnotes":""},"categories":[662018,66052018,44000021],"tags":[],"yst_prominent_words":[],"class_list":["post-55000147","post","type-post","status-publish","format-standard","hentry","category-662018","category-66052018","category-Entity_Framework"],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/posts\/55000147","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/comments?post=55000147"}],"version-history":[{"count":0,"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/posts\/55000147\/revisions"}],"wp:attachment":[{"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/media?parent=55000147"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/categories?post=55000147"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/tags?post=55000147"},{"taxonomy":"yst_prominent_words","embeddable":true,"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/yst_prominent_words?post=55000147"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}