Code First to a New Database (Code First für eine neue Datenbank)

Dieses Video und eine schrittweise exemplarische Vorgehensweise bieten eine Einführung in die Code First-Entwicklung für eine neue Datenbank. In diesem Szenario können Sie eine Datenbank auswählen, die noch nicht existiert und die Code First erstellen soll, oder eine leere Datenbank, der Code First neue Tabellen hinzufügen soll. Mit Code First können Sie Ihr Modell mithilfe von C# oder VB.Net-Klassen definieren. Eine Zusätzliche Konfiguration kann optional mithilfe von Attributen für Ihre Klassen und Eigenschaften oder mithilfe einer Fluent-API ausgeführt werden.

Video ansehen

Dieses Video bietet eine Einführung in die Code First-Entwicklung für eine neue Datenbank. In diesem Szenario können Sie eine Datenbank auswählen, die noch nicht existiert und die Code First erstellen soll, oder eine leere Datenbank, der Code First neue Tabellen hinzufügen soll. Mit Code First können Sie Ihr Modell mithilfe von C# oder VB.Net-Klassen definieren. Eine Zusätzliche Konfiguration kann optional mithilfe von Attributen für Ihre Klassen und Eigenschaften oder mithilfe einer Fluent-API ausgeführt werden.

Präsentation:Rowan Miller

Video: WMV | MP4 | WMV (ZIP)

Voraussetzungen

Sie müssen mindestens Visual Studio 2010 oder Visual Studio 2012 installiert haben, um diese exemplarische Vorgehensweise abzuschließen.

Wenn Sie Visual Studio 2010 verwenden, müssen Sie auch NuGet installiert haben.

1. Erstellen der Anwendung

Um die Dinge einfach zu halten, erstellen wir eine einfache Konsolenanwendung, die Code First zum Ausführen des Datenzugriffs verwendet.

  • Öffnen Sie Visual Studio.
  • Datei -> Neu -> Projekt
  • Wählen Sie im linken Menü Windows und Konsolenanwendung aus.
  • Geben Sie CodeFirstNewDatabaseSample als Namen ein.
  • Klicken Sie auf OK.

2. Erstellen des Modells

Lassen Sie uns ein sehr einfaches Modell mithilfe von Klassen definieren. Wir definieren sie nur in der Datei Program.cs, aber in einer realen Anwendung würden Sie Ihre Klassen in separate Dateien und möglicherweise ein separates Projekt aufteilen.

Fügen Sie unter der Klassendefinition „Program.cs“ die folgenden beiden Klassen hinzu.

public class Blog
{
    public int BlogId { get; set; }
    public string Name { get; set; }

    public virtual List<Post> Posts { get; set; }
}

public class Post
{
    public int PostId { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }

    public int BlogId { get; set; }
    public virtual Blog Blog { get; set; }
}

Sie werden feststellen, dass wir die beiden Navigationseigenschaften (Blog.Posts und Post.Blog) virtuell erstellen. Dies ermöglicht das Lazy Loading-Feature von Entity Framework. Lazy Loading bedeutet, dass der Inhalt dieser Eigenschaften automatisch aus der Datenbank geladen wird, wenn Sie versuchen, darauf zuzugreifen.

3. Erstellen eines Kontexts

Jetzt ist es an der Zeit, einen abgeleiteten Kontext zu definieren, der eine Sitzung mit der Datenbank darstellt, sodass wir Daten abfragen und speichern können. Wir definieren einen Kontext, der von „System.Data.Entity.DbContext“ abgeleitet wird und eine typisierte DbSet<TEntity> für jede Klasse in unserem Modell verfügbar macht.

Wir beginnen nun, Typen aus dem Entity Framework zu verwenden, daher müssen wir das EntityFramework NuGet-Paket hinzufügen.

  • Projekt – > NuGet-Pakete verwalten... Hinweis: Wenn Sie nicht über die Option NuGet-Pakete verwalten verfügen, sollten Sie die neueste Version von NuGet installieren.
  • Wählen Sie die Registerkarte Online aus.
  • Wählen Sie das EntityFramework-Paket aus.
  • Klicken Sie auf Install (Installieren).

Fügen Sie oben in „Program.cs“ eine using-Anweisung für „System.Data.Entity“ hinzu.

using System.Data.Entity;

Fügen Sie unter der Post-Klasse in Program.cs den folgenden abgeleiteten Kontext hinzu.

public class BloggingContext : DbContext
{
    public DbSet<Blog> Blogs { get; set; }
    public DbSet<Post> Posts { get; set; }
}

Hier ist eine vollständige Auflistung der Elemente, die Program.cs jetzt enthalten soll.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Data.Entity;

namespace CodeFirstNewDatabaseSample
{
    class Program
    {
        static void Main(string[] args)
        {
        }
    }

    public class Blog
    {
        public int BlogId { get; set; }
        public string Name { get; set; }

        public virtual List<Post> Posts { get; set; }
    }

    public class Post
    {
        public int PostId { get; set; }
        public string Title { get; set; }
        public string Content { get; set; }

        public int BlogId { get; set; }
        public virtual Blog Blog { get; set; }
    }

    public class BloggingContext : DbContext
    {
        public DbSet<Blog> Blogs { get; set; }
        public DbSet<Post> Posts { get; set; }
    }
}

Das ist der gesamte Code, den wir zum Speichern und Abrufen von Daten benötigen. Hinter den Kulissen geht natürlich einiges vor sich und wir werden uns das gleich ansehen, aber lassen Sie uns das Ganze erst einmal in Aktion sehen.

4. Lesen und Schreiben von Daten

Implementieren Sie die Main-Methode wie unten dargestellt in „Program.cs“. Dieser Code erstellt eine neue Instanz unseres Kontexts und fügt dann einen neuen Blog ein. Anschließend wird eine LINQ-Abfrage verwendet, um alle Blogs aus der Datenbank abzurufen, die alphabetisch nach „Title“ sortiert sind.

class Program
{
    static void Main(string[] args)
    {
        using (var db = new BloggingContext())
        {
            // Create and save a new Blog
            Console.Write("Enter a name for a new Blog: ");
            var name = Console.ReadLine();

            var blog = new Blog { Name = name };
            db.Blogs.Add(blog);
            db.SaveChanges();

            // Display all Blogs from the database
            var query = from b in db.Blogs
                        orderby b.Name
                        select b;

            Console.WriteLine("All blogs in the database:");
            foreach (var item in query)
            {
                Console.WriteLine(item.Name);
            }

            Console.WriteLine("Press any key to exit...");
            Console.ReadKey();
        }
    }
}

Sie können die Anwendung jetzt ausführen und testen.

Enter a name for a new Blog: ADO.NET Blog
All blogs in the database:
ADO.NET Blog
Press any key to exit...

Wo sind meine Daten?

Standardmäßig hat DbContext eine Datenbank für Sie erstellt.

  • Wenn eine lokale SQL Express-Instanz verfügbar ist (standardmäßig mit Visual Studio 2010 installiert), hat Code First die Datenbank in dieser Instanz erstellt.
  • Wenn SQL Express nicht verfügbar ist, versucht Code First, LocalDB zu verwenden (standardmäßig mit Visual Studio 2012 installiert).
  • Die Datenbank wird nach dem vollqualifizierten Namen des abgeleiteten Kontexts benannt, in unserem Fall CodeFirstNewDatabaseSample.BloggingContext.

Dies sind nur die Standardkonventionen, und es gibt verschiedene Möglichkeiten, die Datenbank zu ändern, die Code First verwendet. Weitere Informationen finden Sie im Thema Wie DbContext die Modell- und Datenbankverbindung entdeckt. Sie können eine Verbindung mit der Datenbankdatei herstellen, indem Sie den Server-Explorer in Visual Studio verwenden.

  • Ansicht –>Server-Explorer

  • Klicken Sie mit der rechten Maustaste auf Datenverbindungen und wählen Sie Verbindung hinzufügen… aus.

  • Wenn Sie im Server-Explorer noch keine Verbindung mit einer Datenbank hergestellt haben, müssen Sie Microsoft SQL Server als Datenquelle auswählen.

    Select Data Source

  • Herstellen einer Verbindung mit LocalDB oder SQL Express, je nachdem, welches Sie installiert haben.

Wir können nun das Schema untersuchen, das Code First erstellt hat.

Schema Initial

DbContext hat anhand der von uns definierten DbSet-Eigenschaften ermittelt, welche Klassen in das Modell aufgenommen werden sollen. Anschließend wird der Standardsatz von Code First-Konventionen verwendet, um Tabellen- und Spaltennamen zu bestimmen, Datentypen zu bestimmen, Primärschlüssel zu finden usw. Weiter unten in dieser exemplarischen Vorgehensweise wird erläutert, wie Sie diese Konventionen außer Kraft setzen können.

5. Umgang mit Modelländerungen

Jetzt ist es an der Zeit, einige Änderungen an unserem Modell vorzunehmen. Wenn wir diese Änderungen vornehmen, müssen wir auch das Datenbankschema aktualisieren. Dazu verwenden wir ein Feature namens „Code First Migrationen“ oder kurz „Migrationen“.

Migrationen ermöglichen es uns, einen geordneten Satz von Schritten zu haben, die beschreiben, wie sie unser Datenbankschema aktualisieren (und herabstufen). Jeder dieser Schritte, die als Migration bezeichnet werden, enthält einen Code, der die Änderungen beschreibt, die vorgenommen werden sollen. 

Aktivieren Sie dafür zunächst Code First-Migrationen für den unseren BloggingContext.

  • Wählen Sie Tools – > Bibliotheks-Paket-Manger – > PaketManager-Konsole aus.

  • Führen Sie in der Paket-Manager-Konsole den Befehl Enable-Migrations aus.

  • Dem Projekt wurde ein neuer Ordner „Migrationen“ hinzugefügt, der zwei Elemente enthält:

    • Configuration.cs: Diese Datei enthält die Einstellungen, die Migrationen für die Migration von BloggingContext verwenden. Wir müssen nichts für diese exemplarische Vorgehensweise ändern, aber hier können Sie Seeddaten angeben, Anbieter für andere Datenbanken registrieren, den Namespace ändern, in dem Migrationen generiert werden, usw.
    • <Zeitstempel>_InitialCreate.cs: Dies ist Ihre erste Migration. Sie stellt die Änderungen dar, die bereits an der Datenbank vorgenommen wurden, um sie von einer leeren Datenbank zu einer Datenbank zu machen, die die Tabellen Blogs und Posts enthält. Obwohl wir Code First diese Tabellen automatisch für uns erstellen ließen, wurden sie jetzt, da wir uns für Migrationen entschieden haben, in eine Migration umgewandelt. Code First hat auch in unserer lokalen Datenbank vermerkt, dass diese Migration bereits durchgeführt wurde. Der Zeitstempel für den Dateinamen wird zu Sortierungszwecken verwendet.

    Lassen Sie uns nun eine Änderung an unserem Modell vornehmen. Fügen Sie der Blogklasse eine URL-Eigenschaft hinzu:

public class Blog
{
    public int BlogId { get; set; }
    public string Name { get; set; }
    public string Url { get; set; }

    public virtual List<Post> Posts { get; set; }
}
  • Führen Sie in der Paket-Manager-Konsole den Befehl Add-Migration AddUrl aus. Der Befehl „Add-Migration“ sucht nach Änderungen seit der letzten Migration und erstellt ein Gerüst für eine neue Migration mit allen gefundenen Änderungen. Wir können Migrationen einen Namen geben; in diesem Fall nennen wir die Migration „AddUrl“. Der Gerüstcode besagt, dass wir eine URL-Spalte hinzufügen müssen, die Zeichenfolgendaten enthalten kann, zum dbo.Blogs-Tabelle. Falls nötig, könnten wir den gerüsteten Code bearbeiten, aber das ist in diesem Fall nicht erforderlich.
namespace CodeFirstNewDatabaseSample.Migrations
{
    using System;
    using System.Data.Entity.Migrations;

    public partial class AddUrl : DbMigration
    {
        public override void Up()
        {
            AddColumn("dbo.Blogs", "Url", c => c.String());
        }

        public override void Down()
        {
            DropColumn("dbo.Blogs", "Url");
        }
    }
}
  • Führen Sie in der Paket-Manager-Konsole den Befehl Update-Database aus. Update-Database übernimmt alle ausstehenden Migrationen für die Datenbank. Unsere InitialCreate-Migration wurde bereits angewendet, sodass Migrationen nur unsere neue AddUrl-Migration anwenden. Tipp: Sie können den Schalter –Verbose verwenden, wenn Sie Update-Database aufrufen, um das SQL zu sehen, das an der Datenbank ausgeführt wird.

Die neue URL-Spalte wird nun zur Tabelle „Blogs“ in der Datenbank hinzugefügt:

Schema With Url

6. Datenanmerkungen

Bisher haben wir EF das Modell einfach anhand seiner Standardkonventionen erkennen lassen, aber es wird immer wieder vorkommen, dass unsere Klassen nicht den Konventionen folgen und wir in der Lage sein müssen, weitere Konfigurationen vorzunehmen. Hierfür gibt es zwei Optionen. In diesem Abschnitt werden wir uns die Datenanmerkungen und dann im nächsten Abschnitt die Fluent-API ansehen.

  • Fügen wir unserem Modell eine User-Klasse hinzu.
public class User
{
    public string Username { get; set; }
    public string DisplayName { get; set; }
}
  • Außerdem müssen wir einen Satz zu unserem abgeleiteten Kontext hinzufügen.
public class BloggingContext : DbContext
{
    public DbSet<Blog> Blogs { get; set; }
    public DbSet<Post> Posts { get; set; }
    public DbSet<User> Users { get; set; }
}
  • Wenn wir versuchen, eine Migration hinzuzufügen, erhalten wir die Fehlermeldung „EntityType 'User' hat keinen Schlüssel definiert. Definieren Sie den Schlüssel für diesen EntityType.”, da EF nicht wissen kann, dass Username der Primärschlüssel für User sein sollte.
  • Da wir nun Datenanmerkungen verwenden werden, müssen wir eine using-Anweisung am Anfang von Program.cs hinzufügen.
using System.ComponentModel.DataAnnotations;
  • Kommentieren Sie nun die Username-Eigenschaft, um zu identifizieren, dass sie der Primärschlüssel ist.
public class User
{
    [Key]
    public string Username { get; set; }
    public string DisplayName { get; set; }
}
  • Verwenden Sie den Befehl Add-Migration AddUser, um eine Migration zu erstellen und diese Änderungen auf die Datenbank anzuwenden.
  • Führen Sie die neue Migration mit dem Befehl Update-Database für die Datenbank aus.

Die neue Tabelle wird nun der Datenbank hinzugefügt:

Schema With Users

Die vollständige Liste der Anmerkungen, die von EF unterstützt werden, ist:

7. Fluent-API

Im vorangegangenen Abschnitt haben wir uns mit der Verwendung von Datenkommentaren befasst, um das, was durch Konventionen erkannt wurde, zu ergänzen oder außer Kraft zu setzen. Die andere Möglichkeit zum Konfigurieren des Modells ist die Code First Fluent-API.

Die meisten Modellkonfigurationen können mit einfachen Datenanmerkungen durchgeführt werden. Die Fluent-API ist eine fortschrittlichere Methode zur Angabe der Modellkonfiguration, die alles abdeckt, was Datenannotationen können, und zusätzlich einige fortschrittlichere Konfigurationen ermöglicht, die mit Datenanmerkungen nicht möglich sind. Datenanmerkungen und die Fluent-API können zusammen verwendet werden.

Um auf die Fluent-API zuzugreifen, überschreiben Sie die OnModelCreating-Methode in DbContext. Angenommen, wir möchten die Spalte, in der User.DisplayName gespeichert ist, in display_name umbenennen.

  • Überschreiben Sie die OnModelCreating-Methode auf BloggingContext mit dem folgenden Code.
public class BloggingContext : DbContext
{
    public DbSet<Blog> Blogs { get; set; }
    public DbSet<Post> Posts { get; set; }
    public DbSet<User> Users { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<User>()
            .Property(u => u.DisplayName)
            .HasColumnName("display_name");
    }
}
  • Verwenden Sie den Befehl Add-Migration ChangeDisplayName, um eine Migration zu erstellen und diese Änderungen auf die Datenbank anzuwenden.
  • Führen Sie die neue Migration mit dem Befehl Update-Database für die Datenbank aus.

Die Spalte „DisplayName“ wird jetzt in display_name umbenannt:

Schema With Display Name Renamed

Zusammenfassung

In dieser exemplarischen Vorgehensweise haben wir uns die Code First-Entwicklung mithilfe einer neuen Datenbank angesehen. Wir haben ein Modell mithilfe von Klassen definiert und dann dieses Modell verwendet, um eine Datenbank zu erstellen und Daten zu speichern und abzurufen. Nachdem die Datenbank erstellt wurde, haben wir Code First-Migrationen verwendet, um das Schema zu ändern, während sich unser Modell weiterentwickelt hat. Außerdem haben wir erfahren, wie Sie ein Modell mithilfe von Datenanmerkungen und der Fluent-API konfigurieren.