Freigeben über


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

Hinweis

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

Warnung

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

Wichtig

Diese Informationen beziehen sich auf ein Vorabversionsprodukt, das vor der kommerziellen Freigabe möglicherweise noch wesentlichen Änderungen unterliegt. Microsoft gibt keine Garantie, weder ausdrücklich noch impliziert, hinsichtlich der hier bereitgestellten Informationen.

Die aktuelle Version finden Sie in der .NET 9-Version dieses Artikels.

Von Rick Anderson und Jon P. Smith.

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.

Gerüstbau der Movie-Seiten

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 im Projektmappen-Explorer mit der rechten Maustaste auf den Ordner Controller, und wählen Sie Hinzufügen > Neues Gerüstelement 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.

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.
  • Wählen Sie in der Dropdownliste Datenbankanbieter die Option SQL Server aus.
  • Ansichten und Controllername: Übernehmen Sie die Standardeinstellung.
  • Wählen Sie Hinzufügen.

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 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.
  • Zur appsettings.json -Datei wird eine Datenbankverbindungszeichenfolge hinzugefügt.

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.

Anfängliche Migration

Verwenden Sie das EF Core-Feature für 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ü Tools die Option NuGet-Paket-Manager>Paket-Manager-Konsole 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 automatisch abgeschnitten, falls diese nicht der Standardgenauigkeit 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 Referenz der EF Core-Tools: 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.

Hinweis

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. Globalisierungsanweisungen finden Sie unter 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.EntityFrameworkCore.DbContext abgeleitet und gibt die Entitäten an, die im Datenmodell enthalten sein sollen.

Beim Gerüstbau 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; } = default!;
    }
}

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

Dependency Injection

ASP.NET Core wird mit Abhängigkeitsinjektion (Dependency Injection, DI) erstellt. Dienste, z. B. der Datenbankkontext, müssen mit DI in Program.cs registriert werden. 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 Datenbankverbindungszeichenfolge „MvcMovieContext“.

Untersuchen der generierten Datenbankverbindungszeichenfolge

Beim Gerüstbau wurde der appsettings.json -Datei 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 ASP.NET Core-Konfigurationssystem den ConnectionString-Schlüssel aus der appsettings.json -Datei.

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");
        }
    }
}

Für den Code oben gilt:

  • 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 zur späten Bindung bietet, um Informationen 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üstbaumechanismus hat ein stark typisiertes Modell in 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 auf den Controller movies (das erste URL-Segment).
  • Die Aktion auf details (das zweite URL-Segment).
  • Die id auf 1 (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-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-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 View-Methode ein List-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 der Gerüstbau 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. 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 ein IEnumerable<Movie>-Objekt), wird jedes Element in der Schleife als Movie typisiert. Neben anderen Vorteilen überprüft der Compiler die im Code verwendeten Typen.

Zusätzliche 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.

Gerüstbau der Movie-Seiten

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 im Projektmappen-Explorer mit der rechten Maustaste auf den Ordner Controller, und wählen Sie Hinzufügen > Neues Gerüstelement 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.

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.
  • Wählen Sie in der Dropdownliste Datenbankanbieter die Option SQL Server aus.
  • Ansichten und Controllername: Übernehmen Sie die Standardeinstellung.
  • Wählen Sie Hinzufügen.

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 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.
  • Zur appsettings.json -Datei wird eine Datenbankverbindungszeichenfolge hinzugefügt.

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.

Anfängliche Migration

Verwenden Sie das EF Core-Feature für 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ü Tools die Option NuGet-Paket-Manager>Paket-Manager-Konsole aus.

Geben Sie die folgenden Befehle in die Paket-Manager-Konsole 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 automatisch abgeschnitten, falls diese nicht der Standardgenauigkeit 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.

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 Referenz der EF Core-Tools: 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.

Hinweis

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. Globalisierungsanweisungen finden Sie unter 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.EntityFrameworkCore.DbContext abgeleitet und gibt die Entitäten an, die im Datenmodell enthalten sein sollen.

Beim Gerüstbau 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.

Dependency Injection

ASP.NET Core wird mit Abhängigkeitsinjektion (Dependency Injection, DI) erstellt. Dienste, z. B. der Datenbankkontext, müssen mit DI in Program.cs registriert werden. 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 Datenbankverbindungszeichenfolge „MvcMovieContext“.

Untersuchen der generierten Datenbankverbindungszeichenfolge

Beim Gerüstbau wurde der appsettings.json -Datei eine 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 ASP.NET Core-Konfigurationssystem den ConnectionString-Schlüssel aus der appsettings.json -Datei.

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");
        }
    }
}

Für den Code oben gilt:

  • 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 zur späten Bindung bietet, um Informationen 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üstbaumechanismus hat ein stark typisiertes Modell in 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 auf den Controller movies (das erste URL-Segment).
  • Die Aktion auf details (das zweite URL-Segment).
  • Die id auf 1 (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-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-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 View-Methode ein List-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 der Gerüstbau 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. 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 ein IEnumerable<Movie>-Objekt), wird jedes Element in der Schleife als Movie typisiert. Neben anderen Vorteilen überprüft der Compiler die im Code verwendeten Typen.

Zusätzliche 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.

Gerüstbau der Movie-Seiten

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 im Projektmappen-Explorer mit der rechten Maustaste auf den Ordner Controller, und wählen Sie Hinzufügen > Neues Gerüstelement 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.

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.
  • Wählen Sie in der Dropdownliste Datenbankanbieter die Option SQL Server aus.
  • Ansichten und Controllername: Übernehmen Sie die Standardeinstellung.
  • Wählen Sie Hinzufügen.

Datenkontext hinzufügen: Standardwerte beibehalten Wenn Sie eine Fehlermeldung erhalten, wählen Sie ein zweites Mal Hinzufügen 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 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.
  • Zur appsettings.json -Datei wird eine Datenbankverbindungszeichenfolge hinzugefügt.

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.

Anfängliche Migration

Verwenden Sie das EF Core-Feature für 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ü Tools die Option NuGet-Paket-Manager>Paket-Manager-Konsole aus.

Geben Sie die folgenden Befehle in die Paket-Manager-Konsole 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:

No type was specified for the decimal column 'Price' on entity type 'Movie'. Dadurch werden Werte automatisch abgeschnitten, falls diese nicht der Standardgenauigkeit und -skalierung entsprechen. Explicitly specify the SQL server column type that can accommodate all the values using 'HasColumnType()'. (Für die Spalte „Price“ mit Dezimalwerten für den Entitätstyp „Movie“ wurde kein Typ angegeben. Dadurch werden Werte automatisch abgeschnitten, falls diese nicht der Standardgenauigkeit und -skalierung entsprechen. Geben Sie den Spaltentyp von SQL-Server explizit an, der durch „ForHasColumnType()“ sämtliche Werte unterstützen 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 Referenz der EF Core-Tools: 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.

Hinweis

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. Globalisierungsanweisungen finden Sie unter 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.EntityFrameworkCore.DbContext abgeleitet und gibt die Entitäten an, die im Datenmodell enthalten sein sollen.

Beim Gerüstbau 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.

Dependency Injection

ASP.NET Core wird mit Abhängigkeitsinjektion (Dependency Injection, DI) erstellt. Dienste, z. B. der Datenbankkontext, müssen mit DI in Program.cs registriert werden. 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 Datenbankverbindungszeichenfolge „MvcMovieContext“.

Untersuchen der generierten Datenbankverbindungszeichenfolge

Beim Gerüstbau wurde der appsettings.json -Datei eine 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 ASP.NET Core-Konfigurationssystem den ConnectionString-Schlüssel aus der appsettings.json -Datei.

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");
        }
    }
}

Für den Code oben gilt:

  • 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 zur späten Bindung bietet, um Informationen 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üstbaumechanismus hat ein stark typisiertes Modell in 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 auf den Controller movies (das erste URL-Segment).
  • Die Aktion auf details (das zweite URL-Segment).
  • Die id auf 1 (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-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-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 View-Methode ein List-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 der Gerüstbau 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. 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 ein IEnumerable<Movie>-Objekt), wird jedes Element in der Schleife als Movie typisiert. Neben anderen Vorteilen überprüft der Compiler die im Code verwendeten Typen.

Zusätzliche 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

Wählen Sie im Menü ToolsNuGet-Paket-Manager>Paket-Manager-Konsole (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:

  • Den EF Core-SQL Server-Anbieter. 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.

Gerüstbau der Movie-Seiten

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 im Projektmappen-Explorer mit der rechten Maustaste auf den Ordner Controller, und wählen Sie Hinzufügen > Neues Gerüstelement 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.
  • Ansichten und Controllername: Übernehmen Sie die Standardeinstellung.
  • Wählen Sie Hinzufügen.

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.
  • Zur appsettings.json -Datei wird eine Datenbankverbindungszeichenfolge hinzugefügt.

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.

Erstellen der App

Erstellen Sie die App. Der Compiler generiert mehrere Warnungen zur Behandlung von null-Werten. Weitere Informationen finden Sie in diesem Issue auf GitHub und unter Nullable-Verweistypen.

Entfernen Sie die folgende Zeile aus der Datei MvcMovie.csproj, um die Warnungen von Nullwerte zulassenden Verweistypen zu entfernen:

<Nullable>enable</Nullable>

Dieser Problem sollte im nächsten Release behoben sein.

Anfängliche Migration

Verwenden Sie das EF Core-Feature für 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ü Tools die Option NuGet-Paket-Manager>Paket-Manager-Konsole aus.

Geben Sie die folgenden Befehle in die Paket-Manager-Konsole 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:

No type was specified for the decimal column 'Price' on entity type 'Movie'. Dadurch werden Werte automatisch abgeschnitten, falls diese nicht der Standardgenauigkeit und -skalierung entsprechen. Explicitly specify the SQL server column type that can accommodate all the values using 'HasColumnType()'. (Für die Spalte „Price“ mit Dezimalwerten für den Entitätstyp „Movie“ wurde kein Typ angegeben. Dadurch werden Werte automatisch abgeschnitten, falls diese nicht der Standardgenauigkeit und -skalierung entsprechen. Geben Sie den Spaltentyp von SQL-Server explizit an, der durch „ForHasColumnType()“ sämtliche Werte unterstützen 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 Referenz der EF Core-Tools: 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.

Hinweis

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. Globalisierungsanweisungen finden Sie unter 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.EntityFrameworkCore.DbContext abgeleitet und gibt die Entitäten an, die im Datenmodell enthalten sein sollen.

Beim Gerüstbau 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.

Dependency Injection

ASP.NET Core wird mit Abhängigkeitsinjektion (Dependency Injection, DI) erstellt. Dienste, z. B. der Datenbankkontext, müssen mit DI in Program.cs registriert werden. 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 Datenbankverbindungszeichenfolge „MvcMovieContext“.

Untersuchen der generierten Datenbankverbindungszeichenfolge

Beim Gerüstbau wurde der appsettings.json -Datei eine 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 ASP.NET Core-Konfigurationssystem den ConnectionString-Schlüssel aus der appsettings.json -Datei.

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");
        }
    }
}

Für den Code oben gilt:

  • 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 zur späten Bindung bietet, um Informationen 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üstbaumechanismus hat ein stark typisiertes Modell in 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 auf den Controller movies (das erste URL-Segment).
  • Die Aktion auf details (das zweite URL-Segment).
  • Die id auf 1 (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-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-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 View-Methode ein List-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 der Gerüstbau 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. 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 ein IEnumerable<Movie>-Objekt), wird jedes Element in der Schleife als Movie typisiert. Neben anderen Vorteilen überprüft der Compiler die im Code verwendeten Typen.

Zusätzliche 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-Konsole (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:

  • Den EF Core-SQL Server-Anbieter. 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.

Gerüstbau der Movie-Seiten

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 im Projektmappen-Explorer mit der rechten Maustaste auf den Ordner Controller, und wählen Sie Hinzufügen > Neues Gerüstelement 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.
  • Ansichten und Controllername: Übernehmen Sie die Standardeinstellung.
  • Wählen Sie Hinzufügen.

Datenkontext hinzufügen: Standardwerte behalten

Beim Gerüstbau werden folgende Aktualisierungen vorgenommen:

  • Die erforderlichen Paketverweise werden in die MvcMovie.csproj-Projektdatei eingefügt.
  • Registriert den Datenbankkontext in Startup.ConfigureServices der Datei Startup.cs.
  • Zur appsettings.json -Datei wird eine Datenbankverbindungszeichenfolge hinzugefügt.

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.

Anfängliche Migration

Verwenden Sie das EF Core-Feature für 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ü Tools die Option NuGet-Paket-Manager>Paket-Manager-Konsole aus.

Geben Sie die folgenden Befehle in die Paket-Manager-Konsole 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:

No type was specified for the decimal column 'Price' on entity type 'Movie'. Dadurch werden Werte automatisch abgeschnitten, falls diese nicht der Standardgenauigkeit und -skalierung entsprechen. Explicitly specify the SQL server column type that can accommodate all the values using 'HasColumnType()'. (Für die Spalte „Price“ mit Dezimalwerten für den Entitätstyp „Movie“ wurde kein Typ angegeben. Dadurch werden Werte automatisch abgeschnitten, falls diese nicht der Standardgenauigkeit und -skalierung entsprechen. Geben Sie den Spaltentyp von SQL-Server explizit an, der durch „ForHasColumnType()“ sämtliche Werte unterstützen 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 Referenz der EF Core-Tools: 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.

Hinweis

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. Globalisierungsanweisungen finden Sie unter 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.EntityFrameworkCore.DbContext abgeleitet und gibt die Entitäten an, die im Datenmodell enthalten sein sollen.

Beim Gerüstbau 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 Abhängigkeitsinjektion (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 Datenbankverbindungszeichenfolge „MvcMovieContext“.

Untersuchen der generierten Datenbankverbindungszeichenfolge

Beim Gerüstbau wurde der appsettings.json -Datei 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 ASP.NET Core-Konfigurationssystem den ConnectionString-Schlüssel aus der appsettings.json -Datei.

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");
    }
}

Für den Code oben gilt:

  • 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 zur späten Bindung bietet, um Informationen 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üstbaumechanismus hat ein stark typisiertes Modell in 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 auf den Controller movies (das erste URL-Segment).
  • Die Aktion auf details (das zweite URL-Segment).
  • Die id auf 1 (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-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-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 View-Methode ein List-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 der Gerüstbau 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. 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 ein IEnumerable<Movie>-Objekt), wird jedes Element in der Schleife als Movie typisiert. 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" der Datei appsettings.Development.json hinzu:

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

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

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

Zusätzliche 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-Konsole (PMC) aus.

PMC-Menü

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

Install-Package Microsoft.EntityFrameworkCore.SqlServer

Mit dem vorangehenden Befehl wird der EF Core-SQL Server-Anbieter hinzugefügt. 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 im Datenmodell enthalten sein 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 Abhängigkeitsinjektion (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 abruft, wird später in diesem Tutorial erläutert. In diesem Abschnitt registrieren Sie den Datenbankkontext mit dem DI-Container.

Fügen Sie am Anfang von Startup.cs die folgenden using-Anweisungen 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 der Verbindungszeichenfolge wird an den Kontext übergeben, indem Sie eine Methode auf einem DbContextOptions-Objekt aufrufen. Für die lokale Entwicklung liest das ASP.NET Core-Konfigurationssystem die Verbindungszeichenfolge aus der appsettings.json-Datei.

Untersuchen der Datenbankverbindungszeichenfolge

Fügen Sie in der appsettings.json -Datei 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.

Gerüstbau der Movie-Seiten

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

Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf den Ordner Controllers> Hinzufügen > Neues Gerüstelement.

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: Übernehmen Sie den Standardnamen MoviesController.
  • Wählen Sie Hinzufügen aus.

Visual Studio erstellt Folgendes:

  • Einen Filmcontroller (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.

Anfängliche Migration

Verwenden Sie das EF Core-Feature für Migrationen, 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-Konsole (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:

    No type was specified for the decimal column 'Price' on entity type 'Movie'. Dadurch werden Werte automatisch abgeschnitten, falls diese nicht der Standardgenauigkeit und -skalierung entsprechen. Explicitly specify the SQL server column type that can accommodate all the values using 'HasColumnType()'. (Für die Spalte „Price“ mit Dezimalwerten für den Entitätstyp „Movie“ wurde kein Typ angegeben. Dadurch werden Werte automatisch abgeschnitten, falls diese nicht der Standardgenauigkeit und -skalierung entsprechen. Geben Sie den Spaltentyp von SQL-Server explizit an, der durch „ForHasColumnType()“ sämtliche Werte unterstützen 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 Referenz der EF Core-Tools: 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.

    Hinweis

    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. Globalisierungsanweisungen finden Sie unter 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 zur späten Bindung bietet, um Informationen 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üstbaumechnismus hat diesen Ansatz (d.h. das Übergeben eines stark typisierten Modells) mit der MoviesController-Klasse und Ansichten 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 auf details (das zweite URL-Segment).
  • Die ID auf 1 (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-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 View-Methode ein List-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 der Gerüstbau automatisch die folgende @model-Anweisung am Anfang der Datei Index.cshtml hinzugefügt:

@model IEnumerable<MvcMovie.Models.Movie>

Diese @model-Direktive ermöglicht Ihnen 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 ein IEnumerable<Movie>-Objekt), wird jedes Element in der Schleife als Movie typisiert. Neben anderen Vorteilen bedeutet dies, dass Sie eine Überprüfung des Codes zur Kompilierzeit erhalten.

Zusätzliche Ressourcen