Entity Framework Code First - Teil 01

Mit der Version 4 des Entity Frameworks wurde das Code First Programmiermodell eingeführt, dabei leitet das Entity Framework die Tabellen für die Datenbank automatisch aus den Datenklassen und ihren Eigenschaften ab.

Inzwischen ist das Entity Framework in der Verion 7 verfügbar. Dieser Artikel soll einen kurzen Überblick über die wichtigsten Funktionen geben, die seit der Version 4 vorhanden sind.

Modellklassen und Datenbankkontext erstellen

In dem folgenden Beispiel soll ein einfaches Datenmodell bestehend aus den zwei Entitäten Buch (eng.: Book) und Autor (eng.: Author) erstellt werden. Dazu werden zunächst die zwei POCO Klassen Book und Author erstellt.

[DebuggerDisplay("Book: [{BookId}] - {Title}")]
  public class Book {
   public int BookId { get; set; }
   public string Title { get; set; }
 }

 [DebuggerDisplay("Author: [{AuthorId}] - {Name}")]
 public class Author {
   public int AuthorId { get; set; }
   public string Name { get; set; }
 }

Neben den Datenklassen wird noch der Kontext für die Datenbank benötigt. Dazu wird die Klasse BookContext von der Klasse DbContext abgeleitet. Diese Basisklasse enthält alle Funktionen, die später die POCO Modellklassen mit der Datenbank verbinden.

namespace EF6IntroducationDemo {
  public class BookContext : DbContext {
    public BookContext() : base("BookDb") {  }

    public DbSet<Author> Authors { get; set; }
    public DbSet<Book> Books { get; set; }
  }  
}

Um dem Entity Framework mitteilen zu können, welche Datenbank verwendet werden soll, kann dem Konstruktor der Basisklasse des Kontextes (DbKontext) ein Name übergeben werden. In dem Beispiel ist das BookDb. Wird kein Name übergeben, verwendet Entity Framework automatisch den Namen, der sich aus dem Namensraum der Kontextklasse und deren Namen zusammensetzt. Im Beispiel wäre das EF6IntroducationDemo.BookContext

Anschließend wird versucht ein Connection String zu finden, mit dem entsprechenden Namen, wenn dieser nicht existiert wird automatisch eine Datenbank mit Hilfe des Default Providers unter diesem Namen angelegt. Dabei hängt es von dem Provider ab, ob eine .mdf Datei, eine .sdf Datei oder eine Datenbank auf dem lokalen Server erzeugt wird.

Um in dem Beispiel eine Datei mit Hilfe von localdb zu erstellen, wird folgender Connection String in die Anwendungskonfiguration eingefügt.

<connectionStrings>
  <add name="BookDb" providerName="System.Data.SqlClient" 
  connectionString="Server=(LocalDB)\v11.0; Integrated Security=True;AttachDbFileName=D:\EF6IntroducationDemo\demo.mdf;"/>
</connectionStrings>

Erste Verwendung

Der Zugriff auf die Datenbanken erfolgt mit Hilfe einer neuen Instanz der Kontextklasse. Diese implementiert die Schnittstelle IDisposable, um ggf. nicht benötigte Ressourcen wie z.B. Verbindungen zur Datenbank oder gecachte Einträge wieder frei geben zu können. Aus diesem Grund sollte der Kontext möglichst immer inerhalb eines using - Statements verwendet werden.

using (var db = new BookContext()) {
   // Erzeugen und Speichern eines Buches
   var b = new Book { Title = "JavaScript" };
   db.Books.Add(b);
   db.SaveChanges();
   . . .
   // Abfragen des ersten Buches
   var firstBook = db.Books.FirstOrDefault();
   if (firstBook != null) Console.WriteLine(firstBook.Title)
}

Beim ersten Aufruf dieses Beispielprogramms hat das Entity Framework automatisch die in dem ConnectionString angegebene Datenbank angelegt. Dabei wurde die Struktur der zu erstellenden Tabellen automatisch aus den POCO Klassen abgeleitet.

Der Name der Tabelle(n) wird aus dem Klassennamen ermittelt. Alle öffentlichen Eigenschaften werden automatisch in Tabellen-Spalten des entsprechenden Types abgebildet. Endet der Name einer Eigenschaft auf Id, wird diese Eigenschaft automatisch als Primärschlüssel (eng. primary key) festgelegt. Ist diese Eigenschaft zusätzlich vom Type Int32 bzw. int so wird diese zusätzlich als Identity Spalte gekennzeichnet.

Um die Datenbank für dieses kleine Beispiel zu erstellen, hat das Entity Framework folgende SQL auf der angegebenen Datei ausgefüht. Der Name der erzeugten Datenbank wurde zusätzlich um einen zufälligen Hash-Wert ergänzt, so dass der Name eindeutig ist.

Create Database [BOOKDB_0fc46a8a734e4e93acf652cbff2ba3fb]
CREATE TABLE [dbo].[Authors] (
    [AuthorId] [int] NOT NULL IDENTITY,
    [Name] [nvarchar](max),
    CONSTRAINT [PK_dbo.Authors] PRIMARY KEY ([AuthorId])
)
CREATE TABLE [dbo].[Books] (
    [BookId] [int] NOT NULL IDENTITY,
    [Title] [nvarchar](max),
    CONSTRAINT [PK_dbo.Books] PRIMARY KEY ([BookId])
)
CREATE TABLE [dbo].[__MigrationHistory] (
    [MigrationId] [nvarchar](150) NOT NULL,
    [ContextKey] [nvarchar](300) NOT NULL,
    [Model] [varbinary](max) NOT NULL,
    [ProductVersion] [nvarchar](32) NOT NULL,
    CONSTRAINT [PK_dbo.__MigrationHistory] PRIMARY KEY ([MigrationId], [ContextKey])
)

Weiter zum zweiten Teil: Entity Framework Code First Navigation Properties.