Übung: Einrichten einer Migration

Abgeschlossen

In dieser Lerneinheit erstellen Sie C#-Entitätsklassen, die Tabellen in einer lokalen SQLite-Datenbank zugeordnet werden. Das EF Core-Migrationsfeature erstellt Tabellen aus diesen Entitäten.

Eine Migration bietet eine Möglichkeit, das Datenbankschema inkrementell zu aktualisieren.

Abrufen der Projektdateien

Rufen Sie zunächst die Projektdateien ab. Es gibt verschiedene Möglichkeiten, die Projektdateien abzurufen:

  • Verwenden von GitHub Codespaces
  • Klonen des GitHub-Repositorys

Wenn Sie eine kompatible Containerruntime installiert haben, können Sie auch die Dev Containers-Erweiterung verwenden, um das Repository in einem Container mit vorinstallierten Tools zu öffnen.

Verwenden von GitHub Codespaces

Ein Codespace ist eine in der Cloud gehostete IDE. Wenn Sie GitHub Codespaces verwenden, wechseln Sie zum Repository in Ihrem Browser. Wählen Sie Code aus, und erstellen Sie dann einen neuen Codespace im main-Branch.

Klonen des GitHub-Repositorys

Wenn Sie GitHub Codespaces nicht verwenden, können Sie das GitHub-Projektrepository klonen und die Dateien dann als Ordner in Visual Studio Code öffnen.

  1. Öffnen Sie ein Befehlsterminal, und klonen Sie dann das Projekt aus GitHub mithilfe der Eingabeaufforderung:

    git clone https://github.com/MicrosoftDocs/mslearn-persist-data-ef-core
    
  2. Wechseln Sie zum Ordner mslearn-persist-data-ef-core, und öffnen Sie dann das Projekt in Visual Studio Code:

    cd mslearn-persist-data-ef-core
    code .
    

Überprüfen des Codes

Nun verfügen Sie über die Projektdateien, mit denen Sie arbeiten können. Sehen Sie sich an, was im Projekt enthalten ist, und überprüfen Sie den Code.

  • Das Projekt ist eine ASP.NET Core-Web-API, die sich im Verzeichnis ContosoPizza befindet. Die Dateipfade, auf die in diesem Modul verwiesen wird, sind relativ zum ContosoPizza-Verzeichnis.
  • Services/PizzaService.cs ist eine Dienstklasse, die CRUD-Methoden definiert. Alle Methoden lösen zurzeit eine System.NotImplementedException aus.
  • In Program.cs ist PizzaService über das Abhängigkeitsinjektionssystem von ASP.NET Core registriert.
  • Controllers/PizzaController.cs ist ein Wert für ApiController, der einen Endpunkt für die HTTP-Verben „POST“, „GET“, „PUT“ und „DELETE“ verfügbar macht. Diese Verben rufen die entsprechenden CRUD-Methoden für PizzaService auf. PizzaService wird in den Konstruktor PizzaController eingefügt.
  • Der Ordner Models enthält die Modelle, die von PizzaService und PizzaController verwendet werden.
  • Die Entitätsmodelle Pizza.cs, Topping.cs und Sauce.cs weisen die folgenden Beziehungen auf:
    • Eine Pizza kann mit einer oder mehreren Zutaten belegt sein.
    • Ein Belag kann auf einer oder mehreren Pizzas verwendet werden.
    • Eine Pizza kann nur eine Soße aufweisen, aber eine Soße kann auf vielen Pizzas verwendet werden.

Erstellen der App

Gehen Sie wie folgt vor, um die App in Visual Studio Code zu erstellen:

  1. Klicken Sie im Bereich Explorer mit der rechten Maustaste auf das Verzeichnis ContosoPizza, und wählen Sie In integriertem Terminal öffnen aus.

    Dadurch wird ein Terminalbereich für das Verzeichnis ContosoPizza geöffnet.

  2. Verwenden Sie den folgenden Befehl, um die App zu erstellen:

    dotnet build
    

    Der Code sollte ohne Warnungen oder Fehler erstellt werden.

Hinzufügen von NuGet-Paketen und EF Core-Tools

Die Datenbank-Engine, mit der Sie in diesem Modul arbeiten, ist SQLite. SQLite ist eine schlanke, dateibasierte Datenbank-Engine. Sie eignet sich gut für Entwicklung und Tests, aber auch für kleine Produktionsbereitstellungen.

Hinweis

Wie bereits erwähnt, können Datenbankanbieter in EF Core ergänzt werden. SQLite ist eine gute Wahl für dieses Modul, da es schlank und plattformübergreifend ist. Sie können denselben Code verwenden, um verschiedene andere Datenbank-Engines wie SQL Server und PostgreSQL zu verwenden. Sie können sogar mehrere Datenbank-Engines in derselben App verwenden.

Bevor Sie beginnen, fügen Sie die erforderlichen Pakete hinzu:

  1. Führen Sie im Terminalbereich den folgenden Befehl aus:

    dotnet add package Microsoft.EntityFrameworkCore.Sqlite
    

    Dieser Befehl fügt das NuGet-Paket hinzu, das den EF Core-SQLite-Datenbankanbieter und alle zugehörigen Abhängigkeiten enthält, einschließlich der allgemeinen EF Core-Dienste.

  2. Führen Sie den folgenden Befehl aus:

    dotnet add package Microsoft.EntityFrameworkCore.Design
    

    Dieser Befehl fügt Pakete hinzu, die für die EF Core-Tools erforderlich sind.

  3. Führen Sie zum Abschluss den folgenden Befehl aus:

    dotnet tool install --global dotnet-ef
    

    Dieser Befehl installiert dotnet ef, das Tool, mit dem Sie Migrationen und Gerüstbau erstellen.

    Tipp

    Wenn dotnet ef bereits installiert ist, können Sie dieses Tool aktualisieren, indem Sie dotnet tool update --global dotnet-ef ausführen.

Modellgerüste und DbContext

Jetzt fügen Sie eine DbContext-Implementierung hinzu und konfigurieren diese. DbContext ist ein Gateway, über das Sie mit der Datenbank interagieren können.

  1. Klicken Sie mit der rechten Maustaste auf das Verzeichnis ContosoPizza, und fügen Sie einen neuen Ordner namens Data hinzu.

  2. Erstellen Sie im Ordner Data eine neue Datei namens PizzaContext.cs. Fügen Sie der leeren Datei den folgenden Code hinzu:

    using Microsoft.EntityFrameworkCore;
    using ContosoPizza.Models;
    
    namespace ContosoPizza.Data;
    
    public class PizzaContext : DbContext
    {
        public PizzaContext (DbContextOptions<PizzaContext> options)
            : base(options)
        {
        }
    
        public DbSet<Pizza> Pizzas => Set<Pizza>();
        public DbSet<Topping> Toppings => Set<Topping>();
        public DbSet<Sauce> Sauces => Set<Sauce>();
    }
    

    Für den Code oben gilt:

    • Der Konstruktor akzeptiert einen Parameter vom Typ DbContextOptions<PizzaContext>. Durch den Konstruktor kann externer Code an die Konfiguration übergeben werden, sodass dasselbe DbContext-Element von Test- und Produktionscode gemeinsam genutzt und sogar mit verschiedenen Anbietern verwendet werden kann.
    • Die DbSet<T>-Eigenschaften entsprechen Tabellen, die in der Datenbank erstellt werden sollen.
    • Die Tabellennamen stimmen mit den Eigenschaftsnamen DbSet<T> in der Klasse PizzaContext überein. Sie können dieses Verhalten bei Bedarf außer Kraft setzen.
    • Bei Instanziierung macht PizzaContext die Eigenschaften Pizzas, Toppings und Sauces verfügbar. Änderungen, die Sie an den von diesen Eigenschaften verfügbar gemachten Sammlungen vornehmen, werden an die Datenbank weitergegeben.
  3. Ersetzen Sie // Add the PizzaContext in der Datei Program.cs durch den folgenden Code:

    builder.Services.AddSqlite<PizzaContext>("Data Source=ContosoPizza.db");
    

    Der obige Code:

    • PizzaContext wird mit dem ASP.NET Core-Abhängigkeitsinjektionssystem registriert.
    • Gibt an, dass PizzaContext den SQLite-Datenbankanbieter verwenden soll.
    • Definiert eine SQLite-Verbindungszeichenfolge, die auf die lokale Datei ContosoPizza.db verweist.

    Hinweis

    Da SQLite lokale Datenbankdateien verwendet, stellt die Hartcodierung der Verbindungszeichenfolge wahrscheinlich kein Problem dar. Für Netzwerkdatenbanken wie PostgreSQL und SQL Server sollten Sie Ihre Verbindungszeichenfolgen grundsätzlich sicher speichern. Verwenden Sie für die lokale Entwicklung den Secret Manager. Bei Produktionsbereitstellungen sollten Sie einen Dienst wie Azure Key Vault in Betracht ziehen.

  4. Ersetzen Sie zudem // Additional using declarations in der Datei Program.cs durch den folgenden Code.

    using ContosoPizza.Data;
    

    Dieser Code löst Abhängigkeiten aus dem vorherigen Schritt auf.

  5. Speichern Sie alle Änderungen. GitHub Codespaces speichert Ihre Änderungen automatisch.

  6. Erstellen Sie die App im Terminal, indem Sie dotnet build ausführen. Der Buildvorgang sollte erfolgreich und ohne Warnungen oder Fehler abgeschlossen werden.

Erstellen und Ausführen einer Migration

Als Nächstes erstellen Sie eine Migration, mit der Sie Ihre anfängliche Datenbank erstellen können.

  1. Führen Sie den folgenden Befehl aus, um eine Migration zum Erstellen der Datenbanktabellen zu generieren:

    dotnet ef migrations add InitialCreate --context PizzaContext
    

    Für den obigen Befehl gilt Folgendes:

    • Die Migration erhält den Namen InitialCreate.
    • Die Option --context gibt den Namen der Klasse im Projekt ContosoPizza an, der von DbContext abgeleitet wird.

    Im Projektstamm ContosoPizza wird ein neues Verzeichnis Migrations angezeigt. Das Verzeichnis enthält die Datei <timestamp>_InitialCreate.cs, die Datenbankänderungen beschreibt, die in ein Datendefinitionssprache-Änderungsskript (Data Definition Language, DDL) übersetzt werden sollen.

  2. Führen Sie den folgenden Befehl aus, um die Migration InitialCreate anzuwenden:

    dotnet ef database update --context PizzaContext
    

    Mit diesem Befehl wird die Migration angewendet. Da ContosoPizza.db nicht vorhanden ist, wird die Migration im Projektverzeichnis erstellt.

    Tipp

    Das dotnet ef-Tool wird auf allen Plattformen unterstützt. In Visual Studio unter Windows können Sie die PowerShell-Cmdlets Add-Migration und Update-Database im integrierten Fenster der Paket-Manager-Konsole verwenden.

Untersuchen der Datenbank

EF Core hat eine Datenbank für Ihre App erstellt. Sehen Sie sich nun die Datenbank mithilfe der SQLite-Erweiterung genauer an.

  1. Klicken Sie im Bereich Explorer mit der rechten Maustaste auf die Datei ContosoPizza.db, und wählen Sie Datenbank öffnen aus.

    Screenshot: Menüoption „Datenbank öffnen“ im Explorer-Bereich in Visual Studio Code

    Im Explorer-Bereich wird der Ordner SQLite Explorer angezeigt.

    Screenshot: Ordner „SQLite Explorer“ im Explorer-Bereich

  2. Wählen Sie den Ordner SQLite Explorer aus, um den Knoten und alle untergeordneten Knoten zu erweitern. Klicken Sie mit der rechten Maustaste auf ContosoPizza.db, und wählen Sie Tabelle „sqlite_master“ anzeigen aus, um das vollständige Datenbankschema und die Einschränkungen anzuzeigen, die von der Migration erstellt wurden.

    Screenshot: Erweiterter Ordner „SQLite Explorer“ im Explorer-Bereich

    • Es wurden Tabellen erstellt, die den einzelnen Entitäten entsprechen.
    • Tabellennamen wurden aus den Namen der DbSet-Eigenschaften für PizzaContext entnommen.
    • Eigenschaften namens Id wurden als automatisch inkrementierte Primärschlüsselfelder abgeleitet.
    • Die Namenskonventionen für Primärschlüssel- und Fremdschlüsseleinschränkungen von EF Core sind PK_<primary key property> bzw. FK_<dependent entity>_<principal entity>_<foreign key property>. Die Platzhalter <dependent entity> und <principal entity> entsprechen den Namen der Entitätsklasse.

    Hinweis

    Wie ASP.NET Core MVC verwendet EF Core einen Konvention-vor-Konfiguration-Ansatz. EF Core-Konventionen verkürzen die Entwicklungszeit, indem sie auf die Absicht des Entwicklers schließen. So wird beispielsweise aus einer Eigenschaft namens Id oder <entity name>Id abgeleitet, dass sie der Primärschlüssel der generierten Tabelle ist. Wenn Sie die Benennungskonvention nicht übernehmen möchten, muss die Eigenschaft mit dem Attribut [Key] oder als Schlüssel in der OnModelCreating-Methode von DbContext konfiguriert werden.

Ändern des Modells und Aktualisieren des Datenbankschemas

Ihre Vorgesetzten bei Contoso Pizza nennen Ihnen einige neue Anforderungen, weshalb Sie Ihre Entitätsmodelle ändern müssen. In den folgenden Schritten ändern Sie die Modelle mithilfe von Zuordnungsattributen (manchmal auch als Datenanmerkungen bezeichnet).

  1. Nehmen Sie in Models\Pizza.cs die folgenden Änderungen vor:

    1. Fügen Sie eine using-Anweisung für System.ComponentModel.DataAnnotations hinzu.
    2. Fügen Sie vor der Name-Eigenschaft ein [Required]-Attribut hinzu, um die Eigenschaft als erforderlich zu markieren.
    3. Fügen Sie vor der Name-Eigenschaft ein [MaxLength(100)]-Attribut hinzu, um eine maximale Zeichenfolgenlänge von 100 anzugeben.
    using System.ComponentModel.DataAnnotations;
    
    namespace ContosoPizza.Models;
    
    public class Pizza
    {
        public int Id { get; set; }
    
        [Required]
        [MaxLength(100)]
        public string? Name { get; set; }
    
        public Sauce? Sauce { get; set; }
    
        public ICollection<Topping>? Toppings { get; set; }
    }
    
  2. Nehmen Sie in Models\Sauce.cs die folgenden Änderungen vor:

    1. Fügen Sie eine using-Anweisung für System.ComponentModel.DataAnnotations hinzu.
    2. Fügen Sie vor der Name-Eigenschaft ein [Required]-Attribut hinzu, um die Eigenschaft als erforderlich zu markieren.
    3. Fügen Sie vor der Name-Eigenschaft ein [MaxLength(100)]-Attribut hinzu, um eine maximale Zeichenfolgenlänge von 100 anzugeben.
    4. Fügen Sie eine bool-Eigenschaft namens IsVegan hinzu.
    using System.ComponentModel.DataAnnotations;
    
    namespace ContosoPizza.Models;
    
    public class Sauce
    {
        public int Id { get; set; }
    
        [Required]
        [MaxLength(100)]
        public string? Name { get; set; }
    
        public bool IsVegan { get; set; }
    }
    
  3. Nehmen Sie in Models\Topping.cs die folgenden Änderungen vor:

    1. Fügen Sie using-Anweisungen für System.ComponentModel.DataAnnotations und System.Text.Json.Serialization hinzu.
    2. Fügen Sie vor der Name-Eigenschaft ein [Required]-Attribut hinzu, um die Eigenschaft als erforderlich zu markieren.
    3. Fügen Sie vor der Name-Eigenschaft ein [MaxLength(100)]-Attribut hinzu, um eine maximale Zeichenfolgenlänge von 100 anzugeben.
    4. Fügen Sie eine decimal-Eigenschaft namens Calories unmittelbar nach der Name-Eigenschaft hinzu.
    5. Fügen Sie eine Pizzas-Eigenschaft vom Typ ICollection<Pizza>? hinzu, um aus Pizza-Topping eine m:n-Beziehung zu machen.
    6. Fügen Sie der Pizzas-Eigenschaft ein [JsonIgnore]-Attribut hinzu.

    Wichtig

    Durch diese Schritte wird verhindert, dass Topping-Entitäten die Pizzas-Eigenschaft einschließen, wenn der Web-API-Code die Antwort in JSON serialisiert. Ohne diese Änderung würde eine serialisierte Sammlung von Belägen eine Sammlung mit jeder Pizza enthalten, die den jeweiligen Belag verwendet. Jede Pizza in dieser Auflistung enthielte wiederum eine Auflistung von Belägen, die jeweils wieder eine Auflistung von Pizzas umfassen würde. Diese Art von Endlosschleife wird als Zirkelbezug bezeichnet und kann nicht serialisiert werden.

    using System.ComponentModel.DataAnnotations;
    using System.Text.Json.Serialization;
    
    namespace ContosoPizza.Models;
    
    public class Topping
    {
        public int Id { get; set; }
    
        [Required]
        [MaxLength(100)]
        public string? Name { get; set; }
    
        public decimal Calories { get; set; }
    
        [JsonIgnore]
        public ICollection<Pizza>? Pizzas { get; set; }
    }
    
  4. Speichern Sie alle Änderungen, und führen Sie dotnet build aus.

  5. Führen Sie den folgenden Befehl aus, um eine Migration zum Erstellen der Datenbanktabellen zu generieren:

    dotnet ef migrations add ModelRevisions --context PizzaContext
    

    Eine Migration namens ModelRevisions wird erstellt.

    Hinweis

    Folgende Meldung wird angezeigt: Ein Vorgang wurde geplant, der zum Verlust von Daten führen kann. Überprüfen Sie die Migration auf Genauigkeit. Die Meldung wird angezeigt, da Sie die Beziehung von Pizza mit Topping von 1:n in m:n geändert haben, was erfordert, dass eine vorhandene Fremdschlüsselspalte gelöscht wird. Da in Ihrer Datenbank noch keine Daten vorhanden sind, ist diese Änderung nicht problematisch. Im Allgemeinen empfiehlt es sich jedoch, die generierte Migration zu überprüfen, wenn diese Warnung angezeigt wird. So können Sie sicherstellen, dass durch die Migration keine Daten gelöscht oder abgeschnitten werden.

  6. Führen Sie den folgenden Befehl aus, um die Migration ModelRevisions anzuwenden:

    dotnet ef database update --context PizzaContext
    
  7. Klicken Sie auf der Titelleiste des Ordners SQLite Explorer auf die Schaltfläche Datenbanken aktualisieren.

    Screenshot: Schaltfläche „Datenbanken aktualisieren“ auf der Titelleiste des Ordners „SQLite Explorer“

  8. Klicken Sie im Ordner SQLite Explorer mit der rechten Maustaste auf ContosoPizza.db. Wählen Sie Tabelle „sqlite_master“ anzeigen aus, um das vollständige Datenbankschema und die Einschränkungen anzuzeigen.

    Wichtig

    Die SQLite-Erweiterung verwendet geöffnete SQLite-Registerkarten erneut.

    • Eine Jointabelle PizzaTopping wurde erstellt, um die m:n-Beziehung zwischen Pizzas und Belägen darzustellen.
    • Toppings und Sauces wurden neue Felder hinzugefügt.
      • Calories wird als text-Spalte definiert, weil SQLite keinen entsprechenden decimal-Typ aufweist.
      • In ähnlicher Weise wird IsVegan als integer-Spalte definiert. SQLite definiert keinen bool-Typ.
      • In beiden Fällen verwaltet EF Core die Übersetzung.
    • Die Name-Spalte in jeder Tabelle wurde als not null markiert, SQLite weist jedoch keine MaxLength-Einschränkung auf.

    Tipp

    EF Core-Datenbankanbieter ordnen den Features einer bestimmten Datenbank ein Modellschema zu. Im Gegensatz zu anderen Datenbanken wie SQL Server und PostgreSQL implementiert SQLite keine entsprechende Einschränkung für MaxLength.

  9. Klicken Sie im Ordner SQLite Explorer mit der rechten Maustaste auf die Tabelle _EFMigrationsHistory, und wählen Sie Tabelle anzeigen aus. Die Tabelle enthält eine Liste aller Migrationen, die auf die Datenbank angewendet werden. Da Sie zwei Migrationsvorgänge ausgeführt haben, gibt es zwei Einträge: einen für die InitialCreate-Migration und einen zweiten für ModelRevisions.

Hinweis

In dieser Übung wurden Zuordnungsattribute (Datenanmerkungen) verwendet, um der Datenbank Modelle zuzuordnen. Alternativ zum Zuordnen von Attributen können Sie die Fluent-API „ModelBuilder“ verwenden, um Modelle zu konfigurieren. Beide Ansätze sind möglich, aber einige Entwickler*innen bevorzugen einen der beiden Ansätze.

Sie haben Migrationen verwendet, um ein Datenbankschema zu definieren und zu aktualisieren. In der nächsten Einheit stellen Sie die Methoden zur Datenbearbeitung in PizzaService fertig.

Überprüfen Sie Ihr Wissen

1.

Welche Namenskonvention für Eigenschaften gibt es in einer Entitätsklasse für einen Primärschlüssel?