Tutorial: Erste Schritte mit EF Core in einer ASP.NET Core MVC-Web-App

Von Tom Dykstra und Rick Anderson

In diesem Tutorial erfahren Sie die Grundlagen zu ASP.NET Core MVS und Entity Framework Core mit Controllern und Ansichten. Razor Pages ist ein alternatives Programmiermodell. Für neue Entwicklungsaufgaben wird empfohlen, Razor Pages mit MVC mit Controllern und Ansichten zu verwenden. Informationen finden Sie in der Razor Pages-Version dieses Tutorials. In jedem Tutorial werden einige Themen behandelt, die in den anderen Tutorials nicht behandelt werden:

Einige Aspekte, die in diesem MVC-Tutorial behandelt werden, nicht aber im Razor Pages-Tutorial:

  • Implementieren von Vererbung im Datenmodell
  • Durchführen unformatierter SQL-Abfragen
  • Verwenden dynamischer LINQs zum Vereinfachen des Codes

Einige Aspekte, die im Razor Pages-Tutorial behandelt werden, nicht aber in diesem Tutorial:

  • Verwenden der Select-Methode zum Laden zugehöriger Daten
  • Best Practices für EF

Anhand der Beispiel-Web-App Contoso University wird veranschaulicht, wie Sie mit Entity Framework (EF) Core und Visual Studio eine MVC-Web-App in ASP.NET Core erstellen.

Bei der Beispiel-App handelt es sich um eine Website für die fiktive Contoso University. Sie enthält Funktionen wie die Zulassung von Studenten, die Erstellung von Kursen und Aufgaben von Dozenten. Dies ist das erste von mehreren Tutorials, in denen das Erstellen der Beispiel-App „Contoso University“ erläutert wird.

Voraussetzungen

  • Wenn Sie noch nicht mit MVC in ASP.NET Core vertraut sind, arbeiten Sie zunächst die Tutorialreihe Erste Schritte mit ASP.NET Core MVC durch, bevor Sie mit diesem Tutorial beginnen.

Dieses Tutorial wurde nicht für ASP.NET Core 6 oder höher aktualisiert. Die Anweisungen des Tutorials funktionieren nicht ordnungsgemäß, wenn Sie ein Projekt erstellen, das auf ASP.NET Core 6 oder höher ausgerichtet ist. Die Webvorlagen für ASP.NET Core 6 und höher verwenden z. B. das minimale Hostingmodell, in dem Startup.cs und Program.cs in einer einzelnen Program.cs-Datei zusammengeführt werden.

Ein weiterer Unterschied in .NET 6 ist das NRT-Feature (Nullwerte zulassende Verweistypen). Die Projektvorlagen aktivieren dieses Feature standardmäßig. Probleme können auftreten, wenn EF eine Eigenschaft als erforderlich in .NET 6 betrachtet, die in .NET 5 Nullwerte zulässt. Beispielsweise tritt auf der Seite zum Erstellen von Kursteilnehmern ein Fehler auf, wenn die Eigenschaft Enrollments so festgelegt wird, dass sie Nullwerte zulässt, oder das Hilfstag asp-validation-summary nicht von ModelOnly auf All geändert wird.

Es wird empfohlen, das .NET 5 SDK für dieses Tutorial zu installieren und zu verwenden. Bis dieses Tutorial aktualisiert wird, lesen Sie Razor Pages mit Entity Framework Core in ASP.NET Core – Tutorial 1 von 8 zur Verwendung von Entity Framework mit ASP.NET Core 6 oder höher.

Datenbank-Engines

Die Visual Studio-Anweisungen verwenden SQL Server LocalDB, eine Version von SQL Server Express, die nur unter Windows ausgeführt werden kann.

Lösen und Behandeln von Problemen

Wenn Sie auf ein Problem stoßen, das Sie nicht lösen können, sollten Sie versuchen, Ihren Code mit dem abgeschlossenen Projekt zu vergleichen. Eine Liste mit häufig auftretenden Fehlern und den jeweiligen Lösungen finden Sie im Abschnitt zur Fehlerbehebung auf im letzten Tutorial dieser Tutorialreihe. Wenn Sie dort nicht die gewünschten Informationen finden, können Sie unter „StackOverflow.com“ für ASP.NET Core oder EF Core eine Frage posten.

Tipp

Diese Reihe besteht aus 10 Tutorials, die aufeinander aufbauen. Sie sollten jedes Mal, wenn Sie erfolgreich ein Tutorial abgeschlossen haben, eine Kopie des Projekts erstellen. Wenn Sie dann auf Probleme stoßen, können Sie zurück zum vorherigen Tutorial wechseln und müssen nicht wieder ganz von vorne beginnen.

Web-App der Contoso University

Bei der App, die mithilfe dieser Tutorials erstellt werden soll, handelt es sich um eine einfache Website einer Universität.

Benutzer können Informationen zu den Studenten, Kursen und Dozenten abrufen. Im Folgenden finden Sie einige Screenshots der App:

Indexseite „Studenten“

Bearbeitungsseite für Studenten

Erstellen einer Web-App

  1. Starten Sie Visual Studio, und wählen Sie Neues Projekt erstellen aus.
  2. Wählen Sie im Dialogfeld Neues Projekt erstellenASP.NET Core-Webanwendung>Weiter aus.
  3. Geben Sie ContosoUniversity im Dialogfeld Neues Projekt konfigurieren für Projektname ein. Es ist wichtig, genau diesen Namen unter Berücksichtigung der Groß-/Kleinschreibung zu verwenden, sodass beim Kopieren von Code jeder namespace übereinstimmt.
  4. Klicken Sie auf Erstellen.
  5. Wählen Sie im Dialogfeld Neue ASP.NET Core-Webanwendung erstellen Folgendes aus:
    1. In den Dropdownmenüs .NET Core und ASP.NET Core 5.0.
    2. ASP.NET Core-Web-App (Model View Controller) .
    3. ErstellenDialogfeld: Neues ASP:NET Core-Projekt

Einrichten des Websitestils

Mit ein paar wenigen grundlegenden Änderungen können Sie das Menü, das Layout und die Homepage der Website anpassen.

Öffnen Sie Views/Shared/_Layout.cshtml, und nehmen Sie die folgenden Änderungen vor:

  • Ändern Sie jedes Vorkommen von ContosoUniversity in Contoso University. Diese Begriffskombination kommt dreimal vor.
  • Fügen Sie Menüeinträge für About (Informationen), Students (Studierende), Courses (Kurse), Instructors (Dozent*innen) und Departments (Abteilungen) hinzu, und löschen Sie den Menüeintrag Privacy (Privatsphäre).

Die vorangehenden Änderungen sind im folgenden Code hervorgehoben:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>@ViewData["Title"] - Contoso University</title>
    <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
    <link rel="stylesheet" href="~/css/site.css" />
</head>
<body>
    <header>
        <nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
            <div class="container">
                <a class="navbar-brand" asp-area="" asp-controller="Home" asp-action="Index">Contoso University</a>
                <button class="navbar-toggler" type="button" data-toggle="collapse" data-target=".navbar-collapse" aria-controls="navbarSupportedContent"
                        aria-expanded="false" aria-label="Toggle navigation">
                    <span class="navbar-toggler-icon"></span>
                </button>
                <div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
                    <ul class="navbar-nav flex-grow-1">
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Index">Home</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="About">About</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="Students" asp-action="Index">Students</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="Courses" asp-action="Index">Courses</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="Instructors" asp-action="Index">Instructors</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="Departments" asp-action="Index">Departments</a>
                        </li>
                    </ul>
                </div>
            </div>
        </nav>
    </header>
    <div class="container">
        <main role="main" class="pb-3">
            @RenderBody()
        </main>
    </div>

    <footer class="border-top footer text-muted">
        <div class="container">
            &copy; 2020 - Contoso University - <a asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
        </div>
    </footer>
    <script src="~/lib/jquery/dist/jquery.js"></script>
    <script src="~/lib/bootstrap/dist/js/bootstrap.bundle.js"></script>
    <script src="~/js/site.js" asp-append-version="true"></script>
    @await RenderSectionAsync("Scripts", required: false)
</body>
</html>

Ersetzen Sie in Views/Home/Index.cshtml den Inhalt der Datei durch folgendes Markup:

@{
    ViewData["Title"] = "Home Page";
}

<div class="jumbotron">
    <h1>Contoso University</h1>
</div>
<div class="row">
    <div class="col-md-4">
        <h2>Welcome to Contoso University</h2>
        <p>
            Contoso University is a sample application that
            demonstrates how to use Entity Framework Core in an
            ASP.NET Core MVC web application.
        </p>
    </div>
    <div class="col-md-4">
        <h2>Build it from scratch</h2>
        <p>You can build the application by following the steps in a series of tutorials.</p>
        <p><a class="btn btn-default" href="https://docs.asp.net/en/latest/data/ef-mvc/intro.html">See the tutorial &raquo;</a></p>
    </div>
    <div class="col-md-4">
        <h2>Download it</h2>
        <p>You can download the completed project from GitHub.</p>
        <p><a class="btn btn-default" href="https://github.com/dotnet/AspNetCore.Docs/tree/main/aspnetcore/data/ef-mvc/intro/samples/5cu-final">See project source code &raquo;</a></p>
    </div>
</div>

Drücken Sie STRG+F5, um das Projekt auszuführen, oder wählen Sie aus dem Menü Debuggen > Ohne Debuggen starten aus. Im Folgenden wird die Homepage mit den Registerkarten für die Seiten angezeigt, die in diesem Tutorial erstellt wurden.

Contoso University-Startseite

EF Core NuGet-Pakete

In diesem Tutorial wird SQL Server verwendet, und das Anbieterpaket lautet Microsoft.EntityFrameworkCore.SqlServer.

Das EF-SQL Server-Paket und dessen Abhängigkeiten (Microsoft.EntityFrameworkCore und Microsoft.EntityFrameworkCore.Relational) stellen Runtimeunterstützung für EF bereit.

Fügen Sie das NuGet-Paket Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore hinzu. Geben Sie die folgenden Befehle in die Paket-Manager-Konsole (PMC) ein, um die NuGet-Pakete hinzuzufügen:

Install-Package Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
Install-Package Microsoft.EntityFrameworkCore.SqlServer

Das Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore NuGet-Paket stellt ASP.NET Core-Middleware für EF Core-Fehlerseiten bereit. Diese Middleware hilft bei der Erkennung und Diagnose von Fehlern bei EF Core-Migrationen.

Informationen zu anderen Datenbankanbietern, die für EF Core verfügbar sind, finden Sie unter Datenbankanbieter.

Erstellen des Datenmodells

Die folgenden Entitätsklassen werden für diese App erstellt:

Datenmodelldiagramm zur Kursanmeldung für Studenten

Die vorangehenden Entitäten weisen die folgenden Beziehungen auf:

  • Eine 1:n-Beziehung zwischen der Student- und der Enrollment-Entität: Ein Student kann für eine beliebige Anzahl von Kursen angemeldet werden.
  • Eine 1:n-Beziehung zwischen der Course- und der Enrollment-Entität: Für jeden Kurs kann sich eine beliebige Anzahl von Studenten anmelden.

In den folgenden Abschnitten wird für jede dieser Entitäten eine Klasse erstellt.

Die Entität „Student“

Entitätsdiagramm „Student“

Erstellen Sie mit dem folgenden Code im Ordner Models (Modelle) die Student-Klasse:

using System;
using System.Collections.Generic;

namespace ContosoUniversity.Models
{
    public class Student
    {
        public int ID { get; set; }
        public string LastName { get; set; }
        public string FirstMidName { get; set; }
        public DateTime EnrollmentDate { get; set; }

        public ICollection<Enrollment> Enrollments { get; set; }
    }
}

Die ID-Eigenschaft ist die Primärschlüsselspalte (PS) der Datenbanktabelle, die dieser Klasse entspricht. Standardmäßig interpretiert EF Core eine Eigenschaft mit dem Namen ID oder classnameID als Primärschlüssel. Beispielsweise kann der PS StudentID anstatt IDbenannt werden.

Die Enrollments-Eigenschaft ist eine Navigationseigenschaft. Navigationseigenschaften enthalten andere Entitäten, die dieser Entität zugehörig sind. Die Enrollments-Eigenschaft einer Student-Entität...:

  • ...enthält alle Enrollment-Entitäten, die mit dieser Student-Entität verknüpft sind.
  • Wenn eine bestimmte Student-Zeile in der Datenbank über zwei verknüpfte Enrollment-Zeilen verfügt,...:
    • ...enthält die Enrollments-Navigationseigenschaft dieser Student-Entität diese beiden Enrollment-Entitäten.

Enrollment-Zeilen enthalten den PS-Wert eines Studierenden in der StudentID-Fremdschlüsselspalte (FS).

Folgendes gilt, wenn eine Navigationseigenschaft mehrere Entitäten enthalten kann:

  • Der Typ muss eine Liste sein, z. B. ICollection<T>, List<T>oder HashSet<T>.
  • Entitäten können hinzugefügt, gelöscht und aktualisiert werden.

M:n- und 1:n-Navigationsbeziehungen können mehrere Entitäten enthalten. Wenn ICollection<T> verwendet wird, erstellt EF standardmäßig eine HashSet<T>-Sammlung.

Die Entität „Enrollment“

Entitätsdiagramm „Enrollment“

Erstellen Sie mit dem folgenden Code im Ordner Models (Modelle) die Enrollment-Klasse:

namespace ContosoUniversity.Models
{
    public enum Grade
    {
        A, B, C, D, F
    }

    public class Enrollment
    {
        public int EnrollmentID { get; set; }
        public int CourseID { get; set; }
        public int StudentID { get; set; }
        public Grade? Grade { get; set; }

        public Course Course { get; set; }
        public Student Student { get; set; }
    }
}

Die EnrollmentID-Eigenschaft ist der PS. Diese Entität verwendet selbst das classnameID-Muster anstelle von ID. Die Student-Entität hat das ID-Muster verwendet. Einige Entwickler bevorzugen die Verwendung eines Musters im gesamten Datenmodell. In diesem Tutorial wird durch die Variation veranschaulicht, dass jedes Muster verwendet werden kann. In einem späteren Tutorial wird erläutert, wie Sie durch die Verwendung von ID ohne Klassennamen die Vererbung einfacher in das Datenmodell implementieren.

Bei der Grade-Eigenschaft handelt es sich um eine enum. Das ? nach der Grade-Typdeklaration gibt an, dass die Grade-Eigenschaft NULL-Werte zulässt. Eine Note mit dem Wert null unterscheidet sich von einer Note mit dem Wert 0. null bedeutet, dass eine Note noch nicht bekannt ist oder noch nicht zugewiesen wurde.

Bei der StudentID-Eigenschaft handelt es sich um einen Fremdschlüssel (FS), und Student ist die entsprechende Navigationseigenschaft. Die Enrollment-Entität wird einer Student-Entität zugewiesen. Das bedeutet, dass die Eigenschaft nur eine Student-Entität enthalten kann. Dies unterscheidet sich von der Student.Enrollments-Navigationseigenschaft, die mehrere Enrollment-Entitäten enthalten kann.

Bei der CourseID-Eigenschaft handelt es sich um einen FS, und Course ist die entsprechende Navigationseigenschaft. Die Enrollment-Entität wird einer Course-Entität zugeordnet.

Entity Framework interpretiert eine Eigenschaft als eine FS-Eigenschaft, wenn Sie nach dem Schema <Name der Navigationseigenschaft><Name der Primärschlüsseleigenschaft> benannt ist. Ein Beispiel hierfür wäre StudentID für die Student-Navigationseigenschaft, da ID der PS der Student-Entität ist. FS-Eigenschaften können auch nach dem <Namen der Primärschlüsseleigenschaft> benannt werden. Ein Beispiel hierfür wäre CourseID, da CourseID der PS der Course-Entität ist.

Die Entität „Course“

Entitätsdiagramm „Course“

Erstellen Sie mit dem folgenden Code im Ordner Models (Modelle) die Course-Klasse:

using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;

namespace ContosoUniversity.Models
{
    public class Course
    {
        [DatabaseGenerated(DatabaseGeneratedOption.None)]
        public int CourseID { get; set; }
        public string Title { get; set; }
        public int Credits { get; set; }

        public ICollection<Enrollment> Enrollments { get; set; }
    }
}

Die Enrollments-Eigenschaft ist eine Navigationseigenschaft. Course-Entitäten können sich auf jede beliebige Anzahl von Enrollment-Entitäten beziehen.

Das DatabaseGenerated-Attribut wird in einem späteren Tutorial erläutert. Über dieses Attribut können Sie den PS für den Kurs angeben, anstatt ihn von der Datenbank generieren zu lassen.

Erstellen des Datenbankkontexts

Bei der Datenbankkontextklasse DbContext handelt es sich um die Hauptklasse, die die EF-Funktionen für ein angegebenes Datenmodell koordiniert. Diese Klasse wird durch Ableiten von der Microsoft.EntityFrameworkCore.DbContext-Klasse erstellt. Die abgeleitete Klasse DbContext gibt an, welche Entitäten im Datenmodell enthalten sind. Einige EF-Verhaltensweisen können angepasst werden. In diesem Projekt heißt die Klasse SchoolContext.

Erstellen Sie im Projektordner einen Ordner mit dem Namen Data (Daten).

Erstellen Sie im Ordner Data (Daten) mit dem folgenden Code eine SchoolContext-Klasse:

using ContosoUniversity.Models;
using Microsoft.EntityFrameworkCore;

namespace ContosoUniversity.Data
{
    public class SchoolContext : DbContext
    {
        public SchoolContext(DbContextOptions<SchoolContext> options) : base(options)
        {
        }

        public DbSet<Course> Courses { get; set; }
        public DbSet<Enrollment> Enrollments { get; set; }
        public DbSet<Student> Students { get; set; }
    }
}

Der vorangehende Code erstellt eine DbSet-Eigenschaft für jede Entitätenmenge. In EF-Terminologie:

  • Entitätenmengen entsprechen in der Regel einer Datenbanktabelle.
  • Entitäten entsprechen Zeilen in Tabellen.

Die Anweisungen DbSet<Enrollment> und DbSet<Course> können auch weggelassen werden, wodurch sich nichts an der Funktionsweise ändert. EF würde sie aus folgenden Gründen implizit einschließen:

  • Die Student-Entität verweist auf die Enrollment-Entität.
  • Die Enrollment-Entität verweist auf die Course-Entität.

Wenn die Datenbank erstellt wird, erstellt EF Core Tabellen mit Namen, die den DbSet-Eigenschaftennamen entsprechen. Eigenschaftennamen für Sammlungen stehen in der Regel im Plural. Beispielsweise wird Students statt Student verwendet. Entwickler sind sich uneinig darüber, ob Tabellennamen im Plural stehen sollten oder nicht. In diesen Tutorials wird das Standardverhalten außer Kraft gesetzt, indem Tabellennamen im DbContext im Singular angegeben werden. Fügen Sie dafür den hervorgehobenen Code nach der DbSet-Eigenschaft ein.

using ContosoUniversity.Models;
using Microsoft.EntityFrameworkCore;

namespace ContosoUniversity.Data
{
    public class SchoolContext : DbContext
    {
        public SchoolContext(DbContextOptions<SchoolContext> options) : base(options)
        {
        }

        public DbSet<Course> Courses { get; set; }
        public DbSet<Enrollment> Enrollments { get; set; }
        public DbSet<Student> Students { get; set; }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Course>().ToTable("Course");
            modelBuilder.Entity<Enrollment>().ToTable("Enrollment");
            modelBuilder.Entity<Student>().ToTable("Student");
        }
    }
}

Registrieren von SchoolContext

Dependency Injection ist in ASP.NET Core enthalten. Dienste wie der EF-Datenbankkontext werden per Dependency Injection beim App-Start registriert. Für Komponenten, die diese Dienste benötigen (z. B. MVC-Controller), werden sie über Konstruktorparameter bereitgestellt. Der Code des Controllerkonstruktors, der eine Kontextinstanz abruft, wird später in diesem Tutorial gezeigt.

Öffnen Sie Startup.cs, und fügen Sie der ConfigureServices-Methode die hervorgehobenen Zeilen hinzu, um SchoolContext als Dienst zu registrieren.

using ContosoUniversity.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace ContosoUniversity
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddDbContext<SchoolContext>(options =>
                options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

            services.AddControllersWithViews();
        }

Der Name der Verbindungszeichenfolge wird an den Kontext übergeben, indem Sie eine Methode auf einem DbContextOptionsBuilder-Objekt aufrufen. Für die lokale Entwicklung liest das ASP.NET Core-Konfigurationssystem die Verbindungszeichenfolge aus der appsettings.json-Datei.

Öffnen Sie die appsettings.json -Datei, und fügen Sie wie im folgenden Markup dargestellt eine Verbindungszeichenfolge hinzu:

{
  "ConnectionStrings": {
    "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=ContosoUniversity1;Trusted_Connection=True;MultipleActiveResultSets=true"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}

Hinzufügen des Filters für die Datenbankausnahme

Fügen Sie AddDatabaseDeveloperPageExceptionFilter in ConfigureServices hinzu, wie im folgenden Code dargestellt:

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<SchoolContext>(options =>
        options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

    services.AddDatabaseDeveloperPageExceptionFilter();

    services.AddControllersWithViews();
}

AddDatabaseDeveloperPageExceptionFilter bietet hilfreiche Fehlerinformationen in der Entwicklungsumgebung.

SQL Server Express LocalDB

Die Verbindungszeichenfolge gibt SQL Server LocalDB an. LocalDB ist eine Basisversion der SQL Server Express-Datenbank-Engine, die zwar für die Anwendungsentwicklung, aber nicht für den Produktionseinsatz bestimmt ist. LocalDB wird bedarfsgesteuert gestartet und im Benutzermodus ausgeführt, sodass keine komplexe Konfiguration anfällt. Standardmäßig erstellt LocalDB .mdf-Datenbankdateien im C:/Users/<user>-Verzeichnis.

Initialisieren Sie die Datenbank mit Testdaten

EF erstellt eine leere Datenbank. In diesem Abschnitt wird eine Methode hinzugefügt, die aufgerufen wird, nachdem die Datenbank erstellt wurde, um diese mit Testdaten zu befüllen.

Mithilfe der EnsureCreated-Methode wird die Datenbank automatisch erstellt. In einem späteren Tutorial wird dargestellt, wie Sie mit Änderungen an dem Modell umgehen können, indem Sie Code First-Migrationen verwenden, um das Datenbankschema zu verwenden, anstatt die Datenbank zu verwerfen und neu zu erstellen.

Erstellen Sie im Ordner Data (Daten) mit dem folgenden Code eine neue Klasse mit dem Namen DbInitializer:

using ContosoUniversity.Models;
using System;
using System.Linq;

namespace ContosoUniversity.Data
{
    public static class DbInitializer
    {
        public static void Initialize(SchoolContext context)
        {
            context.Database.EnsureCreated();

            // Look for any students.
            if (context.Students.Any())
            {
                return;   // DB has been seeded
            }

            var students = new Student[]
            {
            new Student{FirstMidName="Carson",LastName="Alexander",EnrollmentDate=DateTime.Parse("2005-09-01")},
            new Student{FirstMidName="Meredith",LastName="Alonso",EnrollmentDate=DateTime.Parse("2002-09-01")},
            new Student{FirstMidName="Arturo",LastName="Anand",EnrollmentDate=DateTime.Parse("2003-09-01")},
            new Student{FirstMidName="Gytis",LastName="Barzdukas",EnrollmentDate=DateTime.Parse("2002-09-01")},
            new Student{FirstMidName="Yan",LastName="Li",EnrollmentDate=DateTime.Parse("2002-09-01")},
            new Student{FirstMidName="Peggy",LastName="Justice",EnrollmentDate=DateTime.Parse("2001-09-01")},
            new Student{FirstMidName="Laura",LastName="Norman",EnrollmentDate=DateTime.Parse("2003-09-01")},
            new Student{FirstMidName="Nino",LastName="Olivetto",EnrollmentDate=DateTime.Parse("2005-09-01")}
            };
            foreach (Student s in students)
            {
                context.Students.Add(s);
            }
            context.SaveChanges();

            var courses = new Course[]
            {
            new Course{CourseID=1050,Title="Chemistry",Credits=3},
            new Course{CourseID=4022,Title="Microeconomics",Credits=3},
            new Course{CourseID=4041,Title="Macroeconomics",Credits=3},
            new Course{CourseID=1045,Title="Calculus",Credits=4},
            new Course{CourseID=3141,Title="Trigonometry",Credits=4},
            new Course{CourseID=2021,Title="Composition",Credits=3},
            new Course{CourseID=2042,Title="Literature",Credits=4}
            };
            foreach (Course c in courses)
            {
                context.Courses.Add(c);
            }
            context.SaveChanges();

            var enrollments = new Enrollment[]
            {
            new Enrollment{StudentID=1,CourseID=1050,Grade=Grade.A},
            new Enrollment{StudentID=1,CourseID=4022,Grade=Grade.C},
            new Enrollment{StudentID=1,CourseID=4041,Grade=Grade.B},
            new Enrollment{StudentID=2,CourseID=1045,Grade=Grade.B},
            new Enrollment{StudentID=2,CourseID=3141,Grade=Grade.F},
            new Enrollment{StudentID=2,CourseID=2021,Grade=Grade.F},
            new Enrollment{StudentID=3,CourseID=1050},
            new Enrollment{StudentID=4,CourseID=1050},
            new Enrollment{StudentID=4,CourseID=4022,Grade=Grade.F},
            new Enrollment{StudentID=5,CourseID=4041,Grade=Grade.C},
            new Enrollment{StudentID=6,CourseID=1045},
            new Enrollment{StudentID=7,CourseID=3141,Grade=Grade.A},
            };
            foreach (Enrollment e in enrollments)
            {
                context.Enrollments.Add(e);
            }
            context.SaveChanges();
        }
    }
}

Der vorangehende Code überprüft, ob die Datenbank vorhanden ist:

  • Wenn die Datenbank nicht gefunden wird, wird Folgendes durchgeführt:
    • Sie wird erstellt und mit Testdaten geladen. Testdaten werden in Arrays anstelle von List<T>-Auflistungen geladen, um die Leistung zu optimieren.
  • Wenn die Datenbank gefunden wird, wird keine Aktion ausgeführt.

Aktualisieren Sie Program.cs mit folgendem Code:

using ContosoUniversity.Data;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;

namespace ContosoUniversity
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var host = CreateHostBuilder(args).Build();

            CreateDbIfNotExists(host);

            host.Run();
        }

        private static void CreateDbIfNotExists(IHost host)
        {
            using (var scope = host.Services.CreateScope())
            {
                var services = scope.ServiceProvider;
                try
                {
                    var context = services.GetRequiredService<SchoolContext>();
                    DbInitializer.Initialize(context);
                }
                catch (Exception ex)
                {
                    var logger = services.GetRequiredService<ILogger<Program>>();
                    logger.LogError(ex, "An error occurred creating the DB.");
                }
            }
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });
    }
}

Program.cs führt beim Starten der App folgende Schritte durch:

  • Rufen Sie eine Datenbankkontextinstanz aus dem Dependency Injection-Container ab.
  • Rufen Sie die DbInitializer.Initialize-Methode auf.
  • Löschen des Kontexts bei Abschluss der Initialize-Methode, wie im folgenden Code dargestellt:
public static void Main(string[] args)
{
     var host = CreateWebHostBuilder(args).Build();

    using (var scope = host.Services.CreateScope())
    {
        var services = scope.ServiceProvider;
        try
        {
            var context = services.GetRequiredService<SchoolContext>();
            DbInitializer.Initialize(context);
        }
        catch (Exception ex)
        {
            var logger = services.GetRequiredService<ILogger<Program>>();
            logger.LogError(ex, "An error occurred while seeding the database.");
        }
    }

    host.Run();
}

Wenn die App zum ersten Mal ausgeführt wird, wird die Datenbank erstellt und mit Testdaten geladen. Führen Sie bei jeder Datenmodelländerung folgende Schritte durch:

  • Löschen der Datenbank.
  • Aktualisieren Sie die Seed-Methode, und beginnen Sie mit einer neuen Datenbank von vorne.

In späteren Tutorials wird die Datenbank angepasst, wenn das Datenmodell geändert wird, ohne sie zu löschen und neu zu erstellen. Wenn das Datenmodell geändert wird, gehen keine Daten verloren.

Erstellen Sie Controller und Ansichten

Verwenden Sie die Gerüstbau-Engine in Visual Studio, um einen MVC-Controller und Ansichten hinzuzufügen, die EF zum Abfragen und Speichern von Daten verwenden.

Die automatische Erstellung von CRUD-Aktionsmethoden und Ansichten wird als Gerüstbau bezeichnet.

  • Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf den Ordner Controllers, und wählen Sie Hinzufügen > Neues Gerüstelement aus.
  • Im Dialogfeld Gerüst hinzufügen:
    • Wählen Sie MVC-Controller mit Ansichten unter Verwendung von Entity Framework aus.
    • Klicken Sie auf Hinzufügen. Das Dialogfeld Add MVC Controller with views, using Entity Framework (MVC-Controller mit Ansichten mithilfe von Entity Framework hinzufügen) wird angezeigt:Das Gerüst „Studierende*r“
    • Wählen Sie unter ModellklasseStudent aus.
    • Wählen Sie unter DatenkontextklasseSchoolContext aus.
    • Akzeptieren Sie den Standardnamen StudentsController.
    • Klicken Sie auf Hinzufügen.

Die Gerüstbau-Engine von Visual Studio erstellt eine StudentsController.cs-Datei und mehrere Ansichten (*.cshtml-Dateien), die mit dem Controller zusammenarbeiten.

Beachten Sie, dass der Controller SchoolContext als Konstruktorparameter verwendet.

namespace ContosoUniversity.Controllers
{
    public class StudentsController : Controller
    {
        private readonly SchoolContext _context;

        public StudentsController(SchoolContext context)
        {
            _context = context;
        }

Über die Abhängigkeitsinjektion in ASP.NET Core wird eine Instanz von SchoolContext an den Controller übergeben. Dies haben Sie in der Startup-Klasse konfiguriert.

Der Controller enthält eine Index-Aktionsmethode, über die alle Studenten in der Datenbank angezeigt werden. Die Methode ruft eine Listen von Studenten aus der Entitätenmenge „Student“ ab, indem sie die Students-Eigenschaft aus der Datenbankkontextinstanz liest:

public async Task<IActionResult> Index()
{
    return View(await _context.Students.ToListAsync());
}

Die Elemente der asynchronen Programmierung in diesem Code werden später im Tutorial erläutert.

In der Views/Students/Index.cshtml-Ansicht wird diese Liste in einer Tabelle dargestellt:

@model IEnumerable<ContosoUniversity.Models.Student>

@{
    ViewData["Title"] = "Index";
}

<h2>Index</h2>

<p>
    <a asp-action="Create">Create New</a>
</p>
<table class="table">
    <thead>
        <tr>
                <th>
                    @Html.DisplayNameFor(model => model.LastName)
                </th>
                <th>
                    @Html.DisplayNameFor(model => model.FirstMidName)
                </th>
                <th>
                    @Html.DisplayNameFor(model => model.EnrollmentDate)
                </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
@foreach (var item in Model) {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.LastName)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.FirstMidName)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.EnrollmentDate)
            </td>
            <td>
                <a asp-action="Edit" asp-route-id="@item.ID">Edit</a> |
                <a asp-action="Details" asp-route-id="@item.ID">Details</a> |
                <a asp-action="Delete" asp-route-id="@item.ID">Delete</a>
            </td>
        </tr>
}
    </tbody>
</table>

Drücken Sie STRG+F5, um das Projekt auszuführen, oder wählen Sie aus dem Menü Debuggen > Ohne Debuggen starten aus.

Klicken Sie auf die Registerkarte „Students“, um die Testdaten abzurufen, die über die DbInitializer.Initialize-Methode eingefügt wurden. Je nachdem, wie klein Ihr Browserfenster dargestellt wird, sehen Sie einen Students-Registerkartenlink unten auf der Seite, oder Sie müssen auf das Navigationssymbol in der oberen rechten Ecke klicken, damit der Link angezeigt wird.

Contoso University-Startseite schmal

Indexseite „Studenten“

Zeigen Sie die Datenbank an

Wenn die App gestartet wird, ruft die DbInitializer.Initialize-Methode EnsureCreated auf. EF hat festgestellt, dass keine Datenbank vorhanden war:

  • Daher wurde eine Datenbank erstellt.
  • Der Code der Initialize-Methode hat die Datenbank mit Daten befüllt.

Verwenden Sie den SQL Server-Objekt-Explorer (SSOX), um die Datenbank in Visual Studio anzuzeigen:

  • Wählen Sie über das Menü Ansicht in Visual Studio SQL Server-Objekt-Explorer aus.
  • Klicken Sie im SSOX auf (localdb)\MSSQLLocalDB > Datenbanken.
  • Wählen Sie ContosoUniversity1 aus, den Eintrag für den Datenbanknamen in der Verbindungszeichenfolge in der appsettings.json -Datei.
  • Erweitern Sie den Knoten Tabellen, um die Tabellen in der Datenbank anzuzeigen.

Tabellen im SSOX

Klicken Sie mit der rechten Maustaste auf die Tabelle Student, und klicken Sie auf Daten anzeigen, um die Daten in der Tabelle anzuzeigen.

Tabelle „Student“ im SSOX

Die Datenbankdateien *.mdf und *.ldf befinden sich im Ordner C:\Benutzer\<Benutzername>.

Da EnsureCreated in der Initialisierermethode aufgerufen wird, die beim App-Start ausgeführt wird, können Sie folgende Aktionen ausführen:

  • Sie können Änderungen an der Student-Klasse vornehmen.
  • Löschen der Datenbank.
  • Sie können die App anhalten und dann starten. Die Datenbank wird automatisch neu erstellt, um die Änderung zu berücksichtigen.

Wenn der Student-Klasse beispielsweise eine EmailAddress-Eigenschaft hinzugefügt wurde, wird in der neu erstellten Tabelle eine EmailAddress-Spalte angezeigt. Die Ansicht zeigt die neue EmailAddress-Eigenschaft nicht an.

Konventionen

Da die Konventionen von EF verwendet werden, ist nicht viel geschriebener Code nötig, damit EF eine vollständige Datenbank erstellt:

  • Die Namen der DbSet-Eigenschaften werden als Tabellennamen verwendet. Für Entitäten, auf die nicht über eine DbSet-Eigenschaft verwiesen wird, werden Entitätsklassennamen als Tabellennamen verwendet.
  • Eigenschaftennamen von Entitäten werden als Spaltennamen verwendet.
  • Entitätseigenschaften mit den Namen ID oder classnameID werden als PS-Eigenschaften erkannt.
  • Eine Eigenschaft wird als FS-Eigenschaft interpretiert, wenn Sie nach dem Schema <Name der Navigationseigenschaft><Name der Primärschlüsseleigenschaft> benannt ist. Ein Beispiel hierfür wäre StudentID für die Student-Navigationseigenschaft, da ID der PS der Student-Entität ist. FS-Eigenschaften können auch nach dem <Namen der Primärschlüsseleigenschaft> benannt werden. Ein Beispiel hierfür wäre EnrollmentID, da EnrollmentID der PS der Enrollment-Entität ist.

Konventionelles Verhalten kann überschrieben werden. Beispielsweise können Tabellennamen wie in diesem Tutorial bereits gezeigt explizit angegeben werden. Spaltennamen und eine beliebige Eigenschaft können als PS oder FS festgelegt werden.

Asynchroner Code

Die asynchrone Programmierung ist der Standardmodus für ASP.NET Core und EF Core.

Der Webserver verfügt nur über eine begrenzte Anzahl von Threads. Daher werden bei hoher Auslastung möglicherweise alle verfügbaren Threads gleichzeitig verwendet. Wenn dies der Fall ist, kann der Server keine neuen Anforderungen verarbeiten, bis die Threads wieder freigegeben werden. Wenn synchroner Code verwendet wird, kann es sein, dass zwar viele Threads belegt sind, diese aber keine Vorgänge ausführen, da sie auf den Abschluss der E/A-Vorgänge warten. Wenn asynchroner Code verwendet wird, werden Threads für den Server freigegeben, wenn diese nur auf den Abschluss der E/A-Vorgänge warten, damit andere Anforderungen verarbeitet werden können. Das bedeutet, dass es durch asynchronen Code ermöglicht wird, Serverressourcen effizienter zu nutzen, und der Server kann ohne Verzögerungen eine größere Menge von Datenverkehr verarbeiten.

Durch die Verwendung von asynchronem Code entsteht ein geringes Maß an Mehraufwand zur Laufzeit. In Situationen, in denen nur wenig Datenverkehr verarbeitet werden muss, haben diese Leistungseinbußen keine negativen Folgen. Wenn es jedoch eine große Menge an Datenverkehr gibt, ist eine potentielle Verbesserung der Leistung von Bedeutung.

Im folgenden Code führen async, Task<T>, await und ToListAsync dazu, dass der Code asynchron ausgeführt wird.

public async Task<IActionResult> Index()
{
    return View(await _context.Students.ToListAsync());
}
  • Das async-Schlüsselwort teilt dem Compiler mit, dass er für einzelne Teile des Methodentexts Rückrufe generieren und das zurückgegebene Task<IActionResult>-Objekt automatisch erstellen soll.
  • Der Rückgabetyp Task<IActionResult> stellt derzeit ausgeführte Arbeiten mit dem Ergebnis von Typ IActionResult dar.
  • Das await-Schlüsselwort hat zur Folge, dass der Compiler die Methode in zwei Teile unterteilt. Der erste Teil endet mit dem Vorgang, der auf asynchrone Weise gestartet wird. Der zweite Teil wird in eine Rückrufmethode übertragen, die aufgerufen wird, wenn der Vorgang abgeschlossen wird.
  • Bei ToListAsync handelt es sich um die asynchrone Version der ToList-Erweiterungsmethode.

Denken Sie an Folgendes, wenn Sie asynchronen Code schreiben, in dem EF verwendet wird:

  • Es werden nur Anweisungen auf asynchrone Weise ausgeführt, die Abfragen oder Befehle auslösen, die an die Datenbank gesendet werden sollen. Das schließt beispielsweise ToListAsync, SingleOrDefaultAsync und SaveChangesAsync ein. Anweisungen wie var students = context.Students.Where(s => s.LastName == "Davolio"), die nur eine IQueryable-Instanz ändern, sind beispielsweise davon ausgeschlossen.
  • Entity Framework Core-Kontexte sind nicht threadsicher. Versuchen Sie daher nicht, mehrere Vorgänge gleichzeitig auszuführen. Wenn Sie eine beliebige EF-Methode aufrufen, sollten Sie immer das await-Schlüsselwort verwenden.
  • Wenn Sie von den Leistungsvorteilen des asynchronen Codes profitieren möchten, vergewissern Sie sich, dass auch alle verwendeten Bibliothekspakete asynchronen Code verwenden, wenn sie EF-Methoden aufrufen, durch die Abfragen an die Datenbank gesendet werden.

Weitere Informationen zur asynchronen Programmierung in .NET finden Sie unter Async (Übersicht).

Begrenzen abgerufener Entitäten

Weitere Informationen zum Begrenzen der Anzahl von Entitäten, die infolge einer Abfrage zurückgegeben werden, finden Sie unter Überlegungen zur Leistung.

SQL Protokollierung von Entity Framework Core

Die Konfiguration der Protokollierung wird meistens im Abschnitt Logging der appsettings.{Environment}.json-Dateien angegeben. Um SQL Anweisungen zu protokollieren, fügen Sie "Microsoft.EntityFrameworkCore.Database.Command": "Information" der Datei appsettings.Development.json hinzu:

{
  "ConnectionStrings": {
    "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=MyDB-2;Trusted_Connection=True;MultipleActiveResultSets=true"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
     ,"Microsoft.EntityFrameworkCore.Database.Command": "Information"
    }
  },
  "AllowedHosts": "*"
}

Mit dem obigen JSON-Code werden SQL Anweisungen in der Befehlszeile und im Visual Studio Ausgabefenster angezeigt.

Weitere Informationen finden Sie im Artikel zum Protokollieren in .NET Core und ASP.NET Core und in diesem GitHub-Issue.

Fahren Sie mit dem nächsten Tutorial fort, um zu erfahren, wie Sie grundlegende CRUD-Vorgänge (Create, Read, Update, Delete = Erstellen, Lesen, Aktualisieren, Löschen) ausführen.

In diesem Tutorial erfahren Sie die Grundlagen zu ASP.NET Core MVS und Entity Framework Core mit Controllern und Ansichten. Razor Pages ist ein alternatives Programmiermodell. Für neue Entwicklungsaufgaben wird empfohlen, Razor Pages mit MVC mit Controllern und Ansichten zu verwenden. Informationen finden Sie in der Razor Pages-Version dieses Tutorials. In jedem Tutorial werden einige Themen behandelt, die in den anderen Tutorials nicht behandelt werden:

Einige Aspekte, die in diesem MVC-Tutorial behandelt werden, nicht aber im Razor Pages-Tutorial:

  • Implementieren von Vererbung im Datenmodell
  • Durchführen unformatierter SQL-Abfragen
  • Verwenden dynamischer LINQs zum Vereinfachen des Codes

Einige Aspekte, die im Razor Pages-Tutorial behandelt werden, nicht aber in diesem Tutorial:

  • Verwenden der Select-Methode zum Laden zugehöriger Daten
  • Best Practices für EF

Die Beispielwebanwendung der Contoso University veranschaulicht, wie mit Entity Framework (EF) Core 2.2 und Visual Studio 2019 ASP.NET Core MVC-Webanwendungen erstellt werden.

Dieses Tutorial wurde nicht für ASP.NET Core 3.1 aktualisiert. Es wurde für ASP.NET Core 5.0 aktualisiert.

Bei der Beispiel-App handelt es sich um eine Website für die fiktive Contoso University. Sie enthält Funktionen wie die Zulassung von Studenten, die Erstellung von Kursen und Aufgaben von Dozenten. Dies ist die erste Tutorial in der Reihe, in dem die Erstellung der Beispielanwendung der Contoso University von Grund auf erläutert wird.

Voraussetzungen

Problembehandlung

Wenn Sie auf ein Problem stoßen, das Sie nicht lösen können, sollten Sie versuchen, Ihren Code mit dem abgeschlossenen Projekt zu vergleichen. Eine Liste mit häufig auftretenden Fehlern und den jeweiligen Lösungen finden Sie im Abschnitt zur Fehlerbehebung auf im letzten Tutorial dieser Tutorialreihe. Wenn Sie dort nicht die gewünschten Informationen finden, können Sie unter „StackOverflow.com“ für ASP.NET Core oder EF Core eine Frage posten.

Tipp

Diese Reihe besteht aus 10 Tutorials, die aufeinander aufbauen. Sie sollten jedes Mal, wenn Sie erfolgreich ein Tutorial abgeschlossen haben, eine Kopie des Projekts erstellen. Wenn Sie dann auf Probleme stoßen, können Sie zurück zum vorherigen Tutorial wechseln und müssen nicht wieder ganz von vorne beginnen.

Web-App der Contoso University

Bei der Anwendung, die Sie mithilfe dieser Tutorials erstellen, handelt es sich um eine einfache Universitätswebsite.

Benutzer können Informationen zu den Studenten, Kursen und Dozenten abrufen. Nachfolgend werden einige Anzeigen dargestellt, die erstellt werden sollen.

Indexseite „Studenten“

Bearbeitungsseite für Studenten

Erstellen einer Web-App

  • Öffnen Sie Visual Studio.

  • Klicken Sie im Menü Datei auf Neu > Projekt.

  • Wählen Sie im linken Bereich Installiert > Visual C# > Web aus.

  • Wählen Sie die Projektvorlage ASP.NET Core-Webanwendung aus.

  • Geben Sie ContosoUniversity als Name ein, und klicken Sie auf OK.

    Dialogfeld

  • Warten Sie, bis das Dialogfeld Neue ASP.NET Core-Webanwendung angezeigt wird.

  • Wählen Sie .NET Core, ASP.NET Core 2.2 und die Vorlage Webanwendung (Model-View-Controller) aus.

  • Stellen Sie sicher, dass Authentifizierung auf Keine Authentifizierung festgelegt ist.

  • Wählen Sie OK aus.

    Dialogfeld „Neues ASP.NET Core-Projekt“

Einrichten des Websitestils

Sie können das Websitemenü, das Layout und die Startseite über einige Änderungen einrichten.

Öffnen Sie Views/Shared/_Layout.cshtml, und nehmen Sie die folgenden Änderungen vor:

  • Ändern Sie jedes „ContosoUniversity“ in „Contoso University“. Diese Begriffskombination kommt dreimal vor.

  • Fügen Sie Menüeinträge für About (Informationen), Students (Studierende), Courses (Kurse), Instructors (Dozent*innen) und Departments (Abteilungen) hinzu, und löschen Sie den Menüeintrag Privacy (Privatsphäre).

Die Änderungen werden hervorgehoben.

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>@ViewData["Title"] - Contoso University</title>

    <environment include="Development">
        <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
    </environment>
    <environment exclude="Development">
        <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.1.3/css/bootstrap.css"
              asp-fallback-href="~/lib/bootstrap/dist/css/bootstrap.css"
              asp-fallback-test-class="sr-only" asp-fallback-test-property="position" asp-fallback-test-value="absolute"
              crossorigin="anonymous"
              integrity="sha256-eSi1q2PG6J7g7ib17yAaWMcrr5GrtohYChqibrV7PBE="/>
    </environment>
    <link rel="stylesheet" href="~/css/site.css" />
</head>
<body>
    <header>
        <nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
            <div class="container">
                <a class="navbar-brand" asp-area="" asp-controller="Home" asp-action="Index">Contoso University</a>
                <button class="navbar-toggler" type="button" data-toggle="collapse" data-target=".navbar-collapse" aria-controls="navbarSupportedContent"
                        aria-expanded="false" aria-label="Toggle navigation">
                    <span class="navbar-toggler-icon"></span>
                </button>
                <div class="navbar-collapse collapse d-sm-inline-flex flex-sm-row-reverse">
                    <ul class="navbar-nav flex-grow-1">
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Index">Home</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="About">About</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="Students" asp-action="Index">Students</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="Courses" asp-action="Index">Courses</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="Instructors" asp-action="Index">Instructors</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="Departments" asp-action="Index">Departments</a>
                        </li>
                    </ul>
                </div>
            </div>
        </nav>
    </header>
    <div class="container">
        <partial name="_CookieConsentPartial" />
        <main role="main" class="pb-3">
            @RenderBody()
        </main>
    </div>

    <footer class="border-top footer text-muted">
        <div class="container">
            &copy; 2019 - Contoso University - <a asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
        </div>
    </footer>

    <environment include="Development">
        <script src="~/lib/jquery/dist/jquery.js"></script>
        <script src="~/lib/bootstrap/dist/js/bootstrap.bundle.js"></script>
    </environment>
    <environment exclude="Development">
        <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.js"
                asp-fallback-src="~/lib/jquery/dist/jquery.js"
                asp-fallback-test="window.jQuery"
                crossorigin="anonymous"
                integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=">
        </script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.1.3/js/bootstrap.bundle.js"
                asp-fallback-src="~/lib/bootstrap/dist/js/bootstrap.bundle.js"
                asp-fallback-test="window.jQuery && window.jQuery.fn && window.jQuery.fn.modal"
                crossorigin="anonymous"
                integrity="sha256-E/V4cWE4qvAeO5MOhjtGtqDzPndRO1LBk8lJ/PR7CA4=">
        </script>
    </environment>
    <script src="~/js/site.js" asp-append-version="true"></script>

    @RenderSection("Scripts", required: false)
</body>
</html>

Ersetzen Sie den Inhalt der Datei Views/Home/Index.cshtml durch den folgenden Code, um den Text über ASP.NET und MVC durch Text über diese Anwendung zu ersetzen:

@{
    ViewData["Title"] = "Home Page";
}

<div class="jumbotron">
    <h1>Contoso University</h1>
</div>
<div class="row">
    <div class="col-md-4">
        <h2>Welcome to Contoso University</h2>
        <p>
            Contoso University is a sample application that
            demonstrates how to use Entity Framework Core in an
            ASP.NET Core MVC web application.
        </p>
    </div>
    <div class="col-md-4">
        <h2>Build it from scratch</h2>
        <p>You can build the application by following the steps in a series of tutorials.</p>
        <p><a class="btn btn-default" href="https://docs.asp.net/en/latest/data/ef-mvc/intro.html">See the tutorial &raquo;</a></p>
    </div>
    <div class="col-md-4">
        <h2>Download it</h2>
        <p>You can download the completed project from GitHub.</p>
        <p><a class="btn btn-default" href="https://github.com/dotnet/AspNetCore.Docs/tree/main/aspnetcore/data/ef-mvc/intro/samples/cu-final">See project source code &raquo;</a></p>
    </div>
</div>

Drücken Sie STRG+F5, um das Projekt auszuführen, oder wählen Sie aus dem Menü Debuggen > Ohne Debuggen starten aus. Dann wird Ihnen die Startseite mit Registerkarten für die Seiten angezeigt, die Sie mithilfe dieses Tutorials erstellen.

Contoso University-Startseite

Informationen zu EF Core NuGet-Paketen

Installieren Sie den Datenbankanbieter, der verwendet werden soll, um einem Projekt EF Core-Unterstützung hinzuzufügen. In diesem Tutorial wird SQL Server verwendet, und das Anbieterpaket lautet Microsoft.EntityFrameworkCore.SqlServer. Dieses Paket ist im Microsoft.AspNetCore.App-Metapaket enthalten, weshalb Sie nicht auf das Paket verweisen müssen.

Das EF SQL Server-Paket und dessen Abhängigkeiten (Microsoft.EntityFrameworkCore und Microsoft.EntityFrameworkCore.Relational) stellen für EF Runtimeunterstützung bereit. Sie fügen später im Laufe des Migrations-Tutorials ein Paket mit Tools hinzu.

Informationen zu anderen Datenbankanbietern, die für Entity Framework Core verfügbar sind, finden Sie unter Datenbankanbieter.

Erstellen des Datenmodells

Als nächstes erstellen Sie Entitätsklassen für die Contoso University-Anwendung. Beginnen Sie mit dem folgenden drei Entitäten.

Datenmodelldiagramm zur Kursanmeldung für Studenten

Es besteht eine 1:n-Beziehung zwischen den Entitäten Student und Enrollment. Außerdem besteht eine 1:n-Beziehung zwischen den Entitäten Course und Enrollment. Das bedeutet, dass ein Student für beliebig viele Kurse angemeldet sein kann und sich für jeden Kurs eine beliebige Anzahl von Studenten anmelden kann.

In den folgenden Abschnitten erstellen Sie für jede dieser Entitäten eine Klasse.

Die Entität „Student“

Entitätsdiagramm „Student“

Erstellen Sie im Ordner Models (Modelle) die Klassendatei Student.cs, und ersetzen Sie den Vorlagencode durch folgenden Code.

using System;
using System.Collections.Generic;

namespace ContosoUniversity.Models
{
    public class Student
    {
        public int ID { get; set; }
        public string LastName { get; set; }
        public string FirstMidName { get; set; }
        public DateTime EnrollmentDate { get; set; }

        public ICollection<Enrollment> Enrollments { get; set; }
    }
}

Die ID-Eigenschaft fungiert als Primärschlüsselspalte der Datenbanktabelle, die dieser Klasse entspricht. Standardmäßig interpretiert Entity Framework Core eine Eigenschaft mit dem Namen ID oder classnameID als Primärschlüssel.

Die Enrollments-Eigenschaft ist eine Navigationseigenschaft. Navigationseigenschaften enthalten andere Entitäten, die dieser Entität zugehörig sind. In diesem Fall enthält die Enrollments-Eigenschaft einer Student entity all diese Enrollment-Entitäten, die mit der Student-Entität in Zusammenhang stehen. Das heißt: Wenn eine Student-Zeile in der Datenbank über zwei zugehörige Enrollment-Zeilen verfügt (Zeilen, in denen der Primärschlüsselwert dieses Studierenden in der StudentID-Fremdschlüsselspalte enthalten ist), enthält die Enrollments-Navigationseigenschaft dieser Student-Entität diese beiden Enrollment-Entitäten.

Wenn eine Navigationseigenschaft mehrere Entitäten enthalten kann (wie bei m:n- oder 1:n-Beziehungen), muss dessen Typ aus einer Liste bestehen, in der Einträge hinzugefügt, gelöscht und aktualisiert werden können – z.B.: ICollection<T>. Sie können die ICollection<T>-Instanz oder einen Typ wie List<T> oder HashSet<T> angeben. Wenn Sie ICollection<T> angeben, erstellt EF standardmäßig eine HashSet<T>-Auflistung.

Die Entität „Enrollment“

Entitätsdiagramm „Enrollment“

Erstellen Sie im Ordner Models (Modelle) die Datei Enrollment.cs, und ersetzen Sie den vorhandenen Code durch folgenden Code:

namespace ContosoUniversity.Models
{
    public enum Grade
    {
        A, B, C, D, F
    }

    public class Enrollment
    {
        public int EnrollmentID { get; set; }
        public int CourseID { get; set; }
        public int StudentID { get; set; }
        public Grade? Grade { get; set; }

        public Course Course { get; set; }
        public Student Student { get; set; }
    }
}

Die EnrollmentID-Eigenschaft wird als Primärschlüssel verwendet. Diese Entität verwendet das classnameID-Muster anstelle der ID alleine, wie in der Student-Entität dargestellt wurde. Normalerweise würden Sie nur ein Muster auswählen und dieses für das gesamte Datenmodell verwenden. Diese Variation soll verdeutlichen, dass Sie ein beliebiges Muster erstellen können. In einem der nächsten Tutorials wird erläutert, wie Sie eine ID ohne Klassennamen verwenden, um die Vererbung einfacher in das Datenmodell zu implementieren.

Die Grade-Eigenschaft ist enum. Das Fragezeichen nach der Grade-Typdeklaration gibt an, dass die Grade-Eigenschaft NULL-Werte zulässt. Eine Grade-Eigenschaft mit dem Wert NULL unterscheidet sich von einer Grade-Eigenschaft mit dem Wert 0 (null). Der Wert NULL bedeutet, dass keine Grade-Eigenschaft bekannt ist oder noch keine zugewiesen wurde.

Bei der StudentID-Eigenschaft handelt es sich um einen Fremdschlüssel, und Student ist die entsprechende Navigationseigenschaft. Eine Enrollment-Entität wird einer Student-Entität zugeordnet, damit die Eigenschaft nur eine Student-Entität enthalten kann. Dies steht im Gegensatz zu der bereits erläuterten Student.Enrollments-Navigationseigenschaft, die mehrere Enrollment-Entitäten enthalten kann.

Bei der CourseID-Eigenschaft handelt es sich um einen Fremdschlüssel, und die zugehörige Navigationseigenschaft lautet Course. Die Enrollment-Entität wird einer Course-Entität zugeordnet.

Entity Framework interpretiert Eigenschaften als Fremdschlüsseleigenschaften, wenn Sie den Namen <navigation property name><primary key property name> haben – z.B. StudentID für die Student-Navigationseigenschaft, da der Primärschlüssel der Student-Entität ID lautet. Fremdschlüsseleigenschaften können auch einfach den Namen <primary key property name> haben – z.B. CourseID, da der Primärschlüssel der Course-Entität CourseID lautet.

Die Entität „Course“

Entitätsdiagramm „Course“

Erstellen Sie im Ordner Models (Modelle) die Datei Course.cs, und ersetzen Sie den vorhandenen Code durch folgenden Code:

using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;

namespace ContosoUniversity.Models
{
    public class Course
    {
        [DatabaseGenerated(DatabaseGeneratedOption.None)]
        public int CourseID { get; set; }
        public string Title { get; set; }
        public int Credits { get; set; }

        public ICollection<Enrollment> Enrollments { get; set; }
    }
}

Die Enrollments-Eigenschaft ist eine Navigationseigenschaft. Course-Entitäten können sich auf jede beliebige Anzahl von Enrollment-Entitäten beziehen.

Weitere Informationen zum DatabaseGenerated-Attribut erhalten Sie in einem späteren Tutorial dieser Reihe. Im Grunde können Sie über dieses Attribut den Primärschlüssel für den Kurs angeben, anstatt ihn von der Datenbank generieren zu lassen.

Erstellen des Datenbankkontexts

Die Datenbankkontextklasse ist die Hauptklasse, die die Entity Framework-Funktionen für ein angegebenes Datenmodell koordiniert. Sie können diese Klasse durch Ableiten von der Microsoft.EntityFrameworkCore.DbContext-Klasse erstellen. Sie geben in Ihrem Code an, welche Entitäten im Datenmodell enthalten sind. Außerdem können Sie bestimmte Entity Framework-Verhalten anpassen. In diesem Projekt heißt die Klasse SchoolContext.

Erstellen Sie im Projektordner einen Ordner mit dem Namen Data (Daten).

Erstellen Sie im Ordner Data (Daten) die Klassendatei SchoolContext.cs, und ersetzen Sie den Vorlagencode durch folgenden Code:

using ContosoUniversity.Models;
using Microsoft.EntityFrameworkCore;

namespace ContosoUniversity.Data
{
    public class SchoolContext : DbContext
    {
        public SchoolContext(DbContextOptions<SchoolContext> options) : base(options)
        {
        }

        public DbSet<Course> Courses { get; set; }
        public DbSet<Enrollment> Enrollments { get; set; }
        public DbSet<Student> Students { get; set; }
    }
}

Dieser Code erstellt eine DbSet-Eigenschaft für jede Entitätenmenge. In der Terminologie von Entity Framework entspricht eine Entitätenmenge in der Regel einer Datenbanktabelle, und eine Entität entspricht einer Zeile in einer Tabelle.

Auch wenn Sie die Anweisungen DbSet<Enrollment> und DbSet<Course> auslassen, ändert dies nichts an der Funktionsweise. Diese sind implizit in Entity Framework enthalten, da die Student-Entität auf die Enrollment-Entität und die Enrollment-Entität auf die Course-Entität verweist.

Wenn die Datenbank erstellt wird, erstellt EF Core Tabellen mit Namen, die den DbSet-Eigenschaftennamen entsprechen. Eigenschaftennamen für Auflistungen stehen in der Regel im Plural (Students anstelle von Student). Allerdings sind sich Entwickler uneinig darüber, ob auch Tabellennamen im Plural stehen sollten. In diesen Tutorials wird das Standardverhalten außer Kraft gesetzt, indem im DbContext Tabellennamen im Singular angegeben werden. Fügen Sie dafür den hervorgehobenen Code nach der DbSet-Eigenschaft ein.

using ContosoUniversity.Models;
using Microsoft.EntityFrameworkCore;

namespace ContosoUniversity.Data
{
    public class SchoolContext : DbContext
    {
        public SchoolContext(DbContextOptions<SchoolContext> options) : base(options)
        {
        }

        public DbSet<Course> Courses { get; set; }
        public DbSet<Enrollment> Enrollments { get; set; }
        public DbSet<Student> Students { get; set; }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Course>().ToTable("Course");
            modelBuilder.Entity<Enrollment>().ToTable("Enrollment");
            modelBuilder.Entity<Student>().ToTable("Student");
        }
    }
}

Erstellen Sie das Projekt, um zu ermitteln, ob Compilerfehler vorliegen.

Registrieren Sie den SchoolContext

ASP.NET Core implementiert standardmäßig Dependency Injection. Dienste wie der EF-Datenbankkontext werden per Dependency Injection beim Anwendungsstart registriert. Komponenten, die diese Dienste erfordern (z.B. Ihre MVC-Controller), werden über Konstruktorparameter bereitgestellt. Nachfolgend in diesem Tutorial wird der Konstruktorcode des Controllers angezeigt, der eine Kontextinstanz abruft.

Öffnen Sie Startup.cs, und fügen Sie der ConfigureServices-Methode die hervorgehobenen Zeilen hinzu, um SchoolContext als Dienst zu registrieren.

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<CookiePolicyOptions>(options =>
    {
        options.CheckConsentNeeded = context => true;
        options.MinimumSameSitePolicy = SameSiteMode.None;
    });

    services.AddDbContext<SchoolContext>(options =>
        options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

    services.AddMvc();
}

Der Name der Verbindungszeichenfolge wird an den Kontext übergeben, indem Sie eine Methode auf einem DbContextOptionsBuilder-Objekt aufrufen. Für die lokale Entwicklung liest das ASP.NET Core-Konfigurationssystem die Verbindungszeichenfolge aus der appsettings.json-Datei.

Fügen Sie using-Anweisungen für die Namespaces ContosoUniversity.Data und Microsoft.EntityFrameworkCore hinzu, und erstellen Sie dann das Projekt.

using ContosoUniversity.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Http;

Öffnen Sie die appsettings.json -Datei, und fügen Sie wie im folgenden Beispiel dargestellt eine Verbindungszeichenfolge hinzu.

{
  "ConnectionStrings": {
    "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=ContosoUniversity1;Trusted_Connection=True;MultipleActiveResultSets=true"
  },
  "Logging": {
    "IncludeScopes": false,
    "LogLevel": {
      "Default": "Warning"
    }
  }
}

SQL Server Express LocalDB

Die Verbindungszeichenfolge gibt eine SQL Server-LocalDB-Datenbank an. LocalDB ist eine Basisversion der SQL Server Express-Datenbank-Engine, die zwar zur Anwendungsentwicklung, aber nicht für den Produktionseinsatz bestimmt ist. LocalDB wird bedarfsgesteuert gestartet und im Benutzermodus ausgeführt, sodass keine komplexe Konfiguration anfällt. Standardmäßig erstellt LocalDB .mdf-Datenbankdateien im C:/Users/<user>-Verzeichnis.

Initialisieren Sie die Datenbank mit Testdaten

Entity Framework erstellt eine leere Datenbank für Sie. In diesem Abschnitt schreiben Sie eine Methode, die aufgerufen wird, nachdem die Datenbank erstellt wurde, um diese mit Testdaten aufzufüllen.

Verwenden Sie an dieser Stelle die EnsureCreated-Methode, um die Datenbank automatisch zu erstellen. In einem späteren Tutorial wird dargestellt, wie Sie mit Änderungen an dem Modell umgehen können, indem Sie Code First-Migrationen verwenden, um das Datenbankschema zu verwenden, anstatt die Datenbank zu verwerfen und neu zu erstellen.

Erstellen Sie im Ordner Data (Daten) eine neue Klassendatei mit dem Namen DbInitializer.cs, und ersetzen Sie den Vorlagencode durch den folgenden Code, wodurch, falls nötig, eine Datenbank erstellt wird und Testdaten in eine neue Datenbank geladen werden.

using ContosoUniversity.Models;
using System;
using System.Linq;

namespace ContosoUniversity.Data
{
    public static class DbInitializer
    {
        public static void Initialize(SchoolContext context)
        {
            context.Database.EnsureCreated();

            // Look for any students.
            if (context.Students.Any())
            {
                return;   // DB has been seeded
            }

            var students = new Student[]
            {
            new Student{FirstMidName="Carson",LastName="Alexander",EnrollmentDate=DateTime.Parse("2005-09-01")},
            new Student{FirstMidName="Meredith",LastName="Alonso",EnrollmentDate=DateTime.Parse("2002-09-01")},
            new Student{FirstMidName="Arturo",LastName="Anand",EnrollmentDate=DateTime.Parse("2003-09-01")},
            new Student{FirstMidName="Gytis",LastName="Barzdukas",EnrollmentDate=DateTime.Parse("2002-09-01")},
            new Student{FirstMidName="Yan",LastName="Li",EnrollmentDate=DateTime.Parse("2002-09-01")},
            new Student{FirstMidName="Peggy",LastName="Justice",EnrollmentDate=DateTime.Parse("2001-09-01")},
            new Student{FirstMidName="Laura",LastName="Norman",EnrollmentDate=DateTime.Parse("2003-09-01")},
            new Student{FirstMidName="Nino",LastName="Olivetto",EnrollmentDate=DateTime.Parse("2005-09-01")}
            };
            foreach (Student s in students)
            {
                context.Students.Add(s);
            }
            context.SaveChanges();

            var courses = new Course[]
            {
            new Course{CourseID=1050,Title="Chemistry",Credits=3},
            new Course{CourseID=4022,Title="Microeconomics",Credits=3},
            new Course{CourseID=4041,Title="Macroeconomics",Credits=3},
            new Course{CourseID=1045,Title="Calculus",Credits=4},
            new Course{CourseID=3141,Title="Trigonometry",Credits=4},
            new Course{CourseID=2021,Title="Composition",Credits=3},
            new Course{CourseID=2042,Title="Literature",Credits=4}
            };
            foreach (Course c in courses)
            {
                context.Courses.Add(c);
            }
            context.SaveChanges();

            var enrollments = new Enrollment[]
            {
            new Enrollment{StudentID=1,CourseID=1050,Grade=Grade.A},
            new Enrollment{StudentID=1,CourseID=4022,Grade=Grade.C},
            new Enrollment{StudentID=1,CourseID=4041,Grade=Grade.B},
            new Enrollment{StudentID=2,CourseID=1045,Grade=Grade.B},
            new Enrollment{StudentID=2,CourseID=3141,Grade=Grade.F},
            new Enrollment{StudentID=2,CourseID=2021,Grade=Grade.F},
            new Enrollment{StudentID=3,CourseID=1050},
            new Enrollment{StudentID=4,CourseID=1050},
            new Enrollment{StudentID=4,CourseID=4022,Grade=Grade.F},
            new Enrollment{StudentID=5,CourseID=4041,Grade=Grade.C},
            new Enrollment{StudentID=6,CourseID=1045},
            new Enrollment{StudentID=7,CourseID=3141,Grade=Grade.A},
            };
            foreach (Enrollment e in enrollments)
            {
                context.Enrollments.Add(e);
            }
            context.SaveChanges();
        }
    }
}

Der Code überprüft, ob Studenten in der Datenbank enthalten sind. Wenn dies nicht der Fall ist, nimmt diese an, dass die Datenbank neu ist und mit Testdaten aufgefüllt werden muss. Testdaten werden in Arrays anstelle von List<T>-Auflistungen geladen, um die Leistung zu optimieren.

Ändern Sie in Program.cs die Main-Methode, um beim Anwendungsstart die folgenden Vorgänge auszuführen:

  • Rufen Sie eine Datenbankkontextinstanz aus dem Dependency Injection-Container ab.
  • Rufen Sie die Seedmethode auf, indem Sie den Kontext an diese übergeben.
  • Löschen Sie den Kontext, nachdem die Seedmethode abgeschlossen wurde.
using ContosoUniversity.Data;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;

namespace ContosoUniversity
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var host = CreateHostBuilder(args).Build();

            CreateDbIfNotExists(host);

            host.Run();
        }

        private static void CreateDbIfNotExists(IHost host)
        {
            using (var scope = host.Services.CreateScope())
            {
                var services = scope.ServiceProvider;
                try
                {
                    var context = services.GetRequiredService<SchoolContext>();
                    DbInitializer.Initialize(context);
                }
                catch (Exception ex)
                {
                    var logger = services.GetRequiredService<ILogger<Program>>();
                    logger.LogError(ex, "An error occurred creating the DB.");
                }
            }
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });
    }
}

Wenn die Anwendung zum ersten Mal ausgeführt wird, wird die Datenbank erstellt und mit Testdaten befüllt. Führen Sie bei jeder Datenmodelländerung folgende Schritte durch:

  • Löschen der Datenbank.
  • Aktualisieren Sie die Seed-Methode, und beginnen Sie auf die gleiche Weise mit einer neuen Datenbank von vorne.

In späteren Tutorials wird ein Update für die Datenbank ausgeführt, wenn das Datenmodell geändert wird, ohne die Datenbank zu löschen und neu zu erstellen.

Erstellen Sie Controller und Ansichten

In diesem Abschnitt wird die Gerüstbau-Engine in Visual Studio verwendet, um einen MVC-Controller und Ansichten hinzuzufügen, die EF zum Abfragen und Speichern von Daten verwenden.

Die automatische Erstellung von CRUD-Aktionsmethoden und Ansichten wird als Gerüstbau bezeichnet. Gerüstbau und Codegeneration unterscheiden sich insofern als der Gerüstbaucode ein Startpunkt ist, den Sie Ihren eigenen Anforderungen entsprechend verändern können. Generierter Code wird in der Regel nicht verändert. Wenn Sie generierten Code anpassen müssen, verwenden Sie partielle Klassen, oder generieren Sie den Code erneut, wenn Änderungen vorgenommen werden.

  • Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf den Ordner Controller, und wählen Sie Hinzufügen > Neues Gerüstelement aus.
  • Im Dialogfeld Gerüst hinzufügen:
    • Wählen Sie MVC-Controller mit Ansichten unter Verwendung von Entity Framework aus.
    • Klicken Sie auf Hinzufügen. Das Dialogfeld Add MVC Controller with views, using Entity Framework (MVC-Controller mit Ansichten mithilfe von Entity Framework hinzufügen) wird angezeigt:Das Gerüst „Studierende*r“
    • Wählen Sie unter ModellklasseStudent aus.
    • Wählen Sie in der DatenkontextklasseSchoolContext aus.
    • Akzeptieren Sie den Standardnamen StudentsController.
    • Klicken Sie auf Hinzufügen.

Die Gerüstbau-Engine von Visual Studio erstellt eine StudentsController.cs-Datei und mehrere Ansichten (.cshtml-Dateien), die mit dem Controller zusammenarbeiten.

Beachten Sie, dass der Controller SchoolContext als Konstruktorparameter verwendet.

namespace ContosoUniversity.Controllers
{
    public class StudentsController : Controller
    {
        private readonly SchoolContext _context;

        public StudentsController(SchoolContext context)
        {
            _context = context;
        }

Über die Abhängigkeitsinjektion in ASP.NET Core wird eine Instanz von SchoolContext an den Controller übergeben. Dies wurde in der Datei Startup.cs konfiguriert.

Der Controller enthält eine Index-Aktionsmethode, über die alle Studenten in der Datenbank angezeigt werden. Die Methode ruft eine Listen von Studenten aus der Entitätenmenge „Student“ ab, indem sie die Students-Eigenschaft aus der Datenbankkontextinstanz liest:

public async Task<IActionResult> Index()
{
    return View(await _context.Students.ToListAsync());
}

Später im Tutorial erfahren Sie mehr über die Elemente der asynchronen Programmierung in diesem Code.

In der Views/Students/Index.cshtml-Ansicht wird diese Liste in einer Tabelle dargestellt:

@model IEnumerable<ContosoUniversity.Models.Student>

@{
    ViewData["Title"] = "Index";
}

<h2>Index</h2>

<p>
    <a asp-action="Create">Create New</a>
</p>
<table class="table">
    <thead>
        <tr>
                <th>
                    @Html.DisplayNameFor(model => model.LastName)
                </th>
                <th>
                    @Html.DisplayNameFor(model => model.FirstMidName)
                </th>
                <th>
                    @Html.DisplayNameFor(model => model.EnrollmentDate)
                </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
@foreach (var item in Model) {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.LastName)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.FirstMidName)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.EnrollmentDate)
            </td>
            <td>
                <a asp-action="Edit" asp-route-id="@item.ID">Edit</a> |
                <a asp-action="Details" asp-route-id="@item.ID">Details</a> |
                <a asp-action="Delete" asp-route-id="@item.ID">Delete</a>
            </td>
        </tr>
}
    </tbody>
</table>

Drücken Sie STRG+F5, um das Projekt auszuführen, oder wählen Sie aus dem Menü Debuggen > Ohne Debuggen starten aus.

Klicken Sie auf die Registerkarte „Students“, um die Testdaten abzurufen, die über die DbInitializer.Initialize-Methode eingefügt wurden. Je nachdem, wie klein Ihr Browserfenster dargestellt wird, sehen Sie einen Students-Registerkartenlink unten auf der Seite, oder Sie müssen auf das Navigationssymbol in der oberen rechten Ecke klicken, damit der Link angezeigt wird.

Contoso University-Startseite schmal

Indexseite „Studenten“

Zeigen Sie die Datenbank an

Nachdem Sie die Anwendung gestartet haben, ruft die DbInitializer.Initialize-Methode EnsureCreated auf. EF hat festgestellt, dass noch keine Datenbank vorhanden war und hat deshalb eine erstellt. Anschließend hat der restliche Initialize-Methodencode die Datenbank mit Daten aufgefüllt. Sie können den SQL Server-Objekt-Explorer (SSOX) verwenden, um die Datenbank in Visual Studio abzurufen.

Schließen Sie den Browser.

Wenn das SSOX-Fenster noch nicht geöffnet ist, wählen Sie es aus dem Menü Ansicht in Visual Studio aus.

Klicken Sie im SSOX auf (localdb)\MSSQLLocalDB > Datenbanken und dann auf den Eintrag zu dem Datenbanknamen, der sich in der Verbindungszeichenfolge in der appsettings.json-Datei befindet.

Erweitern Sie den Knoten Tabellen, um die Tabellen in der Datenbank anzuzeigen.

Tabellen im SSOX

Klicken Sie mit der rechten Maustaste auf die Tabelle Students, und klicken Sie auf Daten anzeigen, um die erstellten Spalten und die in die Tabelle eingefügten Zeilen aufzurufen.

Tabelle „Student“ im SSOX

Die Datenbankdateien im Format MDF und LDF befinden sich im Ordner C:\Benutzer<Benutzername>.

Da Sie EnsureCreated in der Initialisierermethode aufrufen, die beim App-Start ausgeführt wird, können Sie Änderungen an der Student-Klasse vornehmen, die Datenbank löschen oder die Anwendung erneut ausführen. Dann wird Ihre Datenbank automatisch Ihren Änderungen entsprechend neu erstellt. Wenn Sie z.B. eine EmailAddress-Eigenschaft zu der Student-Klasse hinzufügen, wird eine neue EmailAddress-Spalte in der neu erstellten Tabelle angezeigt.

Konventionen

Sie mussten nur wenig Code schreiben, damit Entity Framework eine vollständige Datenbank erstellen kann, da Konventionen oder Annahmen von Entity Framework verwendet werden.

  • Die Namen der DbSet-Eigenschaften werden als Tabellennamen verwendet. Für Entitäten, auf die nicht über eine DbSet-Eigenschaft verwiesen wird, werden Entitätsklassennamen als Tabellennamen verwendet.
  • Eigenschaftennamen von Entitäten werden als Spaltennamen verwendet.
  • Entitätseigenschaften mit dem Namen „ID“ oder „classnameID“ werden als Primärschlüsseleigenschaften erkannt.
  • Eine Eigenschaft wird als Fremdschlüsseleigenschaft interpretiert, wenn sie den Namen <Name der Navigationseigenschaft><Eigenschaftenname des Primärschlüssels> hat (z. B. StudentID für die Student-Navigationseigenschaft, da der Primärschlüssel der Student -Entität ID lautet). Fremdschlüsseleigenschaften können auch einfach den Namen <Eigenschaftenname des Primärschlüssels> haben (z. B. EnrollmentID, da der Primärschlüssel derEnrollment -Entität EnrollmentID lautet).

Konventionelles Verhalten kann überschrieben werden. Beispielsweise können Sie, wie bereits in diesem Tutorial erläutert, Tabellennamen explizit angeben. Außerdem können Sie Spaltennamen und jede beliebige Eigenschaft als Primär- oder Fremdschlüssel festlegen. Dies wird in einem späteren Tutorial in dieser Reihe erläutert.

Asynchroner Code

Die asynchrone Programmierung ist der Standardmodus für ASP.NET Core und EF Core.

Der Webserver verfügt nur über eine begrenzte Anzahl von Threads. Daher werden bei hoher Auslastung möglicherweise alle verfügbaren Threads gleichzeitig verwendet. Wenn dies der Fall ist, kann der Server keine neuen Anforderungen verarbeiten, bis die Threads wieder freigegeben werden. Wenn synchroner Code verwendet wird, kann es sein, dass zwar viele Threads belegt sind, diese aber keine Vorgänge ausführen, da sie auf den Abschluss der E/A-Vorgänge warten. Wenn asynchroner Code verwendet wird, werden Threads für den Server freigegeben, wenn diese nur auf den Abschluss der E/A-Vorgänge warten, damit andere Anforderungen verarbeitet werden können. Das bedeutet, dass es durch asynchronen Code ermöglicht wird, Serverressourcen effizienter zu nutzen, und der Server kann ohne Verzögerungen eine größere Menge von Datenverkehr verarbeiten.

Durch die Verwendung von asynchronem Code entsteht ein geringes Maß an Mehraufwand zur Laufzeit. In Situationen, in denen nur wenig Datenverkehr verarbeitet werden muss, haben diese Leistungseinbußen keine negativen Folgen. Wenn es jedoch eine große Menge an Datenverkehr gibt, ist eine potentielle Verbesserung der Leistung von Bedeutung.

Im folgenden Code haben das async-Schlüsselwort, der Task<T>-Rückgabewert, das await-Schlüsselwort und die ToListAsync-Methode zur Folge, dass der Code auf asynchrone Weise ausgeführt wird.

public async Task<IActionResult> Index()
{
    return View(await _context.Students.ToListAsync());
}
  • Das async-Schlüsselwort teilt dem Compiler mit, dass er für einzelne Teile des Methodentexts Rückrufe generieren und das zurückgegebene Task<IActionResult>-Objekt automatisch erstellen soll.
  • Der Rückgabetyp Task<IActionResult> stellt derzeit ausgeführte Arbeiten mit dem Ergebnis von Typ IActionResult dar.
  • Das await-Schlüsselwort hat zur Folge, dass der Compiler die Methode in zwei Teile unterteilt. Der erste Teil endet mit dem Vorgang, der auf asynchrone Weise gestartet wird. Der zweite Teil wird in eine Rückrufmethode übertragen, die aufgerufen wird, wenn der Vorgang abgeschlossen wird.
  • Bei ToListAsync handelt es sich um die asynchrone Version der ToList-Erweiterungsmethode.

Behalten Sie Folgendes im Hinterkopf, wenn Sie asynchronen Code schreiben, der Entity Framework Core verwendet:

  • Es werden nur Anweisungen auf asynchrone Weise ausgeführt, die Abfragen oder Befehle auslösen, die an die Datenbank gesendet werden sollen. Das schließt beispielsweise ToListAsync, SingleOrDefaultAsync und SaveChangesAsync ein. Anweisungen wie var students = context.Students.Where(s => s.LastName == "Davolio"), die nur eine IQueryable-Instanz ändern, sind beispielsweise davon ausgeschlossen.
  • Entity Framework Core-Kontexte sind nicht threadsicher. Versuchen Sie daher nicht, mehrere Vorgänge gleichzeitig auszuführen. Wenn Sie eine beliebige EF-Methode aufrufen, sollten Sie immer das await-Schlüsselwort verwenden.
  • Wenn Sie von den Leistungsvorteilen des asynchronen Codes profitieren möchten, vergewissern Sie sich, dass auch alle Bibliothekspakete, die Sie verwenden (z.B. zum Paging) asynchronen Code verwenden, wenn sie Entity Framework Core-Methoden aufrufen, die Abfragen an die Datenbank senden.

Weitere Informationen zur asynchronen Programmierung in .NET finden Sie unter Async (Übersicht).

Nächste Schritte

Fahren Sie mit dem nächsten Tutorial fort, um zu erfahren, wie Sie grundlegende CRUD-Vorgänge (Create, Read, Update, Delete = Erstellen, Lesen, Aktualisieren, Löschen) ausführen.