Freigeben über


Teil 4: Hinzufügen eines Modells zu einer ASP.NET Core MVC-App

Note

Dies ist nicht die neueste Version dieses Artikels. Die aktuelle Version finden Sie in der .NET 10-Version dieses Artikels.

Warning

Diese Version von ASP.NET Core wird nicht mehr unterstützt. Weitere Informationen finden Sie unter .NET und .NET Core Support Policy. Die aktuelle Version finden Sie in der .NET 10-Version dieses Artikels.

In diesem Tutorial werden Klassen für die Verwaltung von Filmen in einer Datenbank hinzugefügt. Diese Klassen stellen den „Modell“-Teil der MVC-App dar.

Diese Modellklassen werden mit Entity Framework Core (EF Core) verwendet, um mit einer Datenbank zu arbeiten. EF Core ist ein ORM-Framework (Objektrelationales Mapping), das den Datenzugriffscode vereinfacht, den Sie schreiben müssen.

Die erstellten Modellklassen werden als POCO-Klassen bezeichnet (von Plain Old CLR Objects). POCO-Klassen weisen keine Abhängigkeit von EF Core auf. Sie definieren lediglich die Eigenschaften der Daten, die in der Datenbank gespeichert werden sollen.

In diesem Tutorial werden zuerst die Modellklassen erstellt. Anschließend erstellt EF Core die Datenbank.

Hinzufügen einer Datenmodellklasse

Klicken Sie mit der rechten Maustaste auf den Ordner Modelle>Hinzufügen>Klasse. Nennen Sie die Datei Movie.cs.

Aktualisieren Sie die Datei Models/Movie.cs mit dem folgenden Code:

using System.ComponentModel.DataAnnotations;

namespace MvcMovie.Models;

public class Movie
{
    public int Id { get; set; }
    public string? Title { get; set; }
    [DataType(DataType.Date)]
    public DateTime ReleaseDate { get; set; }
    public string? Genre { get; set; }
    public decimal Price { get; set; }
}

Die Movie-Klasse enthält ein Id-Feld, das von der Datenbank für den Primärschlüssel benötigt wird.

Das DataType-Attribut für ReleaseDate gibt den Typ der Daten (Date) an. Mit diesem Attribut:

  • Der Benutzer muss keine Zeitinformationen in das Datumsfeld eingeben.
  • Nur das Datum wird angezeigt, keine Zeitinformationen.

DataAnnotations werden in einem späteren Tutorial behandelt.

Das Fragezeichen nach string zeigt an, dass die Eigenschaft null sein kann. Weitere Informationen finden Sie unter Nullable-Verweistypen.

Hinzufügen von NuGet-Paketen

Visual Studio installiert automatisch die erforderlichen Pakete.

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

Strukturierung der Filmseiten

Verwenden Sie das Gerüstbauwerkzeug, um CRUD-Seiten (Create, Read, Update und Delete) für das Filmmodell zu erstellen.

Klicken Sie in Projektmappen-Explorer mit der rechten Maustaste auf den Ordner Controller, und wählen Sie Add > New Scaffolded Item aus.

Ansicht des Menüelements, um ein neues Gerüstelement hinzuzufügen.

Im Dialogfeld Neues Gerüstelement hinzufügen:

  • Wählen Sie im linken Bereich Installiert>Allgemein>MVC aus.
  • Wählen Sie MVC-Controller mit Ansichten unter Verwendung von Entity Framework aus.
  • Wählen Sie Hinzufügen aus.

Dialogfeld

Vervollständigen Sie das Dialogfeld MVC-Controller mit Ansichten unter Verwendung von Entity Framework hinzufügen:

  • Wählen Sie in der Dropdownliste Modellklasse den Eintrag Movie (MvcMovie.Models) aus.
  • Wählen Sie in der Zeile Datenkontextklasse das +-Zeichen (Plus) aus.
    • Im Dialogfeld Datenkontext hinzufügen wird der Klassenname MvcMovie.Data.MvcMovieContext generiert.
    • Wählen Sie Hinzufügen aus.
  • Wählen Sie in der Dropdown-Liste DatenbankanbieterSQL Server aus.
  • Ansichten und Controllername: Belassen Sie den Standard.
  • Wählen Sie Hinzufügen aus.

Datenkontext hinzufügen, Standardwerte beibehalten.

Wenn Sie eine Fehlermeldung erhalten, wählen Sie Hinzufügen ein zweites Mal aus, um es erneut zu versuchen.

Scaffolding fügt die folgenden Pakete hinzu:

  • Microsoft.EntityFrameworkCore.SqlServer
  • Microsoft.EntityFrameworkCore.Tools
  • Microsoft.VisualStudio.Web.CodeGeneration.Design

Durch das Gerüst entsteht Folgendes:

  • Ein Film-Controller: Controllers/MoviesController.cs
  • Razor-Ansichtsdateien für die Seiten Erstellen, Löschen, Details, Bearbeiten und Index: Views/Movies/*.cshtml
  • Eine Datenbankkontext-Klasse: Data/MvcMovieContext.cs

Beim Gerüstbau werden folgende Aktualisierungen vorgenommen:

  • Die erforderlichen Paketverweise werden in die MvcMovie.csproj-Projektdatei eingefügt.
  • Registriert den Datenbankkontext in der Datei Program.cs.
  • Fügt der Datei appsettings.json eine Datenbank-Verbindungszeichenfolge hinzu.

Die automatische Erstellung dieser Dateien und Dateiaktualisierungen wird als Gerüstbau bezeichnet.

Sie können die Gerüstbauseiten noch nicht verwenden, da die Datenbank nicht vorhanden ist. Wenn Sie die App ausführen und den Link Movie App auswählen, wird die Fehlermeldung Die Datenbank kann nicht geöffnet werden oder Keine solche Tabelle: Movie angezeigt.

Erstellen Sie die App, um sicherzustellen, dass keine Fehler vorliegen.

Erste Migration

Verwenden Sie das Feature EF CoreMigrationen, um die Datenbank zu erstellen. Bei Migrationen handelt es sich um mehrere Tools, mit denen Sie eine Datenbank dem Datenmodell entsprechend erstellen und aktualisieren können.

Wählen Sie im Menü ToolsNuGet Paket-Manager>Paket-Manager Console aus.

Geben Sie in der Paket-Manager Konsole (PMC) den folgenden Befehl ein:

Add-Migration InitialCreate

  • Add-Migration InitialCreate: Generiert die Migrationsdatei Migrations/{timestamp}_InitialCreate.cs. Das InitialCreate-Argument ist der Migrationsname. Es kann jeder Name verwendet werden, aber per Konvention wird ein Name ausgewählt, der die Migration beschreibt. Da dies die erste Migration ist, enthält die generierte Klasse Code zum Erstellen des Datenbankschemas. Das Datenbankschema basiert auf dem Modell, das in der Klasse MvcMovieContext angegeben ist.

Es wird die folgende Warnung angezeigt, die in einem späteren Schritt behandelt wird:

Für die Dezimaleigenschaft „Preis“ beim Entitätstyp „Movie“ wurde kein Speichertyp angegeben. Dadurch werden Werte stillschweigend abgeschnitten, falls sie nicht der vorgegebenen Genauigkeit und Skalierung entsprechen. Geben Sie den Spaltentyp von SQL-Server explizit an, der durch „HasColumnType“ sämtliche Werte in „OnModelCreating“ unterstützen kann. Geben Sie Genauigkeit und Skalierung mithilfe von „HasPrecision“ an, oder konfigurieren Sie mithilfe von „HasConversion“ einen Wertkonverter.

Geben Sie in der PMC den folgenden Befehl ein:

Update-Database

  • Update-Database: Aktualisiert die Datenbank auf die neueste Migration, die der vorherige Befehl erstellt hat. Der Befehl führt die Up-Methode in der Datei Migrations/{time-stamp}_InitialCreate.cs aus, mit der die Datenbank erstellt wird.

Weitere Informationen zu den PMC-Tools für EF Core finden Sie unter EF Core Tools reference - PMC in Visual Studio.

Testen der App

Führen Sie die APP aus, und wählen Sie den Link Movie App aus.

App, die den Film-App-Bildschirm zeigt.

Wenn Sie eine ähnliche Ausnahme wie die folgende erhalten, haben Sie möglicherweise den Update-Database-Befehl im Migrationsschritt nicht durchgeführt:

SqlException: Cannot open database "MvcMovieContext-1" requested by the login. The login failed.

Note

Sie können unter Umständen in das Feld Price keine Kommas als Dezimaltrennzeichen eingeben. Zur Unterstützung der jQuery-Validierung für Gebietsschemas mit einer anderen Sprache als Englisch, in denen ein Komma („,“) als Dezimaltrennzeichen verwendet wird, und für Datums- und Uhrzeitformate, die nicht dem US-englischen Format entsprechen, muss die App globalisiert werden. Anweisungen zur Globalisierung finden Sie unter dieses GitHub Problem.

Untersuchen der generierten Datenbankkontextklasse und -registrierung

Bei EF Core erfolgt der Datenzugriff über ein Modell. Ein Modell setzt sich aus Entitätsklassen und einem Kontextobjekt zusammen, das eine Sitzung mit der Datenbank darstellt. Das Kontextobjekt ermöglicht das Abfragen und Speichern von Daten. Der Datenbankkontext wird von Microsoft abgeleitet. EntityFrameworkCore.DbContext und gibt die Entitäten an, die in das Datenmodell aufgenommen werden sollen.

Scaffolding erstellt die Data/MvcMovieContext.cs-Datenbankkontextklasse.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using MvcMovie.Models;

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

        public DbSet<MvcMovie.Models.Movie> Movie { get; set; } = default!;
    }
}

Der obige Code erstellt eine DbSet<Movie>-Eigenschaft, die die Filme in der Datenbank darstellt.

Abhängigkeitsinjektion

ASP.NET Core wird mit Dependency Injection (DI) erstellt. Dienste, wie der Datenbankkontext, werden bei DI in Program.cs registriert. Diese Dienste werden den Komponenten, die sie benötigen, über Konstruktorparameter zur Verfügung gestellt.

Der Konstruktor in der Datei Controllers/MoviesController.cs verwendet die Abhängigkeitsinjektion zum Einfügen des Datenbankkontexts (MvcMovieContext) in den Controller. Der Datenbankkontext wird in den einzelnen CRUD-Methoden im Controller verwendet.

Beim Gerüstbau wurde der folgende hervorgehobene Code in Program.cs erzeugt:

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<MvcMovieContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("MvcMovieContext") ?? throw new InvalidOperationException("Connection string 'MvcMovieContext' not found.")));

Das ASP.NET Core-Konfigurationssystem liest die Datenbank-Verbindungszeichenfolge "MvcMovieContext".

Überprüfen Sie die generierte Datenbank-Verbindungszeichenfolge

Das Gerüst hat der Datei appsettings.json eine Verbindungszeichenfolge hinzugefügt.

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "MvcMovieContext": "Server=(localdb)\\mssqllocaldb;Database=MvcMovieContext-4ebefa10-de29-4dea-b2ad-8a8dc6bcf374;Trusted_Connection=True;MultipleActiveResultSets=true"
  }
}

Für die lokale Entwicklung liest das konfigurationssystem ASP.NET Core den Schlüssel ConnectionString aus der Datei appsettings.json.

Die InitialCreate-Klasse

Untersuchen Sie die Migrationsdatei Migrations/{timestamp}_InitialCreate.cs:

using System;
using Microsoft.EntityFrameworkCore.Migrations;

#nullable disable

namespace MvcMovie.Migrations
{
    /// <inheritdoc />
    public partial class InitialCreate : Migration
    {
        /// <inheritdoc />
        protected override void Up(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.CreateTable(
                name: "Movie",
                columns: table => new
                {
                    Id = table.Column<int>(type: "int", nullable: false)
                        .Annotation("SqlServer:Identity", "1, 1"),
                    Title = table.Column<string>(type: "nvarchar(max)", nullable: true),
                    ReleaseDate = table.Column<DateTime>(type: "datetime2", nullable: false),
                    Genre = table.Column<string>(type: "nvarchar(max)", nullable: true),
                    Price = table.Column<decimal>(type: "decimal(18,2)", nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_Movie", x => x.Id);
                });
        }

        /// <inheritdoc />
        protected override void Down(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.DropTable(
                name: "Movie");
        }
    }
}

Im vorhergehenden Code:

  • InitialCreate.Up erstellt die Movie-Tabelle und konfiguriert Id als Primärschlüssel.
  • InitialCreate.Down macht die von der Up-Migration vorgenommenen Schemaänderungen rückgängig.

Abhängigkeitsinjektion im Controller

Öffnen Sie die Datei Controllers/MoviesController.cs, und überprüfen Sie den Konstruktor:

public class MoviesController : Controller
{
    private readonly MvcMovieContext _context;

    public MoviesController(MvcMovieContext context)
    {
        _context = context;
    }

Der Konstruktor verwendet die Abhängigkeitsinjektion zum Einfügen des Datenbankkontexts (MvcMovieContext) in den Controller. Der Datenbankkontext wird in den einzelnen CRUD-Methoden im Controller verwendet.

Testen Sie die Seite Create (Erstellen). Geben Sie die Daten ein, und senden Sie sie.

Testen Sie die Seiten Edit (Bearbeiten), Details und Delete (Löschen).

Stark typisierte Modelle und die @model-Direktive

Weiter oben in diesem Tutorial haben Sie gesehen, wie ein Controller mithilfe des Wörterbuchs ViewData Daten oder Objekte an eine Ansicht übergeben kann. Das Wörterbuch ViewData ist ein dynamisches Objekt, das eine praktische Möglichkeit bietet, um Informationen spätgebunden an eine Ansicht zu übergeben.

MVC bietet die Möglichkeit, stark typisierte Modellobjekte an eine Ansicht zu übergeben. Dieser stark typisierte Ansatz ermöglicht eine Überprüfung Ihres Codes zur Kompilierzeit. Der Gerüstmechanismus hat ein stark typisiertes Modell an die MoviesController-Klasse und Ansichten übergeben.

Untersuchen Sie die generierte Details-Methode in der Datei Controllers/MoviesController.cs:

// GET: Movies/Details/5
public async Task<IActionResult> Details(int? id)
{
    if (id == null)
    {
        return NotFound();
    }

    var movie = await _context.Movie
        .FirstOrDefaultAsync(m => m.Id == id);
    if (movie == null)
    {
        return NotFound();
    }

    return View(movie);
}

Der id-Parameter wird im Allgemeinen als Routendaten übergeben. https://localhost:{PORT}/movies/details/1 legt beispielsweise Folgendes fest:

  • Den Controller dem Controller movies zuordnen, dem ersten URL-Segment.
  • Die Aktion auf details (das zweite URL-Segment).
  • Der id wird auf 1 gesetzt, das letzte URL-Segment.

id kann wie im folgenden Beispiel mit einer Abfragezeichenfolge übergeben werden:

https://localhost:{PORT}/movies/details?id=1

Der id-Parameter wird als Nullable-Typ (int?) für den Fall definiert, dass kein id-Wert angegeben wird.

Ein Lambdaausdruck wird an die FirstOrDefaultAsync-Methode übergeben, um Movie-Entitäten auszuwählen, die mit den Routendaten oder dem Wert der Abfragezeichenfolge übereinstimmen.

var movie = await _context.Movie
    .FirstOrDefaultAsync(m => m.Id == id);

Wenn ein Film gefunden wird, wird eine Instanz des Movie-Modells an die Ansicht Details übergeben:

return View(movie);

Untersuchen Sie den Inhalt der Datei Views/Movies/Details.cshtml:

@model MvcMovie.Models.Movie

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

<h1>Details</h1>

<div>
    <h4>Movie</h4>
    <hr />
    <dl class="row">
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Title)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Title)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.ReleaseDate)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.ReleaseDate)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Genre)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Genre)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Price)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Price)
        </dd>
    </dl>
</div>
<div>
    <a asp-action="Edit" asp-route-id="@Model.Id">Edit</a> |
    <a asp-action="Index">Back to List</a>
</div>

Die @model-Anweisung am Anfang der Ansichtsdatei gibt den Typ des Objekts an, den die Ansicht erwartet. Beim Erstellen des Movie-Controllers wurde die folgende @model-Anweisung eingeschlossen:

@model MvcMovie.Models.Movie

Diese @model-Direktive ermöglicht den Zugriff auf das Video, das der Controller an die Ansicht übergeben hat. Das Model-Objekt ist stark typisiert. In der Ansicht Details.cshtml übergibt der Code jedes Filmfeld an die HTML-Hilfsprogramme DisplayNameFor und DisplayFor mit dem stark typisierten Model-Objekt. Die Methoden Create und Edit und Ansichten übergeben außerdem ein Movie-Modelobjekt.

Untersuchen Sie die Ansicht Index.cshtml und die Index-Methode im Movies-Controller. Beachten Sie, wie der Code beim Aufrufen der List-Methode ein View-Objekt erstellt. Der Code übergibt diese Liste Movies aus der Aktionsmethode Index an die Ansicht:

// GET: Movies
public async Task<IActionResult> Index()
{
    return View(await _context.Movie.ToListAsync());
}

Der Code gibt Problemdetails zurück, wenn die Movie-Eigenschaft des Datenkontexts NULL ist.

Als der Movies-Controller erstellt wurde, hat das Scaffolding automatisch die folgende @model-Anweisung oben in der Datei Index.cshtml hinzugefügt.

@model IEnumerable<MvcMovie.Models.Movie>

Die @model-Direktive ermöglicht den Zugriff auf die Liste der Filme, die der Controller an die Ansicht übergeben hat, indem ein stark typisiertes Model-Objekt verwendet wird. In der Ansicht Index.cshtml durchläuft der Code z. B. die Filme mithilfe einer foreach-Anweisung für das stark typisierte Model-Objekt in einer Schleife:

@model IEnumerable<MvcMovie.Models.Movie>

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

<h1>Index</h1>

<p>
    <a asp-action="Create">Create New</a>
</p>
<table class="table">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.Title)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.ReleaseDate)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Genre)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Price)
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
@foreach (var item in Model) {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.Title)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.ReleaseDate)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Genre)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Price)
            </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>

Da das Model-Objekt stark typisiert ist als IEnumerable<Movie>-Objekt, ist jedes Element in der Schleife vom Typ Movie. Neben anderen Vorteilen überprüft der Compiler die im Code verwendeten Typen.

Weitere Ressourcen

In diesem Tutorial werden Klassen für die Verwaltung von Filmen in einer Datenbank hinzugefügt. Diese Klassen stellen den „Modell“-Teil der MVC-App dar.

Diese Modellklassen werden mit Entity Framework Core (EF Core) verwendet, um mit einer Datenbank zu arbeiten. EF Core ist ein ORM-Framework (Objektrelationales Mapping), das den Datenzugriffscode vereinfacht, den Sie schreiben müssen.

Die erstellten Modellklassen werden als POCO-Klassen bezeichnet (von Plain Old CLR Objects). POCO-Klassen weisen keine Abhängigkeit von EF Core auf. Sie definieren lediglich die Eigenschaften der Daten, die in der Datenbank gespeichert werden sollen.

In diesem Tutorial werden zuerst die Modellklassen erstellt. Anschließend erstellt EF Core die Datenbank.

Hinzufügen einer Datenmodellklasse

Klicken Sie mit der rechten Maustaste auf den Ordner Modelle>Hinzufügen>Klasse. Nennen Sie die Datei Movie.cs.

Aktualisieren Sie die Datei Models/Movie.cs mit dem folgenden Code:

using System.ComponentModel.DataAnnotations;

namespace MvcMovie.Models;

public class Movie
{
    public int Id { get; set; }
    public string? Title { get; set; }
    [DataType(DataType.Date)]
    public DateTime ReleaseDate { get; set; }
    public string? Genre { get; set; }
    public decimal Price { get; set; }
}

Die Movie-Klasse enthält ein Id-Feld, das von der Datenbank für den Primärschlüssel benötigt wird.

Das DataType-Attribut für ReleaseDate gibt den Typ der Daten (Date) an. Mit diesem Attribut:

  • Der Benutzer muss keine Zeitinformationen in das Datumsfeld eingeben.
  • Nur das Datum wird angezeigt, keine Zeitinformationen.

DataAnnotations werden in einem späteren Tutorial behandelt.

Das Fragezeichen nach string gibt an, dass die Eigenschaft Nullwerte zulässt. Weitere Informationen finden Sie unter Nullable-Verweistypen.

Hinzufügen von NuGet-Paketen

Visual Studio installiert automatisch die erforderlichen Pakete.

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

Grundgerüst der Filmseiten

Verwenden Sie das Tool für den Gerüstbau, um die Seiten Create, Read, Update und Delete (CRUD) für das Film-Modell zu erstellen.

Klicken Sie in Projektmappen-Explorer mit der rechten Maustaste auf den Ordner Controller, und wählen Sie Add > New Scaffolded Item aus.

Screenshot für oben genannten Schritt

Im Dialogfeld Neues Gerüstelement hinzufügen:

  • Wählen Sie im linken Bereich Installiert>Allgemein>MVC aus.
  • Wählen Sie MVC-Controller mit Ansichten unter Verwendung von Entity Framework aus.
  • Wählen Sie Hinzufügen aus.

Dialogfeld „Gerüst hinzufügen“

Vervollständigen Sie das Dialogfeld MVC-Controller mit Ansichten unter Verwendung von Entity Framework hinzufügen:

  • Wählen Sie in der Dropdownliste Modellklasse den Eintrag Movie (MvcMovie.Models) aus.
  • Wählen Sie in der Zeile Datenkontextklasse das +-Zeichen (Plus) aus.
    • Im Dialogfeld Datenkontext hinzufügen wird der Klassenname MvcMovie.Data.MvcMovieContext generiert.
    • Wählen Sie Hinzufügen aus.
  • Wählen Sie im Dropdown-Menü DatenbankanbieterSQL Server aus.
  • Ansichten und Controllername: Standard beibehalten.
  • Wählen Sie Hinzufügen aus.

Datenkontext hinzufügen: Standardwerte behalten

Wenn Sie eine Fehlermeldung erhalten, wählen Sie Hinzufügen ein zweites Mal aus, um es erneut zu versuchen.

Beim Gerüstbau werden die folgenden Pakete hinzugefügt:

  • Microsoft.EntityFrameworkCore.SqlServer
  • Microsoft.EntityFrameworkCore.Tools
  • Microsoft.VisualStudio.Web.CodeGeneration.Design

Beim Gerüstbau wird Folgendes erstellt:

  • Ein Film-Controller: Controllers/MoviesController.cs
  • Razor-Ansichtsdateien für die Seiten Erstellen, Löschen, Details, Bearbeiten und Index: Views/Movies/*.cshtml
  • Eine Datenbankkontext-Klasse: Data/MvcMovieContext.cs

Beim Gerüstbau werden folgende Aktualisierungen vorgenommen:

  • Die erforderlichen Paketverweise werden in die MvcMovie.csproj-Projektdatei eingefügt.
  • Registriert den Datenbankkontext in der Datei Program.cs.
  • Fügt der Datei appsettings.json eine Datenbank-Verbindungszeichenfolge hinzu.

Die automatische Erstellung dieser Dateien und Dateiaktualisierungen wird als Gerüstbau bezeichnet.

Sie können die Gerüstbauseiten noch nicht verwenden, da die Datenbank nicht vorhanden ist. Wenn Sie die App ausführen und den Link Movie App auswählen, wird die Fehlermeldung Die Datenbank kann nicht geöffnet werden oder Keine solche Tabelle: Movie angezeigt.

Erstellen Sie die App, um sicherzustellen, dass keine Fehler vorliegen.

Erste Migration

Verwenden Sie die Funktion EF CoreMigrationen, um die Datenbank zu erstellen. Bei Migrationen handelt es sich um mehrere Tools, mit denen Sie eine Datenbank dem Datenmodell entsprechend erstellen und aktualisieren können.

Wählen Sie im Menü Werkzeuge den NuGet Paket-Manager> und die Paket-Manager Console aus.

Geben Sie in der Paket-Manager Konsole (PMC) den folgenden Befehl ein:

Add-Migration InitialCreate

  • Add-Migration InitialCreate: Generiert die Migrationsdatei Migrations/{timestamp}_InitialCreate.cs. Das InitialCreate-Argument ist der Migrationsname. Es kann jeder Name verwendet werden, aber per Konvention wird ein Name ausgewählt, der die Migration beschreibt. Da dies die erste Migration ist, enthält die generierte Klasse Code zum Erstellen des Datenbankschemas. Das Datenbankschema basiert auf dem Modell, das in der Klasse MvcMovieContext angegeben ist.

Es wird die folgende Warnung angezeigt, die in einem späteren Schritt behandelt wird:

Für die Dezimaleigenschaft „Preis“ beim Entitätstyp „Movie“ wurde kein Speichertyp angegeben. Dadurch werden Werte stillschweigend abgeschnitten, falls diese nicht der Standardpräzision und -skala entsprechen. Geben Sie den Spaltentyp von SQL-Server explizit an, der durch „HasColumnType“ sämtliche Werte in „OnModelCreating“ unterstützen kann. Geben Sie Genauigkeit und Skalierung mithilfe von „HasPrecision“ an, oder konfigurieren Sie mithilfe von „HasConversion“ einen Wertkonverter.

Geben Sie in der PMC den folgenden Befehl ein:

Update-Database

  • Update-Database: Aktualisiert die Datenbank auf die neueste Migration, die der vorherige Befehl erstellt hat. Der Befehl führt die Up-Methode in der Datei Migrations/{time-stamp}_InitialCreate.cs aus, mit der die Datenbank erstellt wird.

Weitere Informationen zu den PMC-Tools für EF Core finden Sie unter EF Core Tools reference - PMC in Visual Studio.

Testen der App

Führen Sie die APP aus, und wählen Sie den Link Movie App aus.

Wenn Sie eine ähnliche Ausnahme wie die folgende erhalten, haben Sie möglicherweise den Update-Database-Befehl im Migrationsschritt nicht durchgeführt:

SqlException: Cannot open database "MvcMovieContext-1" requested by the login. The login failed.

Note

Sie können unter Umständen in das Feld Price keine Kommas als Dezimaltrennzeichen eingeben. Zur Unterstützung der jQuery-Validierung für Gebietsschemas mit einer anderen Sprache als Englisch, in denen ein Komma („,“) als Dezimaltrennzeichen verwendet wird, und für Datums- und Uhrzeitformate, die nicht dem US-englischen Format entsprechen, muss die App globalisiert werden. Anweisungen zur Globalisierung finden Sie unter dieses GitHub Problem.

Untersuchen der generierten Datenbankkontextklasse und -registrierung

Bei EF Core erfolgt der Datenzugriff über ein Modell. Ein Modell setzt sich aus Entitätsklassen und einem Kontextobjekt zusammen, das eine Sitzung mit der Datenbank darstellt. Das Kontextobjekt ermöglicht das Abfragen und Speichern von Daten. Der Datenbankkontext wird von Microsoft abgeleitet. EntityFrameworkCore.DbContext und gibt die Entitäten an, die in das Datenmodell aufgenommen werden sollen.

Scaffolding erstellt die Data/MvcMovieContext.cs-Datenbankkontextklasse.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using MvcMovie.Models;

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

        public DbSet<MvcMovie.Models.Movie> Movie { get; set; } = default!;
    }
}

Der obige Code erstellt eine DbSet<Movie>-Eigenschaft, die die Filme in der Datenbank darstellt.

Abhängigkeitsinjektion

ASP.NET Core wird mit Dependency Injection (DI) erstellt. Dienste, wie der Datenbankkontext, werden bei DI in Program.cs registriert. Diese Dienste werden den Komponenten, die sie benötigen, über Konstruktorparameter zur Verfügung gestellt.

Der Konstruktor in der Datei Controllers/MoviesController.cs verwendet die Abhängigkeitsinjektion zum Einfügen des Datenbankkontexts (MvcMovieContext) in den Controller. Der Datenbankkontext wird in den einzelnen CRUD-Methoden im Controller verwendet.

Beim Gerüstbau wurde der folgende hervorgehobene Code in Program.cs erzeugt:

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<MvcMovieContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("MvcMovieContext") ?? throw new InvalidOperationException("Connection string 'MvcMovieContext' not found.")));

Das ASP.NET Core-Konfigurationssystem liest die Datenbank-Verbindungszeichenfolge "MvcMovieContext".

Überprüfen Sie die generierte Datenbank-Verbindungszeichenfolge

Das Gerüst hat der Datei appsettings.json eine Verbindungszeichenfolge hinzugefügt.

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "MvcMovieContext": "Server=(localdb)\\mssqllocaldb;Database=MvcMovieContext-4ebefa10-de29-4dea-b2ad-8a8dc6bcf374;Trusted_Connection=True;MultipleActiveResultSets=true"
  }
}

Für die lokale Entwicklung liest das konfigurationssystem ASP.NET Core den Schlüssel ConnectionString aus der Datei appsettings.json.

Die InitialCreate-Klasse

Untersuchen Sie die Migrationsdatei Migrations/{timestamp}_InitialCreate.cs:

using System;
using Microsoft.EntityFrameworkCore.Migrations;

#nullable disable

namespace MvcMovie.Migrations
{
    /// <inheritdoc />
    public partial class InitialCreate : Migration
    {
        /// <inheritdoc />
        protected override void Up(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.CreateTable(
                name: "Movie",
                columns: table => new
                {
                    Id = table.Column<int>(type: "int", nullable: false)
                        .Annotation("SqlServer:Identity", "1, 1"),
                    Title = table.Column<string>(type: "nvarchar(max)", nullable: true),
                    ReleaseDate = table.Column<DateTime>(type: "datetime2", nullable: false),
                    Genre = table.Column<string>(type: "nvarchar(max)", nullable: true),
                    Price = table.Column<decimal>(type: "decimal(18,2)", nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_Movie", x => x.Id);
                });
        }

        /// <inheritdoc />
        protected override void Down(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.DropTable(
                name: "Movie");
        }
    }
}

Im vorhergehenden Code:

  • InitialCreate.Up erstellt die Movie-Tabelle und konfiguriert Id als Primärschlüssel.
  • InitialCreate.Down macht die von der Up-Migration vorgenommenen Schemaänderungen rückgängig.

Abhängigkeitsinjektion im Controller

Öffnen Sie die Datei Controllers/MoviesController.cs, und überprüfen Sie den Konstruktor:

public class MoviesController : Controller
{
    private readonly MvcMovieContext _context;

    public MoviesController(MvcMovieContext context)
    {
        _context = context;
    }

Der Konstruktor verwendet die Abhängigkeitsinjektion zum Einfügen des Datenbankkontexts (MvcMovieContext) in den Controller. Der Datenbankkontext wird in den einzelnen CRUD-Methoden im Controller verwendet.

Testen Sie die Seite Create (Erstellen). Geben Sie die Daten ein, und senden Sie sie.

Testen Sie die Seiten Edit (Bearbeiten), Details und Delete (Löschen).

Stark typisierte Modelle und die @model-Direktive

Weiter oben in diesem Tutorial haben Sie gesehen, wie ein Controller mithilfe des Wörterbuchs ViewData Daten oder Objekte an eine Ansicht übergeben kann. Das Wörterbuch ViewData ist ein dynamisches Objekt, das eine praktische Möglichkeit bietet, um Informationen spätgebunden an eine Ansicht zu übergeben.

MVC bietet die Möglichkeit, stark typisierte Modellobjekte an eine Ansicht zu übergeben. Dieser stark typisierte Ansatz ermöglicht eine Überprüfung Ihres Codes zur Kompilierzeit. Der Gerüstmechanismus hat ein stark typisiertes Modell an die MoviesController-Klasse und Ansichten übergeben.

Untersuchen Sie die generierte Details-Methode in der Datei Controllers/MoviesController.cs:

// GET: Movies/Details/5
public async Task<IActionResult> Details(int? id)
{
    if (id == null)
    {
        return NotFound();
    }

    var movie = await _context.Movie
        .FirstOrDefaultAsync(m => m.Id == id);
    if (movie == null)
    {
        return NotFound();
    }

    return View(movie);
}

Der id-Parameter wird im Allgemeinen als Routendaten übergeben. https://localhost:{PORT}/movies/details/1 legt beispielsweise Folgendes fest:

  • Den Controller dem Controller movies zuordnen, dem ersten URL-Segment.
  • Die Aktion auf details (das zweite URL-Segment).
  • Der id wird auf 1 gesetzt, das letzte URL-Segment.

id kann wie im folgenden Beispiel mit einer Abfragezeichenfolge übergeben werden:

https://localhost:{PORT}/movies/details?id=1

Der id-Parameter wird als Nullable-Typ (int?) für den Fall definiert, dass kein id-Wert angegeben wird.

Ein Lambdaausdruck wird an die FirstOrDefaultAsync-Methode übergeben, um Movie-Entitäten auszuwählen, die mit den Routendaten oder dem Wert der Abfragezeichenfolge übereinstimmen.

var movie = await _context.Movie
    .FirstOrDefaultAsync(m => m.Id == id);

Wenn ein Film gefunden wird, wird eine Instanz des Movie-Modells an die Ansicht Details übergeben:

return View(movie);

Untersuchen Sie den Inhalt der Datei Views/Movies/Details.cshtml:

@model MvcMovie.Models.Movie

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

<h1>Details</h1>

<div>
    <h4>Movie</h4>
    <hr />
    <dl class="row">
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Title)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Title)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.ReleaseDate)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.ReleaseDate)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Genre)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Genre)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Price)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Price)
        </dd>
    </dl>
</div>
<div>
    <a asp-action="Edit" asp-route-id="@Model.Id">Edit</a> |
    <a asp-action="Index">Back to List</a>
</div>

Die @model-Anweisung am Anfang der Ansichtsdatei gibt den Typ des Objekts an, den die Ansicht erwartet. Beim Erstellen des Movie-Controllers wurde die folgende @model-Anweisung eingeschlossen:

@model MvcMovie.Models.Movie

Diese @model-Anweisung ermöglicht den Zugriff auf den Film, den der Controller an die Ansicht übergeben hat. Das Model-Objekt ist stark typisiert. In der Ansicht Details.cshtml übergibt der Code jedes Filmfeld an die HTML-Helfer DisplayNameFor und DisplayFor mit dem stark typisierten Model-Objekt. Die Methoden Create und Edit und Ansichten übergeben außerdem ein Movie-Modelobjekt.

Untersuchen Sie die Ansicht Index.cshtml und die Index-Methode im Movies-Controller. Beachten Sie, wie der Code beim Aufrufen der List-Methode ein View-Objekt erstellt. Der Code übergibt diese Liste Movies aus der Aktionsmethode Index an die Ansicht:

// GET: Movies
public async Task<IActionResult> Index()
{
    return View(await _context.Movie.ToListAsync());
}

Der Code gibt Problemdetails zurück, wenn die Movie-Eigenschaft des Datenkontexts NULL ist.

Als der Movie-Controller erstellt wurde, hat der Gerüstcode automatisch die folgende @model-Anweisung am Anfang der Datei Index.cshtml hinzugefügt.

@model IEnumerable<MvcMovie.Models.Movie>

Diese @model-Direktive ermöglicht Zugriff auf die Liste der Filme, die der Controller an die Ansicht übergeben hat, indem ein stark typisiertes Model-Objekt verwendet wird. Z. B. durchläuft der Code in der Ansicht Index.cshtml die Filme mittels einer foreach-Anweisung über das stark typisierte Model-Objekt.

@model IEnumerable<MvcMovie.Models.Movie>

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

<h1>Index</h1>

<p>
    <a asp-action="Create">Create New</a>
</p>
<table class="table">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.Title)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.ReleaseDate)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Genre)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Price)
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
@foreach (var item in Model) {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.Title)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.ReleaseDate)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Genre)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Price)
            </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>

Da das Model-Objekt als ein IEnumerable<Movie>-Objekt stark typisiert ist, wird jedes Element in der Schleife als Movie typisiert. Neben anderen Vorteilen überprüft der Compiler die im Code verwendeten Typen.

Weitere Ressourcen

In diesem Tutorial werden Klassen für die Verwaltung von Filmen in einer Datenbank hinzugefügt. Diese Klassen stellen den „Modell“-Teil der MVC-App dar.

Diese Modellklassen werden mit Entity Framework Core (EF Core) verwendet, um mit einer Datenbank zu arbeiten. EF Core ist ein ORM-Framework (Objektrelationales Mapping), das den Datenzugriffscode vereinfacht, den Sie schreiben müssen.

Die erstellten Modellklassen werden als POCO-Klassen bezeichnet (von Plain Old CLR Objects). POCO-Klassen weisen keine Abhängigkeit von EF Core auf. Sie definieren lediglich die Eigenschaften der Daten, die in der Datenbank gespeichert werden sollen.

In diesem Tutorial werden zuerst die Modellklassen erstellt. Anschließend erstellt EF Core die Datenbank.

Hinzufügen einer Datenmodellklasse

Klicken Sie mit der rechten Maustaste auf den Ordner Modelle>Hinzufügen>Klasse. Nennen Sie die Datei Movie.cs.

Aktualisieren Sie die Datei Models/Movie.cs mit dem folgenden Code:

using System.ComponentModel.DataAnnotations;

namespace MvcMovie.Models;

public class Movie
{
    public int Id { get; set; }
    public string? Title { get; set; }
    [DataType(DataType.Date)]
    public DateTime ReleaseDate { get; set; }
    public string? Genre { get; set; }
    public decimal Price { get; set; }
}

Die Movie-Klasse enthält ein Id-Feld, das von der Datenbank für den Primärschlüssel benötigt wird.

Das DataType-Attribut für ReleaseDate gibt den Typ der Daten (Date) an. Mit diesem Attribut:

  • Der Benutzer muss keine Zeitinformationen in das Datumsfeld eingeben.
  • Nur das Datum wird angezeigt, keine Zeitinformationen.

DataAnnotations werden in einem späteren Tutorial behandelt.

Das Fragezeichen nach string gibt an, dass die Eigenschaft Nullwerte zulässt. Weitere Informationen finden Sie unter Nullable-Verweistypen.

Hinzufügen von NuGet-Paketen

Visual Studio installiert automatisch die erforderlichen Pakete.

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

Strukturierung der Filmseiten

Verwenden Sie das Gerüstbauwerkzeug, um CRUD-Seiten (Create, Read, Update und Delete) für das Filmmodell zu erstellen.

Klicken Sie in Projektmappen-Explorer mit der rechten Maustaste auf den Ordner Controller, und wählen Sie Add > New Scaffolded Item aus.

Screenshot für oben genannten Schritt

Im Dialogfeld Neues Gerüstelement hinzufügen:

  • Wählen Sie im linken Bereich Installiert>Allgemein>MVC aus.
  • Wählen Sie MVC-Controller mit Ansichten unter Verwendung von Entity Framework aus.
  • Wählen Sie Hinzufügen aus.

Dialogfeld „Gerüst hinzufügen“

Vervollständigen Sie das Dialogfeld MVC-Controller mit Ansichten unter Verwendung von Entity Framework hinzufügen:

  • Wählen Sie in der Dropdownliste Modellklasse den Eintrag Movie (MvcMovie.Models) aus.
  • Wählen Sie in der Zeile Datenkontextklasse das +-Zeichen (Plus) aus.
    • Im Dialogfeld Datenkontext hinzufügen wird der Klassenname MvcMovie.Data.MvcMovieContext generiert.
    • Wählen Sie Hinzufügen aus.
  • Wählen Sie in der Dropdown-Liste DatenbankanbieterSQL Server aus.
  • Ansichten und Controllername: Belassen Sie den Standard.
  • Wählen Sie Hinzufügen aus.

Datenkontext hinzufügen: Standardwerte behalten

Wenn Sie eine Fehlermeldung erhalten, wählen Sie Hinzufügen ein zweites Mal aus, um es erneut zu versuchen.

Scaffolding fügt die folgenden Pakete hinzu:

  • Microsoft.EntityFrameworkCore.SqlServer
  • Microsoft.EntityFrameworkCore.Tools
  • Microsoft.VisualStudio.Web.CodeGeneration.Design

Das Gerüst erstellt Folgendes:

  • Ein Movies-Controller: Controllers/MoviesController.cs
  • Razor-Ansichtsdateien für die Seiten Erstellen, Löschen, Details, Bearbeiten und Index: Views/Movies/*.cshtml
  • Eine Datenbankkontext-Klasse: Data/MvcMovieContext.cs

Beim Gerüstbau werden folgende Aktualisierungen vorgenommen:

  • Die erforderlichen Paketverweise werden in die MvcMovie.csproj-Projektdatei eingefügt.
  • Registriert den Datenbankkontext in der Datei Program.cs.
  • Fügt der Datei appsettings.json eine Datenbank-Verbindungszeichenfolge hinzu.

Die automatische Erstellung dieser Dateien und Dateiaktualisierungen wird als Gerüstbau bezeichnet.

Sie können die Gerüstbauseiten noch nicht verwenden, da die Datenbank nicht vorhanden ist. Wenn Sie die App ausführen und den Link Movie App auswählen, wird die Fehlermeldung Die Datenbank kann nicht geöffnet werden oder Keine solche Tabelle: Movie angezeigt.

Erstellen Sie die App, um sicherzustellen, dass keine Fehler vorliegen.

Erste Migration

Verwenden Sie die Funktion EF CoreMigrationen, um die Datenbank zu erstellen. Bei Migrationen handelt es sich um mehrere Tools, mit denen Sie eine Datenbank dem Datenmodell entsprechend erstellen und aktualisieren können.

Wählen Sie im Menü Tools den NuGet Paket-Manager>Paket-Manager Console aus.

Geben Sie in der Paket-Manager Konsole (PMC) die folgenden Befehle ein:

Add-Migration InitialCreate
Update-Database

  • Add-Migration InitialCreate: Generiert die Migrationsdatei Migrations/{timestamp}_InitialCreate.cs. Das InitialCreate-Argument ist der Migrationsname. Es kann jeder Name verwendet werden, aber per Konvention wird ein Name ausgewählt, der die Migration beschreibt. Da dies die erste Migration ist, enthält die generierte Klasse Code zum Erstellen des Datenbankschemas. Das Datenbankschema basiert auf dem Modell, das in der Klasse MvcMovieContext angegeben ist.

  • Update-Database: Aktualisiert die Datenbank auf die neueste Migration, die der vorherige Befehl erstellt hat. Der Befehl führt die Up-Methode in der Datei Migrations/{time-stamp}_InitialCreate.cs aus, mit der die Datenbank erstellt wird.

Der Befehl Update-Database generiert folgende Warnung:

Für die Dezimaleigenschaft „Preis“ beim Entitätstyp „Movie“ wurde kein Speichertyp angegeben. Dadurch werden Werte ohne Benachrichtigung abgeschnitten, wenn sie nicht in die Standardgenauigkeit und -skalierung passen. Geben Sie den Spaltentyp von SQL-Server explizit an, der durch „HasColumnType“ sämtliche Werte in „OnModelCreating“ unterstützen kann. Geben Sie Genauigkeit und Skalierung mithilfe von „HasPrecision“ an, oder konfigurieren Sie mithilfe von „HasConversion“ einen Wertkonverter.

Ignorieren Sie die obige Warnung, sie wird in einem späteren Tutorial behoben.

Weitere Informationen zu den PMC-Tools für EF Core finden Sie unter EF Core Tools reference - PMC in Visual Studio.

Testen der App

Führen Sie die APP aus, und wählen Sie den Link Movie App aus.

Wenn Sie eine ähnliche Ausnahme wie die folgende erhalten, haben Sie möglicherweise den Update-Database-Befehl im Migrationsschritt nicht durchgeführt:

SqlException: Cannot open database "MvcMovieContext-1" requested by the login. The login failed.

Note

Sie können unter Umständen in das Feld Price keine Kommas als Dezimaltrennzeichen eingeben. Zur Unterstützung der jQuery-Validierung für Gebietsschemas mit einer anderen Sprache als Englisch, in denen ein Komma („,“) als Dezimaltrennzeichen verwendet wird, und für Datums- und Uhrzeitformate, die nicht dem US-englischen Format entsprechen, muss die App globalisiert werden. Anweisungen zur Globalisierung finden Sie unter dieses GitHub Problem.

Untersuchen der generierten Datenbankkontextklasse und -registrierung

Bei EF Core erfolgt der Datenzugriff über ein Modell. Ein Modell setzt sich aus Entitätsklassen und einem Kontextobjekt zusammen, das eine Sitzung mit der Datenbank darstellt. Das Kontextobjekt ermöglicht das Abfragen und Speichern von Daten. Der Datenbankkontext wird von Microsoft abgeleitet. EntityFrameworkCore.DbContext und gibt die Entitäten an, die in das Datenmodell aufgenommen werden sollen.

Durch das Scaffolding wird die Datenbankkontextklasse Data/MvcMovieContext.cs erstellt:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using MvcMovie.Models;

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

        public DbSet<MvcMovie.Models.Movie> Movie { get; set; }
    }
}

Der obige Code erstellt eine DbSet<Movie>-Eigenschaft, die die Filme in der Datenbank darstellt.

Abhängigkeitsinjektion

ASP.NET Core wird mit Dependency Injection (DI) erstellt. Dienste, wie der Datenbankkontext, sind bei DI in Program.cs registriert. Diese Dienste werden den Komponenten, die sie benötigen, über Konstruktorparameter zur Verfügung gestellt.

Der Konstruktor in der Datei Controllers/MoviesController.cs verwendet die Abhängigkeitsinjektion zum Einfügen des Datenbankkontexts (MvcMovieContext) in den Controller. Der Datenbankkontext wird in den einzelnen CRUD-Methoden im Controller verwendet.

Beim Gerüstbau wurde der folgende hervorgehobene Code in Program.cs erzeugt:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddDbContext<MvcMovieContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("MvcMovieContext")));

Das ASP.NET Core-Konfigurationssystem liest die Verbindungszeichenfolge der "MvcMovieContext"-Datenbank.

Überprüfen Sie die generierte Datenbank-Verbindungszeichenfolge

Das Gerüst hat der Datei appsettings.json ein Verbindungszeichenfolge hinzugefügt:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "MvcMovieContext": "Data Source=MvcMovieContext-ea7a4069-f366-4742-bd1c-3f753a804ce1.db"
  }
}

Für die lokale Entwicklung liest das konfigurationssystem ASP.NET Core den Schlüssel ConnectionString aus der Datei appsettings.json.

Die InitialCreate-Klasse

Untersuchen Sie die Migrationsdatei Migrations/{timestamp}_InitialCreate.cs:

using System;
using Microsoft.EntityFrameworkCore.Migrations;

#nullable disable

namespace MvcMovie.Migrations
{
    public partial class InitialCreate : Migration
    {
        protected override void Up(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.CreateTable(
                name: "Movie",
                columns: table => new
                {
                    Id = table.Column<int>(type: "int", nullable: false)
                        .Annotation("SqlServer:Identity", "1, 1"),
                    Title = table.Column<string>(type: "nvarchar(max)", nullable: true),
                    ReleaseDate = table.Column<DateTime>(type: "datetime2", nullable: false),
                    Genre = table.Column<string>(type: "nvarchar(max)", nullable: true),
                    Price = table.Column<decimal>(type: "decimal(18,2)", nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_Movie", x => x.Id);
                });
        }

        protected override void Down(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.DropTable(
                name: "Movie");
        }
    }
}

Im vorangehenden Code:

  • InitialCreate.Up erstellt die Movie-Tabelle und konfiguriert Id als Primärschlüssel.
  • InitialCreate.Down macht die von der Up-Migration vorgenommenen Schemaänderungen rückgängig.

Abhängigkeitsinjektion im Controller

Öffnen Sie die Datei Controllers/MoviesController.cs, und überprüfen Sie den Konstruktor:

public class MoviesController : Controller
{
    private readonly MvcMovieContext _context;

    public MoviesController(MvcMovieContext context)
    {
        _context = context;
    }

Der Konstruktor verwendet die Abhängigkeitsinjektion zum Einfügen des Datenbankkontexts (MvcMovieContext) in den Controller. Der Datenbankkontext wird in den einzelnen CRUD-Methoden im Controller verwendet.

Testen Sie die Seite Create (Erstellen). Geben Sie die Daten ein, und senden Sie sie.

Testen Sie die Seiten Edit (Bearbeiten), Details und Delete (Löschen).

Stark typisierte Modelle und die @model-Direktive

Weiter oben in diesem Tutorial haben Sie gesehen, wie ein Controller mithilfe des Wörterbuchs ViewData Daten oder Objekte an eine Ansicht übergeben kann. Das Wörterbuch ViewData ist ein dynamisches Objekt, das eine praktische Möglichkeit bietet, um Informationen spätgebunden an eine Ansicht zu übergeben.

MVC bietet die Möglichkeit, stark typisierte Modellobjekte an eine Ansicht zu übergeben. Dieser stark typisierte Ansatz ermöglicht eine Überprüfung Ihres Codes zur Kompilierzeit. Der Gerüstmechanismus hat ein stark typisiertes Modell an die MoviesController-Klasse und Ansichten übergeben.

Untersuchen Sie die generierte Details-Methode in der Datei Controllers/MoviesController.cs:

// GET: Movies/Details/5
public async Task<IActionResult> Details(int? id)
{
    if (id == null)
    {
        return NotFound();
    }

    var movie = await _context.Movie
        .FirstOrDefaultAsync(m => m.Id == id);
    if (movie == null)
    {
        return NotFound();
    }

    return View(movie);
}

Der id-Parameter wird im Allgemeinen als Routendaten übergeben. https://localhost:5001/movies/details/1 legt beispielsweise Folgendes fest:

  • Den Controller dem Controller movies zuordnen, dem ersten URL-Segment.
  • Die Aktion auf details (das zweite URL-Segment).
  • Der id wird auf 1 gesetzt, das letzte URL-Segment.

id kann wie im folgenden Beispiel mit einer Abfragezeichenfolge übergeben werden:

https://localhost:5001/movies/details?id=1

Der id-Parameter wird als Nullable-Typ (int?) für den Fall definiert, dass kein id-Wert angegeben wird.

Ein Lambdaausdruck wird an die FirstOrDefaultAsync-Methode übergeben, um Movie-Entitäten auszuwählen, die mit den Routendaten oder dem Wert der Abfragezeichenfolge übereinstimmen.

var movie = await _context.Movie
    .FirstOrDefaultAsync(m => m.Id == id);

Wenn ein Film gefunden wird, wird eine Instanz des Movie-Modells an die Ansicht Details übergeben:

return View(movie);

Untersuchen Sie den Inhalt der Datei Views/Movies/Details.cshtml:

@model MvcMovie.Models.Movie

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

<h1>Details</h1>

<div>
    <h4>Movie</h4>
    <hr />
    <dl class="row">
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Title)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Title)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.ReleaseDate)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.ReleaseDate)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Genre)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Genre)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Price)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Price)
        </dd>
    </dl>
</div>
<div>
    <a asp-action="Edit" asp-route-id="@Model.Id">Edit</a> |
    <a asp-action="Index">Back to List</a>
</div>

Die @model-Anweisung am Anfang der Ansichtsdatei gibt den Typ des Objekts an, den die Ansicht erwartet. Beim Erstellen des Movie-Controllers wurde die folgende @model-Anweisung eingeschlossen:

@model MvcMovie.Models.Movie

Diese @model-Direktive ermöglicht den Zugriff auf das Video, das der Controller an die Ansicht übergeben hat. Das Model-Objekt ist stark typisiert. In der Ansicht Details.cshtml übergibt der Code jedes Filmfeld an die HTML-Hilfsprogramme DisplayNameFor und DisplayFor mit dem stark typisierten Model-Objekt. Die Methoden Create und Edit und Ansichten übergeben außerdem ein Movie-Modelobjekt.

Untersuchen Sie die Ansicht Index.cshtml und die Index-Methode im Movies-Controller. Beachten Sie, wie der Code beim Aufrufen der List-Methode ein View-Objekt erstellt. Der Code übergibt diese Liste Movies aus der Aktionsmethode Index an die Ansicht:

// GET: Movies
public async Task<IActionResult> Index()
{
    return View(await _context.Movie.ToListAsync());
}

Der Code gibt Problemdetails zurück, wenn die Movie-Eigenschaft des Datenkontexts NULL ist.

Als der Movies-Controller erstellt wurde, hat das Scaffolding automatisch die folgende @model-Anweisung oben in der Datei Index.cshtml hinzugefügt.

@model IEnumerable<MvcMovie.Models.Movie>

Die @model-Direktive ermöglicht den Zugriff auf die Liste der Filme, die der Controller an die Ansicht übergeben hat, indem ein stark typisiertes Model-Objekt verwendet wird. In der Ansicht Index.cshtml durchläuft der Code z. B. die Filme mithilfe einer foreach-Anweisung für das stark typisierte Model-Objekt in einer Schleife:

@model IEnumerable<MvcMovie.Models.Movie>

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

<h1>Index</h1>

<p>
    <a asp-action="Create">Create New</a>
</p>
<table class="table">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.Title)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.ReleaseDate)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Genre)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Price)
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
@foreach (var item in Model) {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.Title)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.ReleaseDate)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Genre)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Price)
            </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>

Da das Model-Objekt stark typisiert ist als IEnumerable<Movie>-Objekt, ist jedes Element in der Schleife vom Typ Movie. Neben anderen Vorteilen überprüft der Compiler die im Code verwendeten Typen.

Weitere Ressourcen

In diesem Tutorial werden Klassen für die Verwaltung von Filmen in einer Datenbank hinzugefügt. Diese Klassen stellen den „Modell“-Teil der MVC-App dar.

Diese Modellklassen werden mit Entity Framework Core (EF Core) verwendet, um mit einer Datenbank zu arbeiten. EF Core ist ein ORM-Framework (Objektrelationales Mapping), das den Datenzugriffscode vereinfacht, den Sie schreiben müssen.

Die erstellten Modellklassen werden als POCO-Klassen bezeichnet (von Plain Old CLR Objects). POCO-Klassen weisen keine Abhängigkeit von EF Core auf. Sie definieren lediglich die Eigenschaften der Daten, die in der Datenbank gespeichert werden sollen.

In diesem Tutorial werden zuerst die Modellklassen erstellt. Anschließend erstellt EF Core die Datenbank.

Hinzufügen einer Datenmodellklasse

Klicken Sie mit der rechten Maustaste auf den Ordner Modelle>Hinzufügen>Klasse. Nennen Sie die Datei Movie.cs.

Aktualisieren Sie die Datei Models/Movie.cs mit dem folgenden Code:

using System.ComponentModel.DataAnnotations;

namespace MvcMovie.Models;

public class Movie
{
    public int Id { get; set; }
    public string? Title { get; set; }
    [DataType(DataType.Date)]
    public DateTime ReleaseDate { get; set; }
    public string? Genre { get; set; }
    public decimal Price { get; set; }
}

Die Movie-Klasse enthält ein Id-Feld, das von der Datenbank für den Primärschlüssel benötigt wird.

Das DataType-Attribut für ReleaseDate gibt den Typ der Daten (Date) an. Mit diesem Attribut:

  • Der Benutzer muss keine Zeitinformationen in das Datumsfeld eingeben.
  • Nur das Datum wird angezeigt, keine Zeitinformationen.

DataAnnotations werden in einem späteren Tutorial behandelt.

Das Fragezeichen nach string gibt an, dass die Eigenschaft Nullwerte zulässt. Weitere Informationen finden Sie unter Nullable-Verweistypen.

Hinzufügen von NuGet-Paketen

Visual Studio installiert automatisch die erforderlichen Pakete.

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

Erstellung der Filmseiten

Verwenden Sie das Tool für den Gerüstbau, um Create, Read, Update und Delete (CRUD)-Seiten für das Film-Modell zu erstellen.

Klicken Sie in Projektmappen-Explorer mit der rechten Maustaste auf den Ordner Controller, und wählen Sie Add > New Scaffolded Item aus.

Screenshot für oben genannten Schritt

Im Dialogfeld Neues Gerüstelement hinzufügen:

  • Wählen Sie im linken Bereich Installiert>Allgemein>MVC aus.
  • Wählen Sie MVC-Controller mit Ansichten unter Verwendung von Entity Framework aus.
  • Wählen Sie Hinzufügen aus.

Dialogfeld „Gerüst hinzufügen“

Vervollständigen Sie das Dialogfeld MVC-Controller mit Ansichten unter Verwendung von Entity Framework hinzufügen:

  • Wählen Sie in der Dropdownliste Modellklasse den Eintrag Movie (MvcMovie.Models) aus.
  • Wählen Sie in der Zeile Datenkontextklasse das +-Zeichen (Plus) aus.
    • Im Dialogfeld Datenkontext hinzufügen wird der Klassenname MvcMovie.Data.MvcMovieContext generiert.
    • Wählen Sie Hinzufügen aus.
  • Wählen Sie in der Datenbankanbieter-Dropdown-Liste SQL Server aus.
  • Ansichten und Controllername: Übernehmen Sie die Standardeinstellungen.
  • Wählen Sie Hinzufügen aus.

Datenkontext hinzufügen: Standardwerte beibehalten Wenn Sie eine Fehlermeldung erhalten, wählen Sie ein zweites Mal Hinzufügen aus, um es erneut zu versuchen.

Das Gerüst fügt die folgenden Pakete hinzu:

  • Microsoft.EntityFrameworkCore.SqlServer
  • Microsoft.EntityFrameworkCore.Tools
  • Microsoft.VisualStudio.Web.CodeGeneration.Design

Beim Gerüstbau wird Folgendes erstellt:

  • Ein Film-Controller: Controllers/MoviesController.cs
  • Razor-Ansichtsdateien für die Seiten Erstellen, Löschen, Details, Bearbeiten und Index: Views/Movies/*.cshtml
  • Eine Datenbankkontext-Klasse: Data/MvcMovieContext.cs

Beim Gerüstbau werden folgende Aktualisierungen vorgenommen:

  • Die erforderlichen Paketverweise werden in die MvcMovie.csproj-Projektdatei eingefügt.
  • Registriert den Datenbankkontext in der Datei Program.cs.
  • Fügt der Datei appsettings.json eine Datenbank-Verbindungszeichenfolge hinzu.

Die automatische Erstellung dieser Dateien und Dateiaktualisierungen wird als Gerüstbau bezeichnet.

Sie können die Gerüstbauseiten noch nicht verwenden, da die Datenbank nicht vorhanden ist. Wenn Sie die App ausführen und den Link Movie App auswählen, wird die Fehlermeldung Die Datenbank kann nicht geöffnet werden oder Keine solche Tabelle: Movie angezeigt.

Erstellen Sie die App, um sicherzustellen, dass keine Fehler vorliegen.

Erste Migration

Verwenden Sie die Funktion EF Core'Migrationen', um die Datenbank zu erstellen. Bei Migrationen handelt es sich um mehrere Tools, mit denen Sie eine Datenbank dem Datenmodell entsprechend erstellen und aktualisieren können.

Wählen Sie im Menü Werkzeuge den NuGet Paket-Manager> und die Paket-Manager Console aus.

Geben Sie in der Paket-Manager Konsole (PMC) die folgenden Befehle ein:

Add-Migration InitialCreate
Update-Database

  • Add-Migration InitialCreate: Generiert die Migrationsdatei Migrations/{timestamp}_InitialCreate.cs. Das InitialCreate-Argument ist der Migrationsname. Es kann jeder Name verwendet werden, aber per Konvention wird ein Name ausgewählt, der die Migration beschreibt. Da dies die erste Migration ist, enthält die generierte Klasse Code zum Erstellen des Datenbankschemas. Das Datenbankschema basiert auf dem Modell, das in der Klasse MvcMovieContext angegeben ist.

  • Update-Database: Aktualisiert die Datenbank auf die neueste Migration, die der vorherige Befehl erstellt hat. Der Befehl führt die Up-Methode in der Datei Migrations/{time-stamp}_InitialCreate.cs aus, mit der die Datenbank erstellt wird.

Der Befehl Update-Database generiert folgende Warnung:

Für die Dezimalspalte 'Price' für den Entitätstyp 'Movie' wurde kein Typ angegeben. Dadurch werden Werte stillschweigend abgeschnitten, falls diese nicht der Standardpräzision und -skala entsprechen. Geben Sie explizit den SQL Server-Spaltentyp an, der alle Werte mit 'HasColumnType()' aufnehmen kann. (Für die Spalte „Price“ mit Dezimalwerten des Entitätstyps „Movie“ wurde kein Typ angegeben. Dadurch werden Werte automatisch abgeschnitten, falls diese nicht der Standardgenauigkeit und -skalierung entsprechen. Geben Sie explizit den SQL Server-Spaltentyp an, der alle Werte unterstützt.)

Ignorieren Sie die obige Warnung, sie wird in einem späteren Tutorial behoben.

Weitere Informationen zu den PMC-Tools für EF Core finden Sie unter EF Core Tools reference - PMC in Visual Studio.

Testen der App

Führen Sie die APP aus, und wählen Sie den Link Movie App aus.

Wenn Sie eine ähnliche Ausnahme wie die folgende erhalten, haben Sie möglicherweise den Update-Database-Befehl im Migrationsschritt nicht durchgeführt:

SqlException: Cannot open database "MvcMovieContext-1" requested by the login. The login failed.

Note

Sie können unter Umständen in das Feld Price keine Kommas als Dezimaltrennzeichen eingeben. Zur Unterstützung der jQuery-Validierung für Gebietsschemas mit einer anderen Sprache als Englisch, in denen ein Komma („,“) als Dezimaltrennzeichen verwendet wird, und für Datums- und Uhrzeitformate, die nicht dem US-englischen Format entsprechen, muss die App globalisiert werden. Anweisungen zur Globalisierung finden Sie unter dieses GitHub Problem.

Untersuchen der generierten Datenbankkontextklasse und -registrierung

Bei EF Core erfolgt der Datenzugriff über ein Modell. Ein Modell setzt sich aus Entitätsklassen und einem Kontextobjekt zusammen, das eine Sitzung mit der Datenbank darstellt. Das Kontextobjekt ermöglicht das Abfragen und Speichern von Daten. Der Datenbankkontext wird von Microsoft abgeleitet. EntityFrameworkCore.DbContext und gibt die Entitäten an, die in das Datenmodell aufgenommen werden sollen.

Beim Scaffolding-Prozess wird die Datenbankkontextklasse Data/MvcMovieContext.cs erstellt.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using MvcMovie.Models;

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

        public DbSet<MvcMovie.Models.Movie> Movie { get; set; }
    }
}

Der obige Code erstellt eine DbSet<Movie>-Eigenschaft, die die Filme in der Datenbank darstellt.

Abhängigkeitsinjektion

ASP.NET Core wird mit Dependency Injection (DI) erstellt. Dienste, wie der Datenbankkontext, werden bei DI in Program.cs registriert. Diese Dienste werden den Komponenten, die sie benötigen, über Konstruktorparameter zur Verfügung gestellt.

Der Konstruktor in der Datei Controllers/MoviesController.cs verwendet die Abhängigkeitsinjektion zum Einfügen des Datenbankkontexts (MvcMovieContext) in den Controller. Der Datenbankkontext wird in den einzelnen CRUD-Methoden im Controller verwendet.

Beim Gerüstbau wurde der folgende hervorgehobene Code in Program.cs erzeugt:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddDbContext<MvcMovieContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("MvcMovieContext")));

Das ASP.NET Core-Konfigurationssystem liest die Verbindungszeichenfolge der Datenbank "MvcMovieContext".

Überprüfen der generierten Datenbank-Verbindungszeichenfolge

Das Gerüst hat der Datei appsettings.json ein Verbindungszeichenfolge hinzugefügt:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "MvcMovieContext": "Data Source=MvcMovieContext-ea7a4069-f366-4742-bd1c-3f753a804ce1.db"
  }
}

Für die lokale Entwicklung liest das konfigurationssystem ASP.NET Core den Schlüssel ConnectionString aus der Datei appsettings.json.

Die InitialCreate-Klasse

Untersuchen Sie die Migrationsdatei Migrations/{timestamp}_InitialCreate.cs:

using System;
using Microsoft.EntityFrameworkCore.Migrations;

#nullable disable

namespace MvcMovie.Migrations
{
    public partial class InitialCreate : Migration
    {
        protected override void Up(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.CreateTable(
                name: "Movie",
                columns: table => new
                {
                    Id = table.Column<int>(type: "int", nullable: false)
                        .Annotation("SqlServer:Identity", "1, 1"),
                    Title = table.Column<string>(type: "nvarchar(max)", nullable: true),
                    ReleaseDate = table.Column<DateTime>(type: "datetime2", nullable: false),
                    Genre = table.Column<string>(type: "nvarchar(max)", nullable: true),
                    Price = table.Column<decimal>(type: "decimal(18,2)", nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_Movie", x => x.Id);
                });
        }

        protected override void Down(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.DropTable(
                name: "Movie");
        }
    }
}

Im vorstehenden Code:

  • InitialCreate.Up erstellt die Movie-Tabelle und konfiguriert Id als Primärschlüssel.
  • InitialCreate.Down macht die von der Up-Migration vorgenommenen Schemaänderungen rückgängig.

Abhängigkeitsinjektion im Controller

Öffnen Sie die Datei Controllers/MoviesController.cs, und überprüfen Sie den Konstruktor:

public class MoviesController : Controller
{
    private readonly MvcMovieContext _context;

    public MoviesController(MvcMovieContext context)
    {
        _context = context;
    }

Der Konstruktor verwendet die Abhängigkeitsinjektion zum Einfügen des Datenbankkontexts (MvcMovieContext) in den Controller. Der Datenbankkontext wird in den einzelnen CRUD-Methoden im Controller verwendet.

Testen Sie die Seite Create (Erstellen). Geben Sie die Daten ein, und senden Sie sie.

Testen Sie die Seiten Edit (Bearbeiten), Details und Delete (Löschen).

Stark typisierte Modelle und die @model-Direktive

Weiter oben in diesem Tutorial haben Sie gesehen, wie ein Controller mithilfe des Wörterbuchs ViewData Daten oder Objekte an eine Ansicht übergeben kann. Das Wörterbuch ViewData ist ein dynamisches Objekt, das eine praktische Möglichkeit bietet, um Informationen spätgebunden an eine Ansicht zu übergeben.

MVC bietet die Möglichkeit, stark typisierte Modellobjekte an eine Ansicht zu übergeben. Dieser stark typisierte Ansatz ermöglicht eine Überprüfung Ihres Codes zur Kompilierzeit. Der Gerüstmechanismus hat ein stark typisiertes Modell an die MoviesController-Klasse und Ansichten übergeben.

Untersuchen Sie die generierte Details-Methode in der Datei Controllers/MoviesController.cs:

// GET: Movies/Details/5
public async Task<IActionResult> Details(int? id)
{
    if (id == null)
    {
        return NotFound();
    }

    var movie = await _context.Movie
        .FirstOrDefaultAsync(m => m.Id == id);
    if (movie == null)
    {
        return NotFound();
    }

    return View(movie);
}

Der id-Parameter wird im Allgemeinen als Routendaten übergeben. https://localhost:5001/movies/details/1 legt beispielsweise Folgendes fest:

  • Den Controller dem Controller movies zuordnen, dem ersten URL-Segment.
  • Die Aktion auf details (das zweite URL-Segment).
  • Der id wird auf 1 gesetzt, das letzte URL-Segment.

id kann wie im folgenden Beispiel mit einer Abfragezeichenfolge übergeben werden:

https://localhost:5001/movies/details?id=1

Der id-Parameter wird als Nullable-Typ (int?) für den Fall definiert, dass kein id-Wert angegeben wird.

Ein Lambdaausdruck wird an die FirstOrDefaultAsync-Methode übergeben, um Movie-Entitäten auszuwählen, die mit den Routendaten oder dem Wert der Abfragezeichenfolge übereinstimmen.

var movie = await _context.Movie
    .FirstOrDefaultAsync(m => m.Id == id);

Wenn ein Film gefunden wird, wird eine Instanz des Movie-Modells an die Ansicht Details übergeben:

return View(movie);

Untersuchen Sie den Inhalt der Datei Views/Movies/Details.cshtml:

@model MvcMovie.Models.Movie

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

<h1>Details</h1>

<div>
    <h4>Movie</h4>
    <hr />
    <dl class="row">
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Title)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Title)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.ReleaseDate)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.ReleaseDate)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Genre)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Genre)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Price)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Price)
        </dd>
    </dl>
</div>
<div>
    <a asp-action="Edit" asp-route-id="@Model.Id">Edit</a> |
    <a asp-action="Index">Back to List</a>
</div>

Die @model-Anweisung am Anfang der Ansichtsdatei gibt den Typ des Objekts an, den die Ansicht erwartet. Beim Erstellen des Movie-Controllers wurde die folgende @model-Anweisung eingeschlossen:

@model MvcMovie.Models.Movie

Diese @model-Direktive ermöglicht den Zugriff auf das Video, das der Controller an die Ansicht übergeben hat. Das Model-Objekt ist stark typisiert. In der Ansicht Details.cshtml übergibt der Code jedes Filmfeld an die HTML-Hilfsprogramme DisplayNameFor und DisplayFor mit dem stark typisierten Model-Objekt. Die Methoden Create und Edit und Ansichten übergeben außerdem ein Movie-Modelobjekt.

Untersuchen Sie die Ansicht Index.cshtml und die Index-Methode im Movies-Controller. Beachten Sie, wie der Code beim Aufrufen der List-Methode ein View-Objekt erstellt. Der Code übergibt diese Liste Movies aus der Aktionsmethode Index an die Ansicht:

// GET: Movies
public async Task<IActionResult> Index()
{
    return View(await _context.Movie.ToListAsync());
}

Der Code gibt Problemdetails zurück, wenn die Movie-Eigenschaft des Datenkontexts NULL ist.

Als der Movies-Controller erstellt wurde, hat das Scaffolding automatisch die folgende @model-Anweisung oben in der Datei Index.cshtml hinzugefügt.

@model IEnumerable<MvcMovie.Models.Movie>

Die @model-Direktive ermöglicht den Zugriff auf die Liste der Filme, die der Controller an die Ansicht übergeben hat, indem ein stark typisiertes Model-Objekt verwendet wird. In der Ansicht Index.cshtml durchläuft der Code z. B. die Filme mithilfe einer foreach-Anweisung für das stark typisierte Model-Objekt in einer Schleife:

@model IEnumerable<MvcMovie.Models.Movie>

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

<h1>Index</h1>

<p>
    <a asp-action="Create">Create New</a>
</p>
<table class="table">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.Title)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.ReleaseDate)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Genre)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Price)
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
@foreach (var item in Model) {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.Title)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.ReleaseDate)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Genre)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Price)
            </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>

Da das Model-Objekt stark typisiert ist als IEnumerable<Movie>-Objekt, ist jedes Element in der Schleife vom Typ Movie. Neben anderen Vorteilen überprüft der Compiler die im Code verwendeten Typen.

Weitere Ressourcen

In diesem Tutorial werden Klassen für die Verwaltung von Filmen in einer Datenbank hinzugefügt. Diese Klassen stellen den „Modell“-Teil der MVC-App dar.

Diese Modellklassen werden mit Entity Framework Core (EF Core) verwendet, um mit einer Datenbank zu arbeiten. EF Core ist ein ORM-Framework (Objektrelationales Mapping), das den Datenzugriffscode vereinfacht, den Sie schreiben müssen.

Die erstellten Modellklassen werden als POCO-Klassen bezeichnet (von Plain Old CLR Objects). POCO-Klassen weisen keine Abhängigkeit von EF Core auf. Sie definieren lediglich die Eigenschaften der Daten, die in der Datenbank gespeichert werden sollen.

In diesem Tutorial werden zuerst die Modellklassen erstellt. Anschließend erstellt EF Core die Datenbank.

Hinzufügen einer Datenmodellklasse

Klicken Sie mit der rechten Maustaste auf den Ordner Modelle>Hinzufügen>Klasse. Nennen Sie die Datei Movie.cs.

Aktualisieren Sie die Datei Models/Movie.cs mit dem folgenden Code:

using System.ComponentModel.DataAnnotations;

namespace MvcMovie.Models
{
    public class Movie
    {
        public int Id { get; set; }
        public string? Title { get; set; }

        [DataType(DataType.Date)]
        public DateTime ReleaseDate { get; set; }
        public string? Genre { get; set; }
        public decimal Price { get; set; }
    }
}

Die Movie-Klasse enthält ein Id-Feld, das von der Datenbank für den Primärschlüssel benötigt wird.

Das DataType-Attribut für ReleaseDate gibt den Typ der Daten (Date) an. Mit diesem Attribut:

  • Der Benutzer muss keine Zeitinformationen in das Datumsfeld eingeben.
  • Nur das Datum wird angezeigt, keine Zeitinformationen.

DataAnnotations werden in einem späteren Tutorial behandelt.

Das Fragezeichen nach string zeigt an, dass die Eigenschaft null sein kann. Weitere Informationen finden Sie unter Nullable-Verweistypen.

Hinzufügen von NuGet-Paketen

Wählen Sie im Menü ToolsNuGet Paket-Manager>Paket-Manager Console (PMC) aus.

PMC-Menü

Führen Sie den folgenden Befehl in der PMC aus:

Install-Package Microsoft.EntityFrameworkCore.Design
Install-Package Microsoft.EntityFrameworkCore.SqlServer

Die oben aufgeführten Befehle fügen Folgendes hinzu:

  • Der SQL Server-Anbieter EF Core. Das Anbieterpaket installiert das EF Core-Paket als Abhängigkeit.
  • Die von den Paketen verwendeten Hilfsprogramme werden im weiteren Verlauf des Tutorials automatisch im Gerüstbauschritt installiert.

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

Strukturvorlage der Filmseiten

Verwenden Sie das Tool für den Gerüstbau, um CRUD-Seiten (Create, Read, Update und Delete) für das Movie-Modell zu erstellen.

Klicken Sie in Projektmappen-Explorer mit der rechten Maustaste auf den Ordner Controller, und wählen Sie Add > New Scaffolded Item aus.

Screenshot für oben genannten Schritt

Wählen Sie im Dialogfeld Gerüst hinzufügen die Option MVC-Controller mit Ansichten unter Verwendung von Entity Framework > Hinzufügen aus.

Dialogfeld „Gerüst hinzufügen“

Vervollständigen Sie das Dialogfeld MVC-Controller mit Ansichten unter Verwendung von Entity Framework hinzufügen:

  • Wählen Sie in der Dropdownliste Modellklasse den Eintrag Movie (MvcMovie.Models) aus.
  • Wählen Sie in der Zeile Datenkontextklasse das +-Zeichen (Plus) aus.
    • Im Dialogfeld Datenkontext hinzufügen wird der Klassenname MvcMovie.Data.MvcMovieContext generiert.
    • Wählen Sie Hinzufügen aus.
  • Ansichten und Controllername: Übernehmen Sie die Standardeinstellung.
  • Wählen Sie Hinzufügen aus.

Datenkontext hinzufügen: Standardwerte behalten

Wenn Sie eine Fehlermeldung erhalten, wählen Sie Hinzufügen ein zweites Mal aus, um es erneut zu versuchen.

Beim Gerüstbau werden folgende Aktualisierungen vorgenommen:

  • Die erforderlichen Paketverweise werden in die MvcMovie.csproj-Projektdatei eingefügt.
  • Registriert den Datenbankkontext in der Datei Program.cs.
  • Fügt eine Verbindungszeichenfolge der Datenbank in der Datei appsettings.json hinzu.

Beim Gerüstbau wird Folgendes erstellt:

  • Ein Movies-Controller: Controllers/MoviesController.cs
  • Razor-Ansichtsdateien für die Seiten Erstellen, Löschen, Details, Bearbeiten und Index: Views/Movies/*.cshtml
  • Eine Datenbankkontext-Klasse: Data/MvcMovieContext.cs

Die automatische Erstellung dieser Dateien und Dateiaktualisierungen wird als Gerüstbau bezeichnet.

Sie können die Gerüstseiten noch nicht verwenden, weil die Datenbank noch nicht existiert. Wenn Sie die App ausführen und den Link Movie App auswählen, wird die Fehlermeldung Die Datenbank kann nicht geöffnet werden oder Keine solche Tabelle: Movie angezeigt.

Erstellen der App

Erstellen Sie die App. Der Compiler generiert mehrere Warnungen zur Behandlung von null-Werten. Weitere Informationen finden Sie unter dieses GitHub problem und Nullable reference types.

Entfernen Sie die folgende Zeile aus der Datei MvcMovie.csproj, um Warnungen bei nullbaren Referenztypen zu vermeiden.

<Nullable>enable</Nullable>

Dieser Problem sollte im nächsten Release behoben sein.

Erste Migration

Verwenden Sie das Feature EF CoreMigrationen, um die Datenbank zu erstellen. Bei Migrationen handelt es sich um mehrere Tools, mit denen Sie eine Datenbank dem Datenmodell entsprechend erstellen und aktualisieren können.

Wählen Sie im Menü ToolsNuGet Paket-Manager>Paket-Manager Console aus.

Geben Sie in der Paket-Manager Konsole (PMC) die folgenden Befehle ein:

Add-Migration InitialCreate
Update-Database

  • Add-Migration InitialCreate: Generiert die Migrationsdatei Migrations/{timestamp}_InitialCreate.cs. Das InitialCreate-Argument ist der Migrationsname. Es kann jeder Name verwendet werden, aber per Konvention wird ein Name ausgewählt, der die Migration beschreibt. Da dies die erste Migration ist, enthält die generierte Klasse Code zum Erstellen des Datenbankschemas. Das Datenbankschema basiert auf dem Modell, das in der Klasse MvcMovieContext angegeben ist.

  • Update-Database: Aktualisiert die Datenbank auf die neueste Migration, die der vorherige Befehl erstellt hat. Der Befehl führt die Up-Methode in der Datei Migrations/{time-stamp}_InitialCreate.cs aus, mit der die Datenbank erstellt wird.

Der Befehl Update-Database generiert folgende Warnung:

Für die Dezimalspalte 'Price' für den Entitätstyp 'Movie' wurde kein Typ angegeben. Dadurch werden Werte ohne Benachrichtigung abgeschnitten, wenn sie nicht in die Standardgenauigkeit und -skalierung passen. Spezifizieren Sie explizit den SQL Server-Spaltentyp, der alle Werte unter Verwendung von 'HasColumnType()' aufnehmen kann.

Ignorieren Sie die obige Warnung, sie wird in einem späteren Tutorial behoben.

Weitere Informationen zu den PMC-Tools für EF Core finden Sie unter EF Core Tools reference - PMC in Visual Studio.

Testen der App

Führen Sie die APP aus, und wählen Sie den Link Movie App aus.

Wenn Sie eine ähnliche Ausnahme wie die folgende erhalten, haben Sie möglicherweise den Migrationsschritt nicht durchgeführt:

SqlException: Cannot open database "MvcMovieContext-1" requested by the login. The login failed.

Note

Sie können unter Umständen in das Feld Price keine Kommas als Dezimaltrennzeichen eingeben. Zur Unterstützung der jQuery-Validierung für Gebietsschemas mit einer anderen Sprache als Englisch, in denen ein Komma („,“) als Dezimaltrennzeichen verwendet wird, und für Datums- und Uhrzeitformate, die nicht dem US-englischen Format entsprechen, muss die App globalisiert werden. Anweisungen zur Globalisierung finden Sie unter dieses GitHub Problem.

Untersuchen der generierten Datenbankkontextklasse und -registrierung

Bei EF Core erfolgt der Datenzugriff über ein Modell. Ein Modell setzt sich aus Entitätsklassen und einem Kontextobjekt zusammen, das eine Sitzung mit der Datenbank darstellt. Das Kontextobjekt ermöglicht das Abfragen und Speichern von Daten. Der Datenbankkontext wird von Microsoft abgeleitet. EntityFrameworkCore.DbContext und gibt die Entitäten an, die in das Datenmodell aufgenommen werden sollen.

Beim Scaffolding-Prozess wird die Datenbankkontextklasse Data/MvcMovieContext.cs erstellt.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using MvcMovie.Models;

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

        public DbSet<MvcMovie.Models.Movie> Movie { get; set; }
    }
}

Der obige Code erstellt eine DbSet<Movie>-Eigenschaft, die die Filme in der Datenbank darstellt.

Abhängigkeitsinjektion

ASP.NET Core wird mit Dependency Injection (DI) erstellt. Dienste, wie der Datenbankkontext, werden bei DI in Program.cs registriert. Diese Dienste werden den Komponenten, die sie benötigen, über Konstruktorparameter zur Verfügung gestellt.

Der Konstruktor in der Datei Controllers/MoviesController.cs verwendet die Abhängigkeitsinjektion zum Einfügen des Datenbankkontexts (MvcMovieContext) in den Controller. Der Datenbankkontext wird in den einzelnen CRUD-Methoden im Controller verwendet.

Beim Gerüstbau wurde der folgende hervorgehobene Code in Program.cs erzeugt:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddDbContext<MvcMovieContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("MvcMovieContext")));

Das ASP.NET Core-Konfigurationssystem liest die Verbindungszeichenfolge der Datenbank "MvcMovieContext".

Überprüfen der generierten Datenbank-Verbindungszeichenfolge

Das Gerüst hat der Datei appsettings.json ein Verbindungszeichenfolge hinzugefügt:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "MvcMovieContext": "Server=(localdb)\\mssqllocaldb;Database=MvcMovieContext-7dc5;Trusted_Connection=True;MultipleActiveResultSets=true"
  }
}

Für die lokale Entwicklung liest das konfigurationssystem ASP.NET Core den Schlüssel ConnectionString aus der Datei appsettings.json.

Die InitialCreate-Klasse

Untersuchen Sie die Migrationsdatei Migrations/{timestamp}_InitialCreate.cs:

using System;
using Microsoft.EntityFrameworkCore.Migrations;

#nullable disable

namespace MvcMovie.Migrations
{
    public partial class InitialCreate : Migration
    {
        protected override void Up(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.CreateTable(
                name: "Movie",
                columns: table => new
                {
                    Id = table.Column<int>(type: "int", nullable: false)
                        .Annotation("SqlServer:Identity", "1, 1"),
                    Title = table.Column<string>(type: "nvarchar(max)", nullable: true),
                    ReleaseDate = table.Column<DateTime>(type: "datetime2", nullable: false),
                    Genre = table.Column<string>(type: "nvarchar(max)", nullable: true),
                    Price = table.Column<decimal>(type: "decimal(18,2)", nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_Movie", x => x.Id);
                });
        }

        protected override void Down(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.DropTable(
                name: "Movie");
        }
    }
}

Im vorstehenden Code:

  • InitialCreate.Up erstellt die Movie-Tabelle und konfiguriert Id als Primärschlüssel.
  • InitialCreate.Down macht die von der Up-Migration vorgenommenen Schemaänderungen rückgängig.

Abhängigkeitsinjektion im Controller

Öffnen Sie die Datei Controllers/MoviesController.cs, und überprüfen Sie den Konstruktor:

public class MoviesController : Controller
{
    private readonly MvcMovieContext _context;

    public MoviesController(MvcMovieContext context)
    {
        _context = context;
    }

Der Konstruktor verwendet die Abhängigkeitsinjektion zum Einfügen des Datenbankkontexts (MvcMovieContext) in den Controller. Der Datenbankkontext wird in den einzelnen CRUD-Methoden im Controller verwendet.

Testen Sie die Seite Create (Erstellen). Geben Sie die Daten ein, und senden Sie sie.

Testen Sie die Seiten Edit (Bearbeiten), Details und Delete (Löschen).

Stark typisierte Modelle und die @model-Direktive

Weiter oben in diesem Tutorial haben Sie gesehen, wie ein Controller mithilfe des Wörterbuchs ViewData Daten oder Objekte an eine Ansicht übergeben kann. Das Wörterbuch ViewData ist ein dynamisches Objekt, das eine praktische Möglichkeit bietet, um Informationen spätgebunden an eine Ansicht zu übergeben.

MVC bietet die Möglichkeit, stark typisierte Modellobjekte an eine Ansicht zu übergeben. Dieser stark typisierte Ansatz ermöglicht eine Überprüfung Ihres Codes zur Kompilierzeit. Der Gerüstmechanismus hat ein stark typisiertes Modell an die MoviesController-Klasse und Ansichten übergeben.

Untersuchen Sie die generierte Details-Methode in der Datei Controllers/MoviesController.cs:

// GET: Movies/Details/5
public async Task<IActionResult> Details(int? id)
{
    if (id == null)
    {
        return NotFound();
    }

    var movie = await _context.Movie
        .FirstOrDefaultAsync(m => m.Id == id);
    if (movie == null)
    {
        return NotFound();
    }

    return View(movie);
}

Der id-Parameter wird im Allgemeinen als Routendaten übergeben. https://localhost:5001/movies/details/1 legt beispielsweise Folgendes fest:

  • Den Controller dem Controller movies zuordnen, dem ersten URL-Segment.
  • Die Aktion auf details (das zweite URL-Segment).
  • Der id wird auf 1 gesetzt, das letzte URL-Segment.

id kann wie im folgenden Beispiel mit einer Abfragezeichenfolge übergeben werden:

https://localhost:5001/movies/details?id=1

Der id-Parameter wird als Nullable-Typ (int?) für den Fall definiert, dass kein id-Wert angegeben wird.

Ein Lambdaausdruck wird an die FirstOrDefaultAsync-Methode übergeben, um Movie-Entitäten auszuwählen, die mit den Routendaten oder dem Wert der Abfragezeichenfolge übereinstimmen.

var movie = await _context.Movie
    .FirstOrDefaultAsync(m => m.Id == id);

Wenn ein Film gefunden wird, wird eine Instanz des Movie-Modells an die Ansicht Details übergeben:

return View(movie);

Untersuchen Sie den Inhalt der Datei Views/Movies/Details.cshtml:

@model MvcMovie.Models.Movie

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

<h1>Details</h1>

<div>
    <h4>Movie</h4>
    <hr />
    <dl class="row">
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Title)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Title)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.ReleaseDate)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.ReleaseDate)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Genre)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Genre)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Price)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Price)
        </dd>
    </dl>
</div>
<div>
    <a asp-action="Edit" asp-route-id="@Model.Id">Edit</a> |
    <a asp-action="Index">Back to List</a>
</div>

Die @model-Anweisung am Anfang der Ansichtsdatei gibt den Typ des Objekts an, den die Ansicht erwartet. Beim Erstellen des Movie-Controllers wurde die folgende @model-Anweisung eingeschlossen:

@model MvcMovie.Models.Movie

Diese @model-Direktive ermöglicht den Zugriff auf das Video, das der Controller an die Ansicht übergeben hat. Das Model-Objekt ist stark typisiert. In der Ansicht Details.cshtml übergibt der Code jedes Filmfeld an die HTML-Helfer DisplayNameFor und DisplayFor mit dem stark typisierten Model-Objekt. Die Methoden Create und Edit und Ansichten übergeben außerdem ein Movie-Modelobjekt.

Untersuchen Sie die Ansicht Index.cshtml und die Index-Methode im Movies-Controller. Beachten Sie, wie der Code beim Aufrufen der List-Methode ein View-Objekt erstellt. Der Code übergibt diese Liste Movies aus der Aktionsmethode Index an die Ansicht:

// GET: Movies
public async Task<IActionResult> Index()
{
    return View(await _context.Movie.ToListAsync());
}

Als der Movie-Controller erstellt wurde, hat der Gerüstcode automatisch die folgende @model-Anweisung am Anfang der Datei Index.cshtml hinzugefügt.

@model IEnumerable<MvcMovie.Models.Movie>

Diese @model-Direktive ermöglicht Zugriff auf die Liste der Filme, die der Controller an die Ansicht übergeben hat, indem ein stark typisiertes Model-Objekt verwendet wird. Z. B. durchläuft der Code in der Ansicht Index.cshtml die Filme mittels einer foreach-Anweisung über das stark typisierte Model-Objekt.

@model IEnumerable<MvcMovie.Models.Movie>

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

<h1>Index</h1>

<p>
    <a asp-action="Create">Create New</a>
</p>
<table class="table">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.Title)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.ReleaseDate)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Genre)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Price)
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
@foreach (var item in Model) {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.Title)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.ReleaseDate)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Genre)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Price)
            </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>

Da das Model-Objekt als ein IEnumerable<Movie>-Objekt stark typisiert ist, wird jedes Element in der Schleife als Movie typisiert. Neben anderen Vorteilen überprüft der Compiler die im Code verwendeten Typen.

Weitere Ressourcen

In diesem Tutorial werden Klassen für die Verwaltung von Filmen in einer Datenbank hinzugefügt. Diese Klassen stellen den „Modell“-Teil der MVC-App dar.

Diese Modellklassen werden mit Entity Framework Core (EF Core) verwendet, um mit einer Datenbank zu arbeiten. EF Core ist ein ORM-Framework (Objektrelationales Mapping), das den Datenzugriffscode vereinfacht, den Sie schreiben müssen.

Die erstellten Modellklassen werden als POCO-Klassen bezeichnet (von Plain Old CLR Objects). POCO-Klassen weisen keine Abhängigkeit von EF Core auf. Sie definieren lediglich die Eigenschaften der Daten, die in der Datenbank gespeichert werden sollen.

In diesem Tutorial werden zuerst die Modellklassen erstellt. Anschließend erstellt EF Core die Datenbank.

Hinzufügen einer Datenmodellklasse

Klicken Sie mit der rechten Maustaste auf den Ordner Modelle>Hinzufügen>Klasse. Nennen Sie die Datei Movie.cs.

Aktualisieren Sie die Datei Models/Movie.cs mit dem folgenden Code:

using System;
using System.ComponentModel.DataAnnotations;

namespace MvcMovie.Models
{
    public class Movie
    {
        public int Id { get; set; }
        public string Title { get; set; }

        [DataType(DataType.Date)]
        public DateTime ReleaseDate { get; set; }
        public string Genre { get; set; }
        public decimal Price { get; set; }
    }
}

Die Movie-Klasse enthält ein Id-Feld, das von der Datenbank für den Primärschlüssel benötigt wird.

Das DataType-Attribut für ReleaseDate gibt den Typ der Daten (Date) an. Mit diesem Attribut:

  • Der Benutzer muss keine Zeitinformationen in das Datumsfeld eingeben.
  • Nur das Datum wird angezeigt, keine Zeitinformationen.

DataAnnotations werden in einem späteren Tutorial behandelt.

Hinzufügen von NuGet-Paketen

Wählen Sie im Menü ToolsNuGet Paket-Manager>Paket-Manager Console (PMC) aus.

PMC-Menü

Führen Sie den folgenden Befehl in der PMC aus:

Install-Package Microsoft.EntityFrameworkCore.Design

Die oben aufgeführten Befehle fügen Folgendes hinzu:

  • Der SQL Server Anbieter EF Core. Das Anbieterpaket installiert das EF Core-Paket als Abhängigkeit.
  • Die von den Paketen verwendeten Hilfsprogramme werden im weiteren Verlauf des Tutorials automatisch im Gerüstbauschritt installiert.

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

Grundgerüst der Filmseiten

Verwenden Sie das Tool für den Gerüstbau, um die Seiten Create, Read, Update und Delete (CRUD) für das Film-Modell zu erstellen.

Klicken Sie in Projektmappen-Explorer mit der rechten Maustaste auf den Ordner Controller, und wählen Sie Add > New Scaffolded Item aus.

Screenshot für oben genannten Schritt

Wählen Sie im Dialogfeld Gerüst hinzufügen die Option MVC-Controller mit Ansichten unter Verwendung von Entity Framework > Hinzufügen aus.

Dialogfeld „Gerüst hinzufügen“

Vervollständigen Sie das Dialogfeld MVC-Controller mit Ansichten unter Verwendung von Entity Framework hinzufügen:

  • Wählen Sie in der Dropdownliste Modellklasse den Eintrag Movie (MvcMovie.Models) aus.
  • Wählen Sie in der Zeile Datenkontextklasse das +-Zeichen (Plus) aus.
    • Im Dialogfeld Datenkontext hinzufügen wird der Klassenname MvcMovie.Data.MvcMovieContext generiert.
    • Wählen Sie Hinzufügen aus.
  • Ansichten und Controllername: Standard beibehalten.
  • Wählen Sie Hinzufügen aus.

Datenkontext hinzufügen: Standardwerte behalten

Das Scaffolding aktualisiert Folgendes:

  • Die erforderlichen Paketverweise werden in die MvcMovie.csproj-Projektdatei eingefügt.
  • Registriert den Datenbankkontext in Startup.ConfigureServices der Datei Startup.cs.
  • Fügt der Datei appsettings.json eine Datenbank-Verbindungszeichenfolge hinzu.

Beim Gerüstbau wird Folgendes erstellt:

  • Ein Movies-Controller: Controllers/MoviesController.cs
  • Razor-Ansichtsdateien für die Seiten Erstellen, Löschen, Details, Bearbeiten und Index: Views/Movies/*.cshtml
  • Eine Datenbankkontext-Klasse: Data/MvcMovieContext.cs

Die automatische Erstellung dieser Dateien und Dateiaktualisierungen wird als Gerüstbau bezeichnet.

Sie können die Gerüstbauseiten noch nicht verwenden, da die Datenbank nicht vorhanden ist. Wenn Sie die App ausführen und den Link Movie App auswählen, wird die Fehlermeldung Die Datenbank kann nicht geöffnet werden oder Keine solche Tabelle: Movie angezeigt.

Erste Migration

Verwenden Sie das Feature EF CoreMigrationen, um die Datenbank zu erstellen. Bei Migrationen handelt es sich um mehrere Tools, mit denen Sie eine Datenbank dem Datenmodell entsprechend erstellen und aktualisieren können.

Wählen Sie im Menü Werkzeuge den NuGet Paket-Manager> und die Paket-Manager Console aus.

Geben Sie in der Paket-Manager Konsole (PMC) die folgenden Befehle ein:

Add-Migration InitialCreate
Update-Database

  • Add-Migration InitialCreate: Generiert die Migrationsdatei Migrations/{timestamp}_InitialCreate.cs. Das InitialCreate-Argument ist der Migrationsname. Es kann jeder Name verwendet werden, aber per Konvention wird ein Name ausgewählt, der die Migration beschreibt. Da dies die erste Migration ist, enthält die generierte Klasse Code zum Erstellen des Datenbankschemas. Das Datenbankschema basiert auf dem Modell, das in der Klasse MvcMovieContext angegeben ist.

  • Update-Database: Aktualisiert die Datenbank auf die neueste Migration, die der vorherige Befehl erstellt hat. Der Befehl führt die Up-Methode in der Datei Migrations/{time-stamp}_InitialCreate.cs aus, mit der die Datenbank erstellt wird.

Der Befehl Update-Database generiert folgende Warnung:

Für die Dezimalspalte 'Price' für den Entitätstyp 'Movie' wurde kein Typ angegeben. Dadurch werden Werte stillschweigend abgeschnitten, falls diese nicht der Standardpräzision und -skala entsprechen. Geben Sie explizit den SQL Server-Spaltentyp an, der alle Werte mit 'HasColumnType()' aufnehmen kann. (Für die Spalte „Price“ mit Dezimalwerten des Entitätstyps „Movie“ wurde kein Typ angegeben. Dadurch werden Werte automatisch abgeschnitten, falls diese nicht der Standardgenauigkeit und -skalierung entsprechen. Geben Sie explizit den SQL Server-Spaltentyp an, der alle Werte unterstützt.)

Ignorieren Sie die obige Warnung, sie wird in einem späteren Tutorial behoben.

Weitere Informationen zu den PMC-Tools für EF Core finden Sie unter EF Core Tools reference - PMC in Visual Studio.

Testen der App

Führen Sie die APP aus, und wählen Sie den Link Movie App aus.

Wenn Sie eine ähnliche Ausnahme wie die folgende erhalten, haben Sie möglicherweise den Migrationsschritt nicht durchgeführt:

SqlException: Cannot open database "MvcMovieContext-1" requested by the login. The login failed.

Note

Sie können unter Umständen in das Feld Price keine Kommas als Dezimaltrennzeichen eingeben. Zur Unterstützung der jQuery-Validierung für Gebietsschemas mit einer anderen Sprache als Englisch, in denen ein Komma („,“) als Dezimaltrennzeichen verwendet wird, und für Datums- und Uhrzeitformate, die nicht dem US-englischen Format entsprechen, muss die App globalisiert werden. Anweisungen zur Globalisierung finden Sie unter dieses GitHub Problem.

Untersuchen der generierten Datenbankkontextklasse und -registrierung

Bei EF Core erfolgt der Datenzugriff über ein Modell. Ein Modell setzt sich aus Entitätsklassen und einem Kontextobjekt zusammen, das eine Sitzung mit der Datenbank darstellt. Das Kontextobjekt ermöglicht das Abfragen und Speichern von Daten. Der Datenbankkontext wird von Microsoft abgeleitet. EntityFrameworkCore.DbContext und gibt die Entitäten an, die in das Datenmodell aufgenommen werden sollen.

Beim Scaffolding-Prozess wird die Datenbankkontextklasse Data/MvcMovieContext.cs erstellt.

using Microsoft.EntityFrameworkCore;
using MvcMovie.Models;

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

        public DbSet<Movie> Movie { get; set; }
    }
}

Der obige Code erstellt eine DbSet<Movie>-Eigenschaft, die die Filme in der Datenbank darstellt.

ASP.NET Core wird mit Dependency Injection (DI) erstellt. Dienste, z. B. der Datenbankkontext, müssen mit DI in Startup registriert werden. Komponenten, die diese Dienste benötigen, werden über Konstruktorparameter bereitgestellt.

Der Konstruktor in der Datei Controllers/MoviesController.cs verwendet die Abhängigkeitsinjektion zum Einfügen des Datenbankkontexts (MvcMovieContext) in den Controller. Der Datenbankkontext wird in den einzelnen CRUD-Methoden im Controller verwendet.

Beim Gerüstbau wurde der folgende hervorgehobene Code in Startup.ConfigureServices erzeugt:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews();

    services.AddDbContext<MvcMovieContext>(options =>
    options.UseSqlServer(Configuration.GetConnectionString("MvcMovieContext")));
}

Das ASP.NET Core-Konfigurationssystem liest die Datenbank-Verbindungszeichenfolge "MvcMovieContext".

Überprüfen Sie die generierte Datenbank-Verbindungszeichenfolge

Das Gerüst hat der Datei appsettings.json eine Verbindungszeichenfolge hinzugefügt.

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

Für die lokale Entwicklung liest das konfigurationssystem ASP.NET Core den Schlüssel ConnectionString aus der Datei appsettings.json.

Die InitialCreate-Klasse

Untersuchen Sie die Migrationsdatei Migrations/{timestamp}_InitialCreate.cs:

public partial class InitialCreate : Migration
{
    protected override void Up(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.CreateTable(
            name: "Movie",
            columns: table => new
            {
                Id = table.Column<int>(type: "int", nullable: false)
                    .Annotation("SqlServer:Identity", "1, 1"),
                Title = table.Column<string>(type: "nvarchar(max)", nullable: true),
                ReleaseDate = table.Column<DateTime>(type: "datetime2", nullable: false),
                Genre = table.Column<string>(type: "nvarchar(max)", nullable: true),
                Price = table.Column<decimal>(type: "decimal(18,2)", nullable: false)
            },
            constraints: table =>
            {
                table.PrimaryKey("PK_Movie", x => x.Id);
            });
    }

    protected override void Down(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.DropTable(
            name: "Movie");
    }
}

Im vorhergehenden Code:

  • InitialCreate.Up erstellt die Movie-Tabelle und konfiguriert Id als Primärschlüssel.
  • InitialCreate.Down macht die von der Up-Migration vorgenommenen Schemaänderungen rückgängig.

Abhängigkeitsinjektion im Controller

Öffnen Sie die Datei Controllers/MoviesController.cs, und überprüfen Sie den Konstruktor:

public class MoviesController : Controller
{
    private readonly MvcMovieContext _context;

    public MoviesController(MvcMovieContext context)
    {
        _context = context;
    }

Der Konstruktor verwendet die Abhängigkeitsinjektion zum Einfügen des Datenbankkontexts (MvcMovieContext) in den Controller. Der Datenbankkontext wird in den einzelnen CRUD-Methoden im Controller verwendet.

Testen Sie die Seite Create (Erstellen). Geben Sie die Daten ein, und senden Sie sie.

Testen Sie die Seiten Edit (Bearbeiten), Details und Delete (Löschen).

Stark typisierte Modelle und die @model-Direktive

Weiter oben in diesem Tutorial haben Sie gesehen, wie ein Controller mithilfe des Wörterbuchs ViewData Daten oder Objekte an eine Ansicht übergeben kann. Das Wörterbuch ViewData ist ein dynamisches Objekt, das eine praktische Möglichkeit bietet, um Informationen spätgebunden an eine Ansicht zu übergeben.

MVC bietet die Möglichkeit, stark typisierte Modellobjekte an eine Ansicht zu übergeben. Dieser stark typisierte Ansatz ermöglicht eine Überprüfung Ihres Codes zur Kompilierzeit. Der Gerüstmechanismus hat ein stark typisiertes Modell an die MoviesController-Klasse und Ansichten übergeben.

Untersuchen Sie die generierte Details-Methode in der Datei Controllers/MoviesController.cs:

// GET: Movies/Details/5
public async Task<IActionResult> Details(int? id)
{
    if (id == null)
    {
        return NotFound();
    }

    var movie = await _context.Movie
        .FirstOrDefaultAsync(m => m.Id == id);
    if (movie == null)
    {
        return NotFound();
    }

    return View(movie);
}

Der id-Parameter wird im Allgemeinen als Routendaten übergeben. https://localhost:5001/movies/details/1 legt beispielsweise Folgendes fest:

  • Den Controller dem Controller movies zuordnen, dem ersten URL-Segment.
  • Die Aktion auf details (das zweite URL-Segment).
  • Der id wird auf 1 gesetzt, das letzte URL-Segment.

id kann wie im folgenden Beispiel mit einer Abfragezeichenfolge übergeben werden:

https://localhost:5001/movies/details?id=1

Der id-Parameter wird als Nullable-Typ (int?) für den Fall definiert, dass kein id-Wert angegeben wird.

Ein Lambdaausdruck wird an die FirstOrDefaultAsync-Methode übergeben, um Movie-Entitäten auszuwählen, die mit den Routendaten oder dem Wert der Abfragezeichenfolge übereinstimmen.

var movie = await _context.Movie
    .FirstOrDefaultAsync(m => m.Id == id);

Wenn ein Film gefunden wird, wird eine Instanz des Movie-Modells an die Ansicht Details übergeben:

return View(movie);

Untersuchen Sie den Inhalt der Datei Views/Movies/Details.cshtml:

@model MvcMovie.Models.Movie

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

<h1>Details</h1>

<div>
    <h4>Movie</h4>
    <hr />
    <dl class="row">
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Title)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Title)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.ReleaseDate)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.ReleaseDate)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Genre)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Genre)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Price)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Price)
        </dd>
    </dl>
</div>
<div>
    <a asp-action="Edit" asp-route-id="@Model.Id">Edit</a> |
    <a asp-action="Index">Back to List</a>
</div>

Die @model-Anweisung am Anfang der Ansichtsdatei gibt den Typ des Objekts an, den die Ansicht erwartet. Beim Erstellen des Movie-Controllers wurde die folgende @model-Anweisung eingeschlossen:

@model MvcMovie.Models.Movie

Diese @model-Direktive ermöglicht den Zugriff auf das Video, das der Controller an die Ansicht übergeben hat. Das Model-Objekt ist stark typisiert. In der Ansicht Details.cshtml übergibt der Code jedes Filmfeld an die HTML-Hilfsprogramme DisplayNameFor und DisplayFor mit dem stark typisierten Model-Objekt. Die Methoden Create und Edit und Ansichten übergeben außerdem ein Movie-Modelobjekt.

Untersuchen Sie die Ansicht Index.cshtml und die Index-Methode im Movies-Controller. Beachten Sie, wie der Code beim Aufrufen der List-Methode ein View-Objekt erstellt. Der Code übergibt diese Liste Movies aus der Aktionsmethode Index an die Ansicht:

// GET: Movies
public async Task<IActionResult> Index()
{
    return View(await _context.Movie.ToListAsync());
}

Als der Movies-Controller erstellt wurde, hat das Scaffolding automatisch die folgende @model-Anweisung oben in der Datei Index.cshtml hinzugefügt.

@model IEnumerable<MvcMovie.Models.Movie>

Die @model-Direktive ermöglicht den Zugriff auf die Liste der Filme, die der Controller an die Ansicht übergeben hat, indem ein stark typisiertes Model-Objekt verwendet wird. In der Ansicht Index.cshtml durchläuft der Code z. B. die Filme mithilfe einer foreach-Anweisung für das stark typisierte Model-Objekt in einer Schleife:

@model IEnumerable<MvcMovie.Models.Movie>

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

<h1>Index</h1>

<p>
    <a asp-action="Create">Create New</a>
</p>
<table class="table">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.Title)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.ReleaseDate)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Genre)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Price)
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
@foreach (var item in Model) {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.Title)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.ReleaseDate)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Genre)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Price)
            </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>

Da das Model-Objekt stark typisiert ist als IEnumerable<Movie>-Objekt, ist jedes Element in der Schleife vom Typ Movie. Neben anderen Vorteilen überprüft der Compiler die im Code verwendeten Typen.

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" zur appsettings.Development.json-Datei 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 vorherigen JSON werden SQL-Anweisungen in der Befehlszeile und im Visual Studio Ausgabefenster angezeigt.

Weitere Informationen finden Sie in den folgenden Ressourcen:

Weitere Ressourcen

In diesem Tutorial werden Klassen für die Verwaltung von Filmen in einer Datenbank hinzugefügt. Diese Klassen stellen den „Modell“-Teil der MVC-App dar.

Diese Modellklassen werden mit Entity Framework Core (EF Core) verwendet, um mit einer Datenbank zu arbeiten. EF Core ist ein ORM-Framework (Objektrelationales Mapping), das den Datenzugriffscode vereinfacht, den Sie schreiben müssen.

Die erstellten Modellklassen werden als POCO-Klassen bezeichnet (von Plain Old CLR Objects). POCO-Klassen weisen keine Abhängigkeit von EF Core auf. Sie definieren lediglich die Eigenschaften der Daten, die in der Datenbank gespeichert werden sollen.

In diesem Tutorial werden zuerst die Modellklassen erstellt. Anschließend erstellt EF Core die Datenbank.

Hinzufügen einer Datenmodellklasse

Klicken Sie mit der rechten Maustaste auf den Ordner Modelle>Hinzufügen>Klasse. Nennen Sie die Datei Movie.cs.

Aktualisieren Sie die Datei Movie.cs mit dem folgenden Code:

using System;
using System.ComponentModel.DataAnnotations;

namespace MvcMovie.Models
{
    public class Movie
    {
        public int Id { get; set; }
        public string Title { get; set; }

        [DataType(DataType.Date)]
        public DateTime ReleaseDate { get; set; }
        public string Genre { get; set; }
        public decimal Price { get; set; }
    }
}

Die Movie-Klasse enthält ein Id-Feld, das von der Datenbank für den Primärschlüssel benötigt wird.

Das DataType-Attribut für ReleaseDate gibt den Typ der Daten (Date) an. Mit diesem Attribut:

  • Es ist nicht erforderlich, dass der Benutzer Zeitinformationen in das Datumsfeld eingibt.
  • Nur das Datum wird angezeigt, keine Zeitinformationen.

DataAnnotations werden in einem späteren Tutorial behandelt.

Hinzufügen von NuGet-Paketen

Wählen Sie im Menü ToolsNuGet Paket-Manager>Paket-Manager Console (PMC) aus.

PMC-Menü

Führen Sie den folgenden Befehl in der PMC aus:

Install-Package Microsoft.EntityFrameworkCore.SqlServer

Der obige Befehl fügt den anbieter EF Core SQL Server hinzu. Das Anbieterpaket installiert das EF Core-Paket als Abhängigkeit. Weitere Pakete werden im weiteren Verlauf des Tutorials automatisch im Gerüstbauschritt installiert.

Erstellen einer Datenbankkontextklasse

Es wird eine Datenbankkontextklasse benötigt, um die EF Core-Funktionen (Erstellen, Lesen, Aktualisieren, Löschen) für das Movie-Modell zu koordinieren. Der Datenbankkontext wird von Microsoft.EntityFrameworkCore.DbContext abgeleitet und gibt die Entitäten an, die in das Datenmodell einbezogen werden sollen.

Erstellen Sie einen Ordner Data.

Fügen Sie eine Datei Data/MvcMovieContext.cs mit dem folgenden Code hinzu:

using Microsoft.EntityFrameworkCore;
using MvcMovie.Models;

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

        public DbSet<Movie> Movie { get; set; }
    }
}

Der vorangehende Code erstellt eine DbSet<Movie>-Eigenschaft für die Entitätenmenge. In der Terminologie von Entity Framework entspricht eine Entitätenmenge in der Regel einer Datenbanktabelle. Entitäten entsprechen Zeilen in Tabellen.

Registrieren des Datenbankkontexts

ASP.NET Core wird mit Dependency Injection (DI) erstellt. Dienste (z. B. der EF Core-Datenbankkontext) müssen beim Anwendungsstart bei DI registriert werden. Komponenten, die diese Dienste erfordern (z. B. Razor Pages), werden über Konstruktorparameter bereitgestellt. Der Konstruktorcode, der eine Datenbankkontext-Instanz erzeugt, wird später im Tutorial gezeigt. In diesem Abschnitt registrieren Sie den Datenbankkontext mit dem DI-Container.

Fügen Sie die folgenden using-Anweisungen am Anfang von Startup.cs hinzu:

using MvcMovie.Data;
using Microsoft.EntityFrameworkCore;

Fügen Sie folgenden hervorgehobenen Code in Startup.ConfigureServices hinzu:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews();

    services.AddDbContext<MvcMovieContext>(options =>
    options.UseSqlServer(Configuration.GetConnectionString("MvcMovieContext")));
}

Der Name des Verbindungszeichenfolge wird an den Kontext übergeben, indem eine Methode für ein DbContextOptions-Objekt aufgerufen wird. Für die lokale Entwicklung liest das konfigurationssystem ASP.NET Core den Verbindungszeichenfolge aus der Datei appsettings.json.

Überprüfen Sie die Datenbank-Verbindungszeichenfolge

Fügen Sie der Datei appsettings.json eine Verbindungszeichenfolge hinzu:

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

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

Grundgerüst der Filmseiten

Verwenden Sie das Scaffolding-Tool, um CRUD-Seiten („Create“ (Erstellen), „Read“ (Lesen), „Update“ (Aktualisieren) und „Delete“ (Löschen)) für das Filmmodell zu erstellen.

Klicken Sie in Projektmappen-Explorer mit der rechten Maustaste auf die Controller Ordner >> Neues Gerüstelement hinzufügen.

Screenshot für oben genannten Schritt

Wählen Sie im Dialogfeld Gerüst hinzufügen die Option MVC-Controller mit Ansichten unter Verwendung von Entity Framework > Hinzufügen aus.

Dialogfeld „Gerüst hinzufügen“

Bearbeiten Sie das Dialogfeld Controller hinzufügen:

  • Modellklasse:Movie (MvcMovie.Models)
  • Datenkontextklasse:MvcMovieContext (MvcMovie.Data)

Datenkontext hinzufügen

  • Ansichten: Belassen Sie den Standardwert der einzelnen Optionen aktiviert.
  • Controllername: Behalten Sie den Standard MoviesController bei.
  • Wählen Sie Hinzufügen aus.

Visual Studio erstellt:

  • Ein Film-Controller (Controllers/MoviesController.cs)
  • Razor Ansichtsdateien für die Seiten „Erstellen“, „Löschen“, „Details“, „Bearbeiten“ und „Index“ (*Views/Movies/„.cshtml“)

Die automatische Erstellung dieser Dateien wird als Gerüstbau bezeichnet.

Sie können die Gerüstbauseiten noch nicht verwenden, da die Datenbank nicht vorhanden ist. Wenn Sie die App ausführen und auf den Link Movie App klicken, erhalten Sie eine Fehlermeldung des Typs Datenbank kann nicht geöffnet werden oder Keine solche Tabelle vorhanden: Movie.

Erste Migration

Verwenden Sie die Funktion EF CoreMigrationen, um die Datenbank zu erstellen. Migrations sind eine Reihe von Tools, mit denen Sie eine Datenbank erstellen und aktualisieren können, damit die Ihrem Datenmodell entspricht.

Wählen Sie im Menü ToolsNuGet Paket-Manager>Paket-Manager Console (PMC) aus.

Geben Sie in der PMC die folgenden Befehle ein:

Add-Migration InitialCreate
Update-Database
  • Add-Migration InitialCreate: Generiert die Migrationsdatei Migrations/{timestamp}_InitialCreate.cs. Das InitialCreate-Argument ist der Migrationsname. Es kann jeder Name verwendet werden, aber per Konvention wird ein Name ausgewählt, der die Migration beschreibt. Da dies die erste Migration ist, enthält die generierte Klasse Code zum Erstellen des Datenbankschemas. Das Datenbankschema basiert auf dem Modell, das in der Klasse MvcMovieContext angegeben ist.

  • Update-Database: Aktualisiert die Datenbank auf die neueste Migration, die der vorherige Befehl erstellt hat. Der Befehl führt die Up-Methode in der Datei Migrations/{time-stamp}_InitialCreate.cs aus, mit der die Datenbank erstellt wird.

    Der Befehl zum Aktualisieren der Datenbank generiert folgende Warnung:

    Für die Dezimalspalte 'Price' für den Entitätstyp 'Movie' wurde kein Typ angegeben. Dadurch werden Werte stillschweigend abgeschnitten, falls diese nicht der Standardpräzision und -skala entsprechen. Spezifizieren Sie explizit den SQL Server-Spaltentyp, der alle Werte unter Verwendung von 'HasColumnType()' aufnehmen kann.

    Sie können diese Warnung ignorieren, sie wird in einem späteren Tutorial behoben.

Weitere Informationen zu den PMC-Tools für EF Core finden Sie unter EF Core Tools reference - PMC in Visual Studio.

Die InitialCreate-Klasse

Untersuchen Sie die Migrationsdatei Migrations/{timestamp}_InitialCreate.cs:

public partial class InitialCreate : Migration
{
    protected override void Up(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.CreateTable(
            name: "Movie",
            columns: table => new
            {
                Id = table.Column<int>(nullable: false)
                    .Annotation("SqlServer:ValueGenerationStrategy", 
                                 SqlServerValueGenerationStrategy.IdentityColumn),
                Title = table.Column<string>(nullable: true),
                ReleaseDate = table.Column<DateTime>(nullable: false),
                Genre = table.Column<string>(nullable: true),
                Price = table.Column<decimal>(nullable: false)
            },
            constraints: table =>
            {
                table.PrimaryKey("PK_Movie", x => x.Id);
            });
    }

    protected override void Down(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.DropTable(
            name: "Movie");
    }
}

Die Up-Methode erstellt die Movie-Tabelle und konfiguriert Id als Primärschlüssel. Die Down-Methode macht die von der Up-Migration vorgenommenen Schemaänderungen rückgängig.

Testen der App

  • Führen Sie die APP aus, und klicken Sie auf den Link Movie App.

    Wenn eine Ausnahme auftritt, die der folgenden ähnelt:

SqlException: Cannot open database "MvcMovieContext-1" requested by the login. The login failed.

Sie haben möglicherweise den Migrationsschritt ausgelassen.

  • Testen Sie die Seite Create (Erstellen). Geben Sie die Daten ein, und senden Sie sie.

    Note

    Sie können unter Umständen in das Feld Price keine Kommas als Dezimaltrennzeichen eingeben. Zur Unterstützung der jQuery-Validierung für Gebietsschemas mit einer anderen Sprache als Englisch, in denen ein Komma („,“) als Dezimaltrennzeichen verwendet wird, und für Datums- und Uhrzeitformate, die nicht dem US-englischen Format entsprechen, muss die App globalisiert werden. Anweisungen zur Globalisierung finden Sie unter dieses GitHub Problem.

  • Testen Sie die Seiten Edit (Bearbeiten), Details und Delete (Löschen).

Abhängigkeitsinjektion im Controller

Öffnen Sie die Datei Controllers/MoviesController.cs, und überprüfen Sie den Konstruktor:

public class MoviesController : Controller
{
    private readonly MvcMovieContext _context;

    public MoviesController(MvcMovieContext context)
    {
        _context = context;
    }

Der Konstruktor verwendet die Abhängigkeitsinjektion zum Einfügen des Datenbankkontexts (MvcMovieContext) in den Controller. Der Datenbankkontext wird in den einzelnen CRUD-Methoden im Controller verwendet.

Stark typisierte Modelle und das Schlüsselwort @model

Weiter oben in diesem Tutorial haben Sie gesehen, wie ein Controller mithilfe des Wörterbuchs ViewData Daten oder Objekte an eine Ansicht übergeben kann. Das Wörterbuch ViewData ist ein dynamisches Objekt, das eine praktische Möglichkeit bietet, um Informationen spätgebunden an eine Ansicht zu übergeben.

MVC bietet außerdem die Möglichkeit, stark typisierte Modellobjekte an eine Ansicht zu übergeben. Dieser stark typisierte Ansatz ermöglicht eine Überprüfung Ihres Codes zur Kompilierzeit. Der Gerüstmechanismus hat diesen Ansatz (d.h. das Übergeben eines stark typisierten Modells) mit der MoviesController-Klasse und Views genutzt.

Untersuchen Sie die generierte Details-Methode in der Datei Controllers/MoviesController.cs:

// GET: Movies/Details/5
public async Task<IActionResult> Details(int? id)
{
    if (id == null)
    {
        return NotFound();
    }

    var movie = await _context.Movie
        .FirstOrDefaultAsync(m => m.Id == id);
    if (movie == null)
    {
        return NotFound();
    }

    return View(movie);
}

Der id-Parameter wird im Allgemeinen als Routendaten übergeben. https://localhost:5001/movies/details/1 legt beispielsweise Folgendes fest:

  • Den Controller auf den Controller movies (das erste URL-Segment).
  • Die Aktion zu details (das zweite URL-Segment).
  • Die ID wird auf 1 gesetzt (das letzte URL-Segment).

Sie können die id auch mithilfe einer Abfragezeichenfolge übergeben:

https://localhost:5001/movies/details?id=1

Der id-Parameter wird als Nullable-Typ (int?) für den Fall definiert, dass kein ID-Wert angegeben wird.

Ein Lambdaausdruck wird an FirstOrDefaultAsync übergeben, um Filmentitäten auszuwählen, die mit den Routendaten oder dem Wert der Abfragezeichenfolge übereinstimmen.

var movie = await _context.Movie
    .FirstOrDefaultAsync(m => m.Id == id);

Wenn ein Film gefunden wird, wird eine Instanz des Movie-Modells an die Ansicht Details übergeben:

return View(movie);

Untersuchen Sie den Inhalt der Datei Views/Movies/Details.cshtml:

@model MvcMovie.Models.Movie

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

<h1>Details</h1>

<div>
    <h4>Movie</h4>
    <hr />
    <dl class="row">
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Title)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Title)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.ReleaseDate)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.ReleaseDate)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Genre)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Genre)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Price)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Price)
        </dd>
    </dl>
</div>
<div>
    <a asp-action="Edit" asp-route-id="@Model.Id">Edit</a> |
    <a asp-action="Index">Back to List</a>
</div>

Die @model-Anweisung am Anfang der Ansichtsdatei gibt den Typ des Objekts an, den die Ansicht erwartet. Beim Erstellen des Movie-Controllers wurde die folgende @model-Anweisung eingeschlossen:

@model MvcMovie.Models.Movie

Diese @model-Anweisung ermöglicht den Zugriff auf den Film, den der Controller an die Ansicht übergeben hat. Das Model-Objekt ist stark typisiert. In der Ansicht Details.cshtml übergibt der Code jedes Filmfeld an die HTML-Helfer DisplayNameFor und DisplayFor mit dem stark typisierten Model-Objekt. Die Methoden Create und Edit und Ansichten übergeben außerdem ein Movie-Modelobjekt.

Untersuchen Sie die Ansicht Index.cshtml und die Index-Methode im Movies-Controller. Beachten Sie, wie der Code beim Aufrufen der List-Methode ein View-Objekt erstellt. Der Code übergibt diese Liste Movies aus der Aktionsmethode Index an die Ansicht:

// GET: Movies
public async Task<IActionResult> Index()
{
    return View(await _context.Movie.ToListAsync());
}

Als der Movie-Controller erstellt wurde, hat der Gerüstcode automatisch die folgende @model-Anweisung am Anfang der Datei Index.cshtml hinzugefügt.

@model IEnumerable<MvcMovie.Models.Movie>

Diese @model-Direktive ermöglicht Ihnen, mit einem stark typisierten Model-Objekt auf die vom Controller an die Ansicht übergebene Filmliste zuzugreifen. Z. B. durchläuft der Code in der Ansicht Index.cshtml die Filme mittels einer foreach-Anweisung über das stark typisierte Model-Objekt.

@model IEnumerable<MvcMovie.Models.Movie>

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

<h1>Index</h1>

<p>
    <a asp-action="Create">Create New</a>
</p>
<table class="table">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.Title)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.ReleaseDate)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Genre)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Price)
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
@foreach (var item in Model) {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.Title)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.ReleaseDate)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Genre)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Price)
            </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>

Da das Model-Objekt stark typisiert ist (als ein IEnumerable<Movie>-Objekt), wird jedes Element in der Schleife als Movie typisiert. Neben anderen Vorteilen bedeutet dies, dass Sie eine Kompilierungszeitprüfung des Codes erhalten.

Weitere Ressourcen