Ich habe mich in der letzten Zeit etwas mit dem Entity-Framework 6, speziell dem Code-First Ansatz beschäftigt, da ich dieses für ein neues Projekt verwenden möchte.
Ich denke das praktische an Code-First ist, dass man sein Model nur noch als POCO´s im Quelltext hinterlegt und das Framework selbst sowohl die Erstellung der Datenbank/Tabellen im jeweiliges SQl Dialekt des angebundenen Providers übernimmt, als auch die O/R Mappings zur Datenbank herstellt.
Dies bedeutet dann Theoretisch das man wirklich ohne Code-Änderung auf verschiedene Datenbank-Provider zugreifen kann.
Die Theorie ist darauf beschränkt das es zwar schon Entity Framework 6 Provider z.B. für MSSQL, MySQL, SQLite, PostgreSQl und FireBird gibt, aber im Prinzip nur der Microsoft-Eigene SQL Provider für SQl-Server, LocalDB und SQL Azure den Code-First Ansatz mit Migrationen wirklich vollständig unterstützt.
Vom Mysql-Provider gibt es derzeit eine Development Version (6.8.1.0) welche vom Ansatz her funktioniert, aber z.B. bei UTF8-Datenbanken Probleme macht (bezogen auf die Keys, welche in der __Migration Tabelle erzeugt werden).
Am einfachsten ist es, wenn man SQL LocalDB zum testen Benutzt (da dies auch der Standard-Provider für das EF6 ist).
Aus diesem Grund habe ich im Visual Studio 2013 ein neues Winforms-Projekt mit dem Namen „EF6LocalDB“ angelegt.
In die Form habe ich einen einfachen Button gelegt der vorerst noch nicht gefüllt wird (Typische 1-Knopf-Anwendung).
Um das Entity Framework 6 erst einmal nutzen zu können, muss man es via NuGet installieren.
Dazu ruft man die Paket-Manager-Konsole auf und tippt dort folgendes ein:
Install-Package EntityFramework -Version 6.0.1 |
Als Ausgabe erhält man daraufhin folgendes:
"EntityFramework 6.0.1" wird installiert. EntityFramework wird von Microsoft heruntergeladen. Die hierfür geltenden Lizenzbedingungen sind unter http://go.microsoft.com/fwlink/?LinkID=320539 verfügbar. Überprüfen Sie das Paket auf zusätzliche Abhängigkeiten, die ggf. eigenen Lizenzbedingungen unterliegen. Durch die Verwendung des Pakets und der Abhängigkeiten stimmen Sie diesen Lizenzbedingungen zu. Wenn Sie den Lizenzbedingungen nicht zustimmen, löschen Sie die relevanten Komponenten von Ihrem Gerät. "EntityFramework 6.0.1" wurde erfolgreich installiert. "EntityFramework 6.0.1" wird "EF6LocalDB" hinzugefügt. "EntityFramework 6.0.1" wurde "EF6LocalDB" erfolgreich hinzugefügt. Type 'get-help EntityFramework' to see all available Entity Framework commands. |
Damit wurde das Entity Framework 6 in unserem Projekt hinzugefügt.
Nun brauchen wir noch einen Connectionstring zum SQL LocalDB in der App.Config.
Ich habe bei mir eine LocalDB Instanz mit dem Namen „Lokaltest“, der Standard wäre hier „v11“.
Folgendes wird in der App.config hinzugefügt um SQL LocalDB mit der Instanz „Lokaltest“ und der Datenbank „myDatabase“ zu benutzen:
<connectionStrings> <add name="Context" connectionString="Data Source=(LocalDB)\Lokaltest;Database=myDataBase;Integrated Security=True" providerName="System.Data.SqlClient" /> </connectionStrings> |
Damit sieht die gesamte App.config wie folgt aus:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | <?xml version="1.0" encoding="utf-8"?> <configuration> <configSections> <!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 --> <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" /> </configSections> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" /> </startup> <entityFramework> <defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework"> <parameters> <parameter value="v11.0" /> </parameters> </defaultConnectionFactory> <providers> <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" /> </providers> </entityFramework> <connectionStrings> <add name="Context" connectionString="Data Source=(LocalDB)\Lokaltest;Database=myDataBase;Integrated Security=True" providerName="System.Data.SqlClient" /> </connectionStrings> </configuration> |
Als nächtes möchte ich meinen Datenbank-Context anlegen und erst einmal ein Stumpfes Objekt (Personen) mit ein paar Eigenschaften dort registrieren.
Dafür erstelle ich die Klasse „context.cs“ mit folgendem Inhalt:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | using System; using System.Collections.Generic; using System.Data.Entity; using System.Linq; using System.Text; using System.Threading.Tasks; namespace EF6LocalDB { class Context : DbContext { public Context() : base("Context") { } public DbSet<Personen> Personen { get; set; } } class Personen { public int id { get; set; } public string Name { get; set; } public string EMail { get; set; } public DateTime Erstelldat { get; set; } } } |
Über den String „Context“ im Konstruktor der gleichnamigen DBContext Klasse übergebe ich den Namen des Connctionstrings aus der App.config.
Der nächste Schritt ist nun über das Entity-Framework die Migrations-Dateien erstellen zu lassen.
Migrationsdateien sind in diesem Fall kleine C# Klassen in welchen durch LINQ definiert wird wie der Tabellen-Aufbau auszusehen hat – quasi die CREATE TABLE und ALTER TABLE in LINQ.
Um dies zu erreichen gibt man in der Paket-Manager Konsole den Befehl „Enable-Migrations“ ein und erhält folgende Ausgabe:
PM> Enable-Migrations Checking if the context targets an existing database... Code First Migrations enabled for project EF6LocalDB. |
Im Projekt wurde nun ein neuer Ordner „Migrations“ hinzugefügt in dem sich eine Configuration.cs befindet in welcher Migrations-Spezifische Einstellungen geändert werden können. Vorerst lassen wir die Datei so wie sie erstellt wurde.
Als nächsten Schritt nach dem aktivieren der Migrationen wollen wir nun auch eine Migration erstellen.
Dies funktioniert über den Befehl „Add-Migration“ – dieser Fragt dann nach einem Namen für die Migration (ich wähle hier einfach „v1“ für Version1):
PM> Add-Migration Cmdlet Add-Migration an der Befehlspipelineposition 1 Geben Sie Werte für die folgenden Parameter an: Name: v1 Scaffolding migration 'v1'. |
Was ist nun passiert?
Im Prinzip hat sich das Add-Migration Script alle über das DbSet<> registrierte Klassen angeschaut und herausgefunden ob Klassen hinzugekommen oder geändert wurden.
Da wir die Klasse „Personen“ neu hinzugefügt haben, haben wir nun auch eine Migrationsdatei für genau diese Klasse bekommen um daraus im LocalDB eine Tabelle anlegen zu können.
Die Definition für diese Klasse/Tabelle befindet sich nun im besteheneden Migration-Ordner.
Sie trägt den Namen 201311301721422_v1.cs (Datum/Zeit der Erstellung + den zusätzlich abgefragten Namen „v1“) und hat den folgenden Inhalt:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | namespace EF6LocalDB.Migrations { using System; using System.Data.Entity.Migrations; public partial class v1 : DbMigration { public override void Up() { CreateTable( "dbo.Personens", c => new { id = c.Int(nullable: false, identity: true), Name = c.String(), EMail = c.String(), Erstelldat = c.DateTime(nullable: false), }) .PrimaryKey(t => t.id); } public override void Down() { DropTable("dbo.Personens"); } } } |
Man kann sehr schön sehen wie die Tabelle anhand von LINQ Statements angelegt werden soll.
Um diese Tabelle nun auch im LocalDB anzulegen, bzw das Migrations-Script auszuführen ist der Befehl „Update-Database“ notwendig.
Dieser Durchsucht alle Migrations-Dateien und überträgt die Änderungen in die Datenbank, sofern dies noch nicht geschehen ist:
PM> Update-Database Specify the '-Verbose' flag to view the SQL statements being applied to the target database. Applying explicit migrations: [201311301721422_v1]. Applying explicit migration: 201311301721422_v1. Running Seed method. |
Und damit ist wieder einmal ein Stück Magie passiert.
Schauen wir uns jetzt die LocalDb Instanz im SQL Management Studio an, sehen wir das die Datenbank „myDatabase“ und ebenfalls 2 Tabellen angelegt wurden.
Zum einen die Tabelle „__MigrationHistory“ in der das Entity Framework die Migrationen Listet die Bereits in der Datenbank getätigt wurden – und zum andere die Tabelle „Personens“, welche sich aus dem Aufbau unserer Klasse „Personen“ ergibt.
Sollte man nun Änderungen an der Klasse machen oder eine neue Klasse mit in das DbSet<> aufnehmen wollen, erstellt man sich eine neue Migrationsdatei über „Add-Migration“ und überträgt die Änderungen danach über „Update-Database“ in die Datenbank.
Um eine Auflistung der Migrationen zu bekommen, welche bereits in der Datenbank getätigt wurden, setzt man den Befehl „Get-Migrations“ in der Paket-Manager Konsole ab und bekommt dann die folgende Ausgabe:
PM> Get-Migrations Retrieving migrations that have been applied to the target database. 201311301721422_v1 |
Als Ergebnis der ganzen Ef6-Spielerei können wir nun unseren DBContext in der 1-Knopf-Anwendung benutzen um z.B. einen Datensatz anzulegen:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | private void button1_Click(object sender, EventArgs e) { // DBContext Instanziieren Context DB = new Context(); // Neues Personen Objekt erzeugen und mit Daten füllen Personen p = new Personen(); p.Name = "Sven"; p.EMail = "sven@frickelblog.de"; p.Erstelldat = DateTime.Now; // Objekt der Tabelle hinzufügen DB.Personen.Add(p); // Änderungen in die Datenbank Speichern DB.SaveChanges(); } |
Wenn man nun auf den Knopf klickt, wird der Datensatz in der Tabelle angelegt, als Ergebnis erhält man:
id Name EMail Erstelldat ----------- --------- ---------------------- ----------------------- 1 Sven sven@frickelblog.de 2013-11-30 19:37:41.667 |
Ebenfalls könnte man sich nun genauso gut mit LINQ eine Liste aller Datensätze der Personen-Tabelle ausgeben lassen:
1 2 3 4 5 | // DBContext Instanziieren Context DB = new Context(); // Alle Datensätze der Tabelle "Personen" in eine Liste ausgeben List<Personen> lp = (from tPersonen in DB.Personen select tPersonen).ToList(); |
Ja – Das war es erst mal zum Thema EF6 und Code-First.
Ist auf jeden Fall eine sehr Spannende Sache die unter Microsoft-Eigenen Produkten auch schon recht gut funktioniert.
Ich hoffe ich konnte dem ein oder anderen von euch mit diesem Artikel etwas helfen.
Vielen Dank für die Aufmerksamkeit,
Sven
Vom Feinsten, Sven 🙂 Klappt wunderbar wenn man nicht zu blöd ist das korrekte Standardprojekt für die Console auszuwählen 😀