Partager via


Partie 4, ajoutez un modèle à une application MVC ASP.NET Core

Note

Ceci n’est pas la dernière version de cet article. Pour la version actuelle, consultez la version .NET 10 de cet article.

Warning

Cette version de ASP.NET Core n’est plus prise en charge. Pour plus d’informations, consultez la Politique de support .NET et .NET Core. Pour la version actuelle, consultez la version .NET 10 de cet article.

Dans ce tutoriel, des classes sont ajoutées pour la gestion des films dans une base de données. Ces classes constituent la partie « Modèle » de l’application MVC.

Ces classes de modèle sont utilisées avec Entity Framework Core (EF Core) pour travailler avec une base de données. EF Core est une infrastructure de mappage relationnel d’objets qui simplifie le code d’accès aux données à écrire.

Les classes du modèle créées sont appelées classes POCO, à partir de Plain Old CLR Objects. Les classes POCO n’ont aucune dépendance vis-à-vis de EF Core. Elles définissent uniquement les propriétés des données qui seront stockées dans la base de données.

Dans ce tutoriel, les classes du modèle sont d’abord créées, puis EF Core crée la base de données.

Ajouter une classe de modèle de données

Cliquez avec le bouton droit sur le dossier Modèles>Ajouter>Classe. Nommez le fichier Movie.cs.

Mettez à jour le fichier Models/Movie.cs avec le code suivant :

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

La classe Movie contient un champ Id, qui est nécessaire à la base de données pour la clé primaire.

L’attribut DataType de ReleaseDate spécifie le type de données (Date). Avec cet attribut :

  • L’utilisateur n’est pas obligé d’entrer les informations de temps dans le champ de date.
  • Seule la date est affichée, pas les informations de temps.

Les DataAnnotations sont traitées dans un prochain didacticiel.

La présence du point d’interrogation après string indique que la propriété est nullable. Pour plus d’informations, consultez Types de références annulables.

Ajouter des packages NuGet

Visual Studio installe automatiquement les packages requis.

Générez le projet en tant que vérification des erreurs du compilateur.

Générer automatiquement des pages de film

Utilisez l’outil de scaffolding pour produire Create, Read, Update et Delete (CRUD) des pages pour le modèle de film.

Dans Explorateur de solutions, cliquez avec le bouton droit sur le dossier Controllers et sélectionnez Add > Nouvel élément généré automatiquement.

Affichage de l’élément de menu pour ajouter un nouvel élément généré automatiquement.

Dans la boîte de dialogue Ajouter un nouvel élément scaffoldé :

  • Dans le volet gauche, sélectionnez Installé>Commun>MVC.
  • Sélectionnez Contrôleur MVC avec vues, utilisant Entity Framework.
  • Sélectionnez Ajouter.

Boîte de dialogue Ajouter un Scaffold.

Terminez la boîte de dialogue Ajouter un contrôleur MVC avec des vues, à l’aide d’Entity Framework :

  • Dans la liste déroulante Classe du modèle, sélectionnez Film (MvcMovie.Models).
  • Dans la ligne Classe du contexte de données, sélectionnez le signe + (plus).
    • Dans la boîte de dialogue Ajouter un contexte de données, le nom de classe MvcMovie.Data.MvcMovieContext est généré.
    • Sélectionnez Ajouter.
  • Dans le menu déroulant fournisseur de base de données, sélectionnez SQL Server.
  • Vues et Nom du contrôleur : conservez la valeur par défaut.
  • Sélectionnez Ajouter.

Ajouter un contexte de données en gardant les valeurs par défaut.

Si vous obtenez un message d’erreur, sélectionnez Ajouter une deuxième fois pour réessayer.

L’échafaudage ajoute les packages suivants :

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

Le scaffolding crée les éléments suivants :

  • Un contrôleur de films : Controllers/MoviesController.cs
  • Razor afficher les fichiers pour les pages Créer, Supprimer, Détails, Modifier et Indexer : Views/Movies/*.cshtml
  • Une classe de contexte de base de données : Data/MvcMovieContext.cs

Le scaffolding met à jour les éléments suivants :

  • Insère les références de package requises dans le fichier projet MvcMovie.csproj.
  • Inscrit le contexte de base de données dans le fichier Program.cs.
  • Ajoute une chaîne de connexion de base de données au fichier appsettings.json.

La création automatique de ces fichiers et mises à jour est appelée génération de modèles automatique.

Les pages générées automatiquement ne peuvent pas encore être utilisées, car la base de données n’existe pas. L’exécution de l’application et la sélection du lien Application vidéo entraînent un message d’erreur Impossible d’ouvrir la base de données ou Le type de table suivant n’existe pas : Film.

Générez l’application pour vérifier qu’il n’y a pas d’erreurs.

Migration initiale

Utilisez la fonctionnalité EF CoreMigrations pour créer la base de données. La fonctionnalité Migrations est un ensemble d’outils qui vous permettent de créer et de mettre à jour une base de données pour qu’elle corresponde à votre modèle de données.

Dans le menu Tools, sélectionnez NuGet Gestionnaire de package>Gestionnaire de package Console .

Dans la console Gestionnaire de package (PMC), entrez la commande suivante :

Add-Migration InitialCreate

  • Add-Migration InitialCreate: Génère un Migrations/{timestamp}_InitialCreate.cs fichier de migration. L’argument InitialCreate est le nom de la migration. Vous pouvez utiliser n’importe quel nom, mais par convention, un nom décrivant la migration est sélectionné. Étant donné qu’il s’agit de la première migration, la classe générée contient du code permettant de créer le schéma de la base de données. Le schéma de base de données est basé sur le modèle spécifié dans la classe MvcMovieContext.

L’avertissement suivant s’affiche et sera traité dans une étape ultérieure :

Aucun type de magasin n’a été spécifié pour la propriété décimale « Price » sur le type d’entité « Movie ». Les valeurs sont tronquées en mode silencieux si elles ne sont pas compatibles avec la précision et l’échelle par défaut. Spécifiez explicitement le type de colonne SQL Server qui peut prendre en charge toutes les valeurs dans « OnModelCreating » à l’aide de « HasColumnType », spécifiez la précision et l’échelle à l’aide de « HasPrecision » ou configurez un convertisseur de valeurs à l’aide de « HasConversion ».

Dans la console PMC, entrez la commande suivante :

Update-Database

  • Update-Database : Met à jour la base de données vers la dernière migration, qui a été créée par la commande précédente. Cette commande exécute la méthode Up dans le fichier Migrations/{time-stamp}_InitialCreate.cs, qui crée la base de données.

Pour plus d’informations sur les outils PMC pour EF Core, consultez EF Core tools reference - PMC dans Visual Studio.

Tester l’application

Exécutez l’application et sélectionnez sur le lien Movie App.

Application montrant l’affichage De l’application vidéo.

Si vous obtenez une exception semblable à ce qui suit, vous avez peut-être manqué la commande Update-Database à l’étape des migrations :

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

Note

Vous ne pourrez peut-être pas entrer de virgules décimales dans le champ Price. Pour prendre en charge la validation jQuery pour les paramètres régionaux autres que « Anglais » qui utilisent une virgule (« , ») comme décimale et des formats de date autres que le format « Anglais (États-Unis »), l’application doit être localisée. Pour obtenir des instructions sur la mondialisation, consultez ce problème GitHub.

Examinez la classe de contexte de base de données générée et l’enregistrement

Avec EF Core, l’accès aux données est effectué à l’aide d’un modèle. Un modèle est constitué de classes d’entité et d’un objet de contexte qui représente une session avec la base de données. L’objet de contexte permet l’interrogation et l’enregistrement des données. Le contexte de base de données est dérivé de Microsoft. EntityFrameworkCore.DbContext et spécifie les entités à inclure dans le modèle de données.

La génération de modèles crée la classe Data/MvcMovieContext.cs de contexte de base de données :

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!;
    }
}

Le code précédent crée une propriété DbSet<Movie> qui représente les films dans la base de données.

Injection de dépendances

ASP.NET Core est généré avec dependency injection (DI). Les services, tels que le contexte de base de données, sont enregistrés dans DI dans Program.cs. Ces services sont fournis aux composants qui en ont besoin via des paramètres de constructeur.

Dans le fichier Controllers/MoviesController.cs, le constructeur utilise une injection de dépendance pour injecter le contexte de base de données MvcMovieContext dans le contrôleur. Le contexte de base de données est utilisé dans chacune des méthodes CRUD du contrôleur.

La génération de l'échafaudage a produit le code suivant en surbrillance dans Program.cs :

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

Le système de configuration ASP.NET Core lit la chaîne de connexion à la base de données « MvcMovieContext ».

Examiner la chaîne de connexion de la base de données générée

L'échafaudage a ajouté une chaîne de connexion au fichier appsettings.json :

{
  "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"
  }
}

Pour le développement local, le système de configuration ASP.NET Core lit la clé ConnectionString à partir du fichier appsettings.json.

La classe InitialCreate

Examinez le fichier de migration 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");
        }
    }
}

Dans le code précédent :

  • InitialCreate.Up crée la table Film et configure Id comme clé primaire.
  • InitialCreate.Down rétablit les modifications de schéma provoquées par la migration Up.

Injection de dépendances dans le contrôleur

Ouvrez le fichier Controllers/MoviesController.cs et examinez le constructeur :

public class MoviesController : Controller
{
    private readonly MvcMovieContext _context;

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

Le constructeur utilise une injection de dépendance pour injecter le contexte de base de données (MvcMovieContext) dans le contrôleur. Le contexte de base de données est utilisé dans chacune des méthodes CRUD du contrôleur.

Testez la page Create. Entrez et envoyez des données.

Testez les pages Edit, Details et Delete.

Modèles fortement typés et directive @model

Plus tôt dans ce didacticiel, vous avez vu comment un contrôleur peut passer des données ou des objets à une vue en utilisant le dictionnaire ViewData. Le dictionnaire ViewData est un objet dynamique qui permet d'utiliser le binding tardif de manière pratique pour transmettre des informations à une vue.

Le modèle MVC fournit la possibilité de passer des objets de modèle fortement typés à une vue. Cette approche fortement typée permet de vérifier votre code au moment de la compilation. Le mécanisme d'échafaudage a passé un modèle fortement typé dans la classe MoviesController et les vues.

Examinez la méthode Details générée dans le fichier 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);
}

Le paramètre id est généralement passé en tant que données de routage. Par exemple, https://localhost:{PORT}/movies/details/1 définit :

  • Le contrôleur associé au contrôleur movies, le premier segment de l’URL.
  • L’action sur details, le deuxième segment de l’URL.
  • Réglez id sur 1, pour le dernier segment de l'URL.

Le id peut être transmis avec une chaîne de requête, comme dans l’exemple suivant :

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

Le paramètre id est défini comme type Nullable (int?) au cas où la valeur id n’est pas fournie.

Une expression lambda est passée à la méthode FirstOrDefaultAsync pour sélectionner les entités de film qui correspondent aux données de routage ou à la valeur de la chaîne de requête.

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

Si un film est trouvé, une instance du modèle Movie est passée à la vue Details :

return View(movie);

Examinez le contenu du fichier 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>

L’instruction @model située en haut du fichier de la vue spécifie le type d’objet attendu par la vue. Lorsque le contrôleur de film était créé, l’instruction @model suivante était incluse :

@model MvcMovie.Models.Movie

Cette directive @model autorise l’accès au film que le contrôleur a passé à la vue. L’objet Model est fortement typé. Par exemple, dans la vue Details.cshtml, le code passe chaque champ du film aux Helpers HTML DisplayNameFor et DisplayFor avec l’objet Model fortement typé. Les méthodes et les vues Create et Edit passent aussi un objet du modèle Movie.

Examinez la vue Index.cshtml et la méthode Index dans le contrôleur Movies. Notez comment le code crée un objet List quand il appelle la méthode View. Le code passe cette liste Movies de la méthode d’action Index à la vue :

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

Le code retourne les détails du problème si la propriété Movie du contexte de données est null.

Lors de la création du contrôleur des films, le scaffolding a inclus l'instruction @model suivante en haut du fichier Index.cshtml :

@model IEnumerable<MvcMovie.Models.Movie>

La directive @model permet d’accéder à la liste des films que le contrôleur a passé à la vue en utilisant un objet Model qui est fortement typé. Par exemple, dans la vue Index.cshtml, le code parcourt les films avec une instruction foreach sur l’objet Model fortement typé.

@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>

Comme l’objet Model est fortement typé en tant qu’objet IEnumerable<Movie>, chaque élément de la boucle est typé en tant que Movie. Entre autres avantages, le compilateur valide les types utilisés dans le code.

Ressources supplémentaires

Dans ce tutoriel, des classes sont ajoutées pour la gestion des films dans une base de données. Ces classes constituent la partie « Modèle » de l’application MVC.

Ces classes de modèle sont utilisées avec Entity Framework Core (EF Core) pour travailler avec une base de données. EF Core est une infrastructure de mappage relationnel d’objets qui simplifie le code d’accès aux données à écrire.

Les classes du modèle créées sont appelées classes POCO, à partir de Plain Old CLR Objects. Les classes POCO n’ont aucune dépendance vis-à-vis de EF Core. Elles définissent uniquement les propriétés des données qui seront stockées dans la base de données.

Dans ce tutoriel, les classes du modèle sont d’abord créées, puis EF Core crée la base de données.

Ajouter une classe de modèle de données

Cliquez avec le bouton droit sur le dossier Modèles>Ajouter>Classe. Nommez le fichier Movie.cs.

Mettez à jour le fichier Models/Movie.cs avec le code suivant :

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

La classe Movie contient un champ Id, qui est nécessaire à la base de données pour la clé primaire.

L’attribut DataType de ReleaseDate spécifie le type de données (Date). Avec cet attribut :

  • L’utilisateur n’est pas obligé d’entrer les informations de temps dans le champ de date.
  • Seule la date est affichée, pas les informations de temps.

Les DataAnnotations sont traitées dans un prochain didacticiel.

La présence du point d'interrogation après string indique que la propriété est nullable. Pour plus d’informations, consultez types de référence nullables.

Ajouter des packages NuGet

Visual Studio installe automatiquement les packages requis.

Générez le projet en tant que vérification des erreurs du compilateur.

Générer automatiquement des pages de film

Utilisez l’outil de scaffolding pour générer des pages Create, Read, Update, et Delete (CRUD) pour le modèle de film.

Dans Explorateur de solutions, cliquez avec le bouton droit sur le dossier Controllers et sélectionnez Ajouter > Nouvel élément généré automatiquement.

affichage de l’étape ci-dessus

Dans la boîte de dialogue Ajouter un nouvel élément échafaudé :

  • Dans le volet gauche, sélectionnez Installé>Commun>MVC.
  • Sélectionnez Contrôleur MVC avec vues, utilisant Entity Framework.
  • Sélectionnez Ajouter.

Boîte de dialogue Ajouter une échafaudage

Terminez la boîte de dialogue Ajouter un contrôleur MVC avec des vues, à l’aide d’Entity Framework :

  • Dans la liste déroulante Classe du modèle, sélectionnez Film (MvcMovie.Models).
  • Dans la ligne Classe du contexte de données, sélectionnez le signe + (plus).
    • Dans la boîte de dialogue Ajouter un contexte de données, le nom de classe MvcMovie.Data.MvcMovieContext est généré.
    • Sélectionnez Ajouter.
  • Dans la liste déroulante fournisseur de base de données, sélectionnez SQL Server.
  • Vues et Nom du contrôleur : conservez la valeur par défaut.
  • Sélectionnez Ajouter.

Ajouter un contexte de données – conserver les valeurs par défaut

Si vous obtenez un message d’erreur, sélectionnez Ajouter une deuxième fois pour réessayer.

Le scaffolding ajoute les paquets suivants :

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

L'échafaudage crée les éléments suivants :

  • Un contrôleur de films : Controllers/MoviesController.cs
  • Razor afficher les fichiers pour les pages Créer, Supprimer, Détails, Modifier et Indexer : Views/Movies/*.cshtml
  • Une classe de contexte de base de données : Data/MvcMovieContext.cs

La génération de modèles automatique met à jour les éléments suivants :

  • Insère les références de package requises dans le fichier projet MvcMovie.csproj.
  • Inscrit le contexte de base de données dans le fichier Program.cs.
  • Ajoute une chaîne de connexion de base de données au fichier appsettings.json.

La création automatique de ces fichiers et la mise à jour des fichiers sont connues sous le nom de scaffolding.

Les pages générées automatiquement ne peuvent pas encore être utilisées, car la base de données n’existe pas. L’exécution de l’application et la sélection du lien Application vidéo entraînent un message d’erreur Impossible d’ouvrir la base de données ou Le type de table suivant n’existe pas : Film.

Générez l’application pour vérifier qu’il n’y a pas d’erreurs.

Migration initiale

Utilisez la fonctionnalité EF CoreMigrations pour créer la base de données. La fonctionnalité Migrations est un ensemble d’outils qui vous permettent de créer et de mettre à jour une base de données pour qu’elle corresponde à votre modèle de données.

Dans le menu Tools, sélectionnez NuGet Gestionnaire de package>Gestionnaire de package Console .

Dans la console Gestionnaire de package (PMC), entrez la commande suivante :

Add-Migration InitialCreate

  • Add-Migration InitialCreate: Génère un Migrations/{timestamp}_InitialCreate.cs fichier de migration. L’argument InitialCreate est le nom de la migration. Vous pouvez utiliser n’importe quel nom, mais par convention, un nom décrivant la migration est sélectionné. Étant donné qu’il s’agit de la première migration, la classe générée contient du code permettant de créer le schéma de la base de données. Le schéma de base de données est basé sur le modèle spécifié dans la classe MvcMovieContext.

L’avertissement suivant s’affiche et sera traité dans une étape ultérieure :

Aucun type de magasin n’a été spécifié pour la propriété décimale « Price » sur le type d’entité « Movie ». Les valeurs sont tronquées en mode silencieux si elles ne sont pas compatibles avec la précision et l’échelle par défaut. Spécifiez explicitement le type de colonne SQL Server qui peut prendre en charge toutes les valeurs dans « OnModelCreating » à l’aide de « HasColumnType », spécifiez la précision et l’échelle à l’aide de « HasPrecision » ou configurez un convertisseur de valeurs à l’aide de « HasConversion ».

Dans la console PMC, entrez la commande suivante :

Update-Database

  • Update-Database : Met à jour la base de données vers la dernière migration, qui a été créée par la commande précédente. Cette commande exécute la méthode Up dans le fichier Migrations/{time-stamp}_InitialCreate.cs, qui crée la base de données.

Pour plus d’informations sur les outils PMC pour EF Core, consultez EF Core tools reference - PMC dans Visual Studio.

Tester l’application

Exécutez l’application et sélectionnez sur le lien Movie App.

Si vous obtenez une exception semblable à ce qui suit, vous avez peut-être manqué la commande Update-Database à l’étape des migrations :

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

Note

Vous ne pourrez peut-être pas entrer de virgules décimales dans le champ Price. Pour prendre en charge la validation jQuery pour les paramètres régionaux autres que « Anglais » qui utilisent une virgule (« , ») comme décimale et des formats de date autres que le format « Anglais (États-Unis »), l’application doit être localisée. Pour obtenir des instructions sur la mondialisation, consultez ce problème GitHub.

Examinez la classe de contexte de base de données générée et l’enregistrement

Avec EF Core, l’accès aux données est effectué à l’aide d’un modèle. Un modèle est constitué de classes d’entité et d’un objet de contexte qui représente une session avec la base de données. L’objet de contexte permet l’interrogation et l’enregistrement des données. Le contexte de base de données est dérivé de Microsoft. EntityFrameworkCore.DbContext et spécifie les entités à inclure dans le modèle de données.

La génération de modèles crée la classe Data/MvcMovieContext.cs de contexte de base de données :

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!;
    }
}

Le code précédent crée une propriété DbSet<Movie> qui représente les films dans la base de données.

Injection de dépendances

ASP.NET Core est généré avec dependency injection (DI). Les services, tels que le contexte de base de données, sont enregistrés dans DI dans Program.cs. Ces services sont fournis aux composants qui en ont besoin via des paramètres de constructeur.

Dans le fichier Controllers/MoviesController.cs, le constructeur utilise une injection de dépendance pour injecter le contexte de base de données MvcMovieContext dans le contrôleur. Le contexte de base de données est utilisé dans chacune des méthodes CRUD du contrôleur.

La génération de l'échafaudage a produit le code suivant en surbrillance dans Program.cs :

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

Le système de configuration ASP.NET Core lit la chaîne de connexion à la base de données « MvcMovieContext ».

Examiner la chaîne de connexion de la base de données générée

L'échafaudage a ajouté une chaîne de connexion au fichier appsettings.json :

{
  "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"
  }
}

Pour le développement local, le système de configuration ASP.NET Core lit la clé ConnectionString à partir du fichier appsettings.json.

La classe InitialCreate

Examinez le fichier de migration 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");
        }
    }
}

Dans le code précédent :

  • InitialCreate.Up crée la table Film et configure Id comme clé primaire.
  • InitialCreate.Down rétablit les modifications de schéma provoquées par la migration Up.

Injection de dépendances dans le contrôleur

Ouvrez le fichier Controllers/MoviesController.cs et examinez le constructeur :

public class MoviesController : Controller
{
    private readonly MvcMovieContext _context;

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

Le constructeur utilise une injection de dépendance pour injecter le contexte de base de données (MvcMovieContext) dans le contrôleur. Le contexte de base de données est utilisé dans chacune des méthodes CRUD du contrôleur.

Testez la page Create. Entrez et envoyez des données.

Testez les pages Edit, Details et Delete.

Modèles fortement typés et directive @model

Plus tôt dans ce didacticiel, vous avez vu comment un contrôleur peut passer des données ou des objets à une vue en utilisant le dictionnaire ViewData. Le dictionnaire ViewData est un objet dynamique qui permet d'utiliser le binding tardif de manière pratique pour transmettre des informations à une vue.

Le modèle MVC fournit la possibilité de passer des objets de modèle fortement typés à une vue. Cette approche fortement typée permet de vérifier votre code au moment de la compilation. Le mécanisme d'échafaudage a passé un modèle fortement typé dans la classe MoviesController et les vues.

Examinez la méthode Details générée dans le fichier 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);
}

Le paramètre id est généralement passé en tant que données de routage. Par exemple, https://localhost:{PORT}/movies/details/1 définit :

  • Le contrôleur associé au contrôleur movies, le premier segment de l’URL.
  • L’action sur details, le deuxième segment de l’URL.
  • Réglez id sur 1, pour le dernier segment de l'URL.

Le id peut être transmis avec une chaîne de requête, comme dans l’exemple suivant :

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

Le paramètre id est défini comme type Nullable (int?) au cas où la valeur id n’est pas fournie.

Une expression lambda est passée à la méthode FirstOrDefaultAsync pour sélectionner les entités de film qui correspondent aux données de routage ou à la valeur de la chaîne de requête.

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

Si un film est trouvé, une instance du modèle Movie est passée à la vue Details :

return View(movie);

Examinez le contenu du fichier 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>

L’instruction @model située en haut du fichier de la vue spécifie le type d’objet attendu par la vue. Lorsque le contrôleur de film était créé, l’instruction @model suivante était incluse :

@model MvcMovie.Models.Movie

Cette directive @model autorise l’accès au film que le contrôleur a passé à la vue. L’objet Model est fortement typé. Par exemple, dans la vue Details.cshtml, le code passe chaque champ du film aux Helpers HTML DisplayNameFor et DisplayFor avec l’objet Model fortement typé. Les méthodes et les vues Create et Edit passent aussi un objet du modèle Movie.

Examinez la vue Index.cshtml et la méthode Index dans le contrôleur Movies. Notez comment le code crée un objet List quand il appelle la méthode View. Le code passe cette liste Movies de la méthode d’action Index à la vue :

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

Le code retourne les détails du problème si la propriété Movie du contexte de données est null.

Lors de la création du contrôleur des films, le scaffolding a inclus l'instruction @model suivante en haut du fichier Index.cshtml :

@model IEnumerable<MvcMovie.Models.Movie>

La directive @model permet d’accéder à la liste des films que le contrôleur a passé à la vue en utilisant un objet Model qui est fortement typé. Par exemple, dans la vue Index.cshtml, le code parcourt les films avec une instruction foreach sur l’objet Model fortement typé.

@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>

Comme l’objet Model est fortement typé en tant qu’objet IEnumerable<Movie>, chaque élément de la boucle est typé en tant que Movie. Entre autres avantages, le compilateur valide les types utilisés dans le code.

Ressources supplémentaires

Dans ce tutoriel, des classes sont ajoutées pour la gestion des films dans une base de données. Ces classes constituent la partie « Modèle » de l’application MVC.

Ces classes de modèle sont utilisées avec Entity Framework Core (EF Core) pour travailler avec une base de données. EF Core est une infrastructure de mappage relationnel d’objets qui simplifie le code d’accès aux données à écrire.

Les classes du modèle créées sont appelées classes POCO, à partir de Plain Old CLR Objects. Les classes POCO n’ont aucune dépendance vis-à-vis de EF Core. Elles définissent uniquement les propriétés des données qui seront stockées dans la base de données.

Dans ce tutoriel, les classes du modèle sont d’abord créées, puis EF Core crée la base de données.

Ajouter une classe de modèle de données

Cliquez avec le bouton droit sur le dossier Modèles>Ajouter>Classe. Nommez le fichier Movie.cs.

Mettez à jour le fichier Models/Movie.cs avec le code suivant :

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

La classe Movie contient un champ Id, qui est nécessaire à la base de données pour la clé primaire.

L’attribut DataType de ReleaseDate spécifie le type de données (Date). Avec cet attribut :

  • L’utilisateur n’est pas obligé d’entrer les informations de temps dans le champ de date.
  • Seule la date est affichée, pas les informations de temps.

Les DataAnnotations sont traitées dans un prochain didacticiel.

La présence du point d'interrogation après string indique que la propriété est nullable. Pour plus d’informations, consultez types de référence nullables.

Ajouter des packages NuGet

Visual Studio installe automatiquement les packages requis.

Générez le projet en tant que vérification des erreurs du compilateur.

Générer automatiquement des pages de film

Utilisez l’outil de scaffolding pour produire Create, Read, Update et Delete (CRUD) des pages pour le modèle de film.

Dans Explorateur de solutions, cliquez avec le bouton droit sur le dossier Controllers et sélectionnez Add > Nouvel élément généré automatiquement.

affichage de l’étape ci-dessus

Dans la boîte de dialogue Ajouter un nouvel élément scaffoldé :

  • Dans le volet gauche, sélectionnez Installé>Commun>MVC.
  • Sélectionnez Contrôleur MVC avec vues, utilisant Entity Framework.
  • Sélectionnez Ajouter.

Boîte de dialogue Ajouter une échafaudage

Terminez la boîte de dialogue Ajouter un contrôleur MVC avec des vues, à l’aide d’Entity Framework :

  • Dans la liste déroulante Classe du modèle, sélectionnez Film (MvcMovie.Models).
  • Dans la ligne Classe du contexte de données, sélectionnez le signe + (plus).
    • Dans la boîte de dialogue Ajouter un contexte de données, le nom de classe MvcMovie.Data.MvcMovieContext est généré.
    • Sélectionnez Ajouter.
  • Dans le menu déroulant fournisseur de base de données, sélectionnez SQL Server.
  • Vues et Nom du contrôleur : conservez la valeur par défaut.
  • Sélectionnez Ajouter.

Ajouter un contexte de données – conserver les valeurs par défaut

Si vous obtenez un message d’erreur, sélectionnez Ajouter une deuxième fois pour réessayer.

L’échafaudage ajoute les packages suivants :

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

L'échafaudage crée les éléments suivants :

  • Un contrôleur de films : Controllers/MoviesController.cs
  • Razor afficher les fichiers pour les pages Créer, Supprimer, Détails, Modifier et Indexer : Views/Movies/*.cshtml
  • Une classe de contexte de base de données : Data/MvcMovieContext.cs

La structure de code met à jour les éléments suivants :

  • Insère les références de package requises dans le fichier projet MvcMovie.csproj.
  • Inscrit le contexte de base de données dans le fichier Program.cs.
  • Ajoute une chaîne de connexion de base de données au fichier appsettings.json.

La création automatique de ces fichiers et mises à jour est appelée génération de modèles automatique.

Les pages échafaudées ne sont pas encore utilisables, car la base de données n’existe pas. L’exécution de l’application et la sélection du lien Application vidéo entraînent un message d’erreur Impossible d’ouvrir la base de données ou Le type de table suivant n’existe pas : Film.

Générez l’application pour vérifier qu’il n’y a pas d’erreurs.

Migration initiale

Utilisez la fonctionnalité EF CoreMigrations pour créer la base de données. La fonctionnalité Migrations est un ensemble d’outils qui vous permettent de créer et de mettre à jour une base de données pour qu’elle corresponde à votre modèle de données.

Dans le menu Tools, sélectionnez NuGet Gestionnaire de package>Gestionnaire de package Console .

Dans la console Gestionnaire de package (PMC), entrez les commandes suivantes :

Add-Migration InitialCreate
Update-Database

  • Add-Migration InitialCreate: Génère un Migrations/{timestamp}_InitialCreate.cs fichier de migration. L’argument InitialCreate est le nom de la migration. Vous pouvez utiliser n’importe quel nom, mais par convention, un nom décrivant la migration est sélectionné. Étant donné qu’il s’agit de la première migration, la classe générée contient du code permettant de créer le schéma de la base de données. Le schéma de base de données est basé sur le modèle spécifié dans la classe MvcMovieContext.

  • Update-Database : Met à jour la base de données vers la dernière migration, qui a été créée par la commande précédente. Cette commande exécute la méthode Up dans le fichier Migrations/{time-stamp}_InitialCreate.cs, qui crée la base de données.

La commande Update-Database génère l’avertissement suivant :

Aucun type de magasin n’a été spécifié pour la propriété décimale « Price » sur le type d’entité « Movie ». Les valeurs sont tronquées en mode silencieux si elles ne sont pas compatibles avec la précision et l’échelle par défaut. Spécifiez explicitement le type de colonne SQL Server qui peut prendre en charge toutes les valeurs dans « OnModelCreating » à l’aide de « HasColumnType », spécifiez la précision et l’échelle à l’aide de « HasPrecision » ou configurez un convertisseur de valeurs à l’aide de « HasConversion ».

Ignorez l’avertissement précédent, il est résolu dans un tutoriel ultérieur.

Pour plus d’informations sur les outils PMC pour EF Core, consultez EF Core tools reference - PMC dans Visual Studio.

Tester l’application

Exécutez l’application et sélectionnez sur le lien Movie App.

Si vous obtenez une exception semblable à ce qui suit, vous avez peut-être manqué la commande Update-Database à l’étape des migrations :

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

Note

Vous ne pourrez peut-être pas entrer de virgules décimales dans le champ Price. Pour prendre en charge la validation jQuery pour les paramètres régionaux autres que « Anglais » qui utilisent une virgule (« , ») comme décimale et des formats de date autres que le format « Anglais (États-Unis »), l’application doit être localisée. Pour obtenir des instructions sur la mondialisation, consultez ce problème GitHub.

Examinez la classe de contexte de base de données générée et l’enregistrement

Avec EF Core, l’accès aux données est effectué à l’aide d’un modèle. Un modèle est constitué de classes d’entité et d’un objet de contexte qui représente une session avec la base de données. L’objet de contexte permet l’interrogation et l’enregistrement des données. Le contexte de base de données est dérivé de Microsoft. EntityFrameworkCore.DbContext et spécifie les entités à inclure dans le modèle de données.

La génération de modèles crée la classe Data/MvcMovieContext.cs de contexte de base de données :

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

Le code précédent crée une propriété DbSet<Movie> qui représente les films dans la base de données.

Injection de dépendances

ASP.NET Core est généré avec dependency injection (DI). Les services, tels que le contexte de base de données, sont enregistrés dans DI dans Program.cs. Ces services sont fournis aux composants qui en ont besoin via des paramètres de constructeur.

Dans le fichier Controllers/MoviesController.cs, le constructeur utilise une injection de dépendance pour injecter le contexte de base de données MvcMovieContext dans le contrôleur. Le contexte de base de données est utilisé dans chacune des méthodes CRUD du contrôleur.

La génération de l'échafaudage a produit le code suivant en surbrillance dans Program.cs :

var builder = WebApplication.CreateBuilder(args);

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

Le système de configuration ASP.NET Core lit la chaîne de connexion à la base de données « MvcMovieContext ».

Examiner la chaîne de connexion de la base de données générée

L'échafaudage a ajouté une chaîne de connexion au fichier appsettings.json :

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

Pour le développement local, le système de configuration ASP.NET Core lit la clé ConnectionString à partir du fichier appsettings.json.

La classe InitialCreate

Examinez le fichier de migration 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");
        }
    }
}

Dans le code précédent :

  • InitialCreate.Up crée la table Film et configure Id comme clé primaire.
  • InitialCreate.Down rétablit les modifications de schéma provoquées par la migration Up.

Injection de dépendances dans le contrôleur

Ouvrez le fichier Controllers/MoviesController.cs et examinez le constructeur :

public class MoviesController : Controller
{
    private readonly MvcMovieContext _context;

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

Le constructeur utilise une injection de dépendance pour injecter le contexte de base de données (MvcMovieContext) dans le contrôleur. Le contexte de base de données est utilisé dans chacune des méthodes CRUD du contrôleur.

Testez la page Create. Entrez et envoyez des données.

Testez les pages Edit, Details et Delete.

Modèles fortement typés et directive @model

Plus tôt dans ce didacticiel, vous avez vu comment un contrôleur peut passer des données ou des objets à une vue en utilisant le dictionnaire ViewData. Le dictionnaire ViewData est un objet dynamique qui permet d'utiliser le binding tardif de manière pratique pour transmettre des informations à une vue.

Le modèle MVC fournit la possibilité de passer des objets de modèle fortement typés à une vue. Cette approche fortement typée permet de vérifier votre code au moment de la compilation. Le mécanisme d'échafaudage a passé un modèle fortement typé dans la classe MoviesController et les vues.

Examinez la méthode Details générée dans le fichier 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);
}

Le paramètre id est généralement passé en tant que données de routage. Par exemple, https://localhost:5001/movies/details/1 définit :

  • Le contrôleur associé au contrôleur movies, le premier segment de l’URL.
  • L’action sur details, le deuxième segment de l’URL.
  • Réglez id sur 1, pour le dernier segment de l'URL.

Le id peut être transmis avec une chaîne de requête, comme dans l’exemple suivant :

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

Le paramètre id est défini comme type Nullable (int?) au cas où la valeur id n’est pas fournie.

Une expression lambda est passée à la méthode FirstOrDefaultAsync pour sélectionner les entités de film qui correspondent aux données de routage ou à la valeur de la chaîne de requête.

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

Si un film est trouvé, une instance du modèle Movie est passée à la vue Details :

return View(movie);

Examinez le contenu du fichier 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>

L’instruction @model située en haut du fichier de la vue spécifie le type d’objet attendu par la vue. Lorsque le contrôleur de film était créé, l’instruction @model suivante était incluse :

@model MvcMovie.Models.Movie

Cette directive @model autorise l’accès au film que le contrôleur a passé à la vue. L’objet Model est fortement typé. Par exemple, dans la vue Details.cshtml, le code passe chaque champ du film aux Helpers HTML DisplayNameFor et DisplayFor avec l’objet Model fortement typé. Les méthodes et les vues Create et Edit passent aussi un objet du modèle Movie.

Examinez la vue Index.cshtml et la méthode Index dans le contrôleur Movies. Notez comment le code crée un objet List quand il appelle la méthode View. Le code passe cette liste Movies de la méthode d’action Index à la vue :

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

Le code retourne les détails du problème si la propriété Movie du contexte de données est null.

Lors de la création du contrôleur des films, le scaffolding a inclus l'instruction @model suivante en haut du fichier Index.cshtml :

@model IEnumerable<MvcMovie.Models.Movie>

La directive @model permet d’accéder à la liste des films que le contrôleur a passé à la vue en utilisant un objet Model qui est fortement typé. Par exemple, dans la vue Index.cshtml, le code parcourt les films avec une instruction foreach sur l’objet Model fortement typé.

@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>

Comme l’objet Model est fortement typé en tant qu’objet IEnumerable<Movie>, chaque élément de la boucle est typé en tant que Movie. Entre autres avantages, le compilateur valide les types utilisés dans le code.

Ressources supplémentaires

Dans ce tutoriel, des classes sont ajoutées pour la gestion des films dans une base de données. Ces classes constituent la partie « Modèle » de l’application MVC.

Ces classes de modèle sont utilisées avec Entity Framework Core (EF Core) pour travailler avec une base de données. EF Core est une infrastructure de mappage relationnel d’objets qui simplifie le code d’accès aux données à écrire.

Les classes du modèle créées sont appelées classes POCO, à partir de Plain Old CLR Objects. Les classes POCO n’ont aucune dépendance vis-à-vis de EF Core. Elles définissent uniquement les propriétés des données qui seront stockées dans la base de données.

Dans ce tutoriel, les classes du modèle sont d’abord créées, puis EF Core crée la base de données.

Ajouter une classe de modèle de données

Cliquez avec le bouton droit sur le dossier Modèles>Ajouter>Classe. Nommez le fichier Movie.cs.

Mettez à jour le fichier Models/Movie.cs avec le code suivant :

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

La classe Movie contient un champ Id, qui est nécessaire à la base de données pour la clé primaire.

L’attribut DataType de ReleaseDate spécifie le type de données (Date). Avec cet attribut :

  • L’utilisateur n’est pas obligé d’entrer les informations de temps dans le champ de date.
  • Seule la date est affichée, pas les informations de temps.

Les DataAnnotations sont traitées dans un prochain didacticiel.

La présence du point d'interrogation après string indique que la propriété est nullable. Pour plus d’informations, consultez types de référence nullables.

Ajouter des packages NuGet

Visual Studio installe automatiquement les packages requis.

Générez le projet en tant que vérification des erreurs du compilateur.

Générer automatiquement des pages de film

Utilisez l’outil de scaffolding pour produire Create, Read, Update et Delete (CRUD) des pages pour le modèle de film.

Dans Explorateur de solutions, cliquez avec le bouton droit sur le dossier Controllers et sélectionnez Add > Nouvel élément généré automatiquement.

affichage de l’étape ci-dessus

Dans la boîte de dialogue Ajouter un nouvel élément scaffoldé :

  • Dans le volet gauche, sélectionnez Installé>Commun>MVC.
  • Sélectionnez Contrôleur MVC avec vues, utilisant Entity Framework.
  • Sélectionnez Ajouter.

Boîte de dialogue Ajouter une échafaudage

Terminez la boîte de dialogue Ajouter un contrôleur MVC avec des vues, à l’aide d’Entity Framework :

  • Dans la liste déroulante Classe du modèle, sélectionnez Film (MvcMovie.Models).
  • Dans la ligne Classe du contexte de données, sélectionnez le signe + (plus).
    • Dans la boîte de dialogue Ajouter un contexte de données, le nom de classe MvcMovie.Data.MvcMovieContext est généré.
    • Sélectionnez Ajouter.
  • Dans le menu déroulant fournisseur de base de données, sélectionnez SQL Server.
  • Vues et Nom du contrôleur : conservez la valeur par défaut.
  • Sélectionnez Ajouter.

Ajouter un contexte de données – conserver les valeurs par défaut Si vous obtenez un message d’erreur, sélectionnez Ajouter une deuxième fois pour réessayer.

L’échafaudage ajoute les packages suivants :

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

L'échafaudage crée les éléments suivants :

  • Un contrôleur de films : Controllers/MoviesController.cs
  • Razor afficher les fichiers pour les pages Créer, Supprimer, Détails, Modifier et Indexer : Views/Movies/*.cshtml
  • Une classe de contexte de base de données : Data/MvcMovieContext.cs

La structure de code met à jour les éléments suivants :

  • Insère les références de package requises dans le fichier projet MvcMovie.csproj.
  • Inscrit le contexte de base de données dans le fichier Program.cs.
  • Ajoute une chaîne de connexion de base de données au fichier appsettings.json.

La création automatique de ces fichiers et mises à jour est appelée génération de modèles automatique.

Les pages générées automatiquement ne peuvent pas encore être utilisées, car la base de données n’existe pas. L’exécution de l’application et la sélection du lien Application vidéo entraînent un message d’erreur Impossible d’ouvrir la base de données ou Le type de table suivant n’existe pas : Film.

Générez l’application pour vérifier qu’il n’y a pas d’erreurs.

Migration initiale

Utilisez la fonctionnalité EF CoreMigrations pour créer la base de données. La fonctionnalité Migrations est un ensemble d’outils qui vous permettent de créer et de mettre à jour une base de données pour qu’elle corresponde à votre modèle de données.

Dans le menu Tools, sélectionnez NuGet Gestionnaire de package>Gestionnaire de package Console .

Dans la console Gestionnaire de package (PMC), entrez les commandes suivantes :

Add-Migration InitialCreate
Update-Database

  • Add-Migration InitialCreate: Génère un Migrations/{timestamp}_InitialCreate.cs fichier de migration. L’argument InitialCreate est le nom de la migration. Vous pouvez utiliser n’importe quel nom, mais par convention, un nom décrivant la migration est sélectionné. Étant donné qu’il s’agit de la première migration, la classe générée contient du code permettant de créer le schéma de la base de données. Le schéma de base de données est basé sur le modèle spécifié dans la classe MvcMovieContext.

  • Update-Database : Met à jour la base de données vers la dernière migration, qui a été créée par la commande précédente. Cette commande exécute la méthode Up dans le fichier Migrations/{time-stamp}_InitialCreate.cs, qui crée la base de données.

La commande Update-Database génère l’avertissement suivant :

Aucun type n’a été spécifié pour la colonne décimale 'Price' sur le type d’entité 'Movie'. Les valeurs sont tronquées en mode silencieux si elles ne sont pas compatibles avec la précision et l’échelle par défaut. Spécifiez explicitement le type de colonne SQL Server capable d’accueillir toutes les valeurs en utilisant ’HasColumnType()’.

Ignorez l’avertissement précédent, il est résolu dans un tutoriel ultérieur.

Pour plus d’informations sur les outils PMC pour EF Core, consultez EF Core tools reference - PMC dans Visual Studio.

Tester l’application

Exécutez l’application et sélectionnez sur le lien Movie App.

Si vous obtenez une exception semblable à ce qui suit, vous avez peut-être manqué la commande Update-Database à l’étape des migrations :

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

Note

Vous ne pourrez peut-être pas entrer de virgules décimales dans le champ Price. Pour prendre en charge la validation jQuery pour les paramètres régionaux autres que « Anglais » qui utilisent une virgule (« , ») comme décimale et des formats de date autres que le format « Anglais (États-Unis »), l’application doit être localisée. Pour obtenir des instructions sur la mondialisation, consultez ce problème GitHub.

Examinez la classe de contexte de base de données générée et l’enregistrement

Avec EF Core, l’accès aux données est effectué à l’aide d’un modèle. Un modèle est constitué de classes d’entité et d’un objet de contexte qui représente une session avec la base de données. L’objet de contexte permet l’interrogation et l’enregistrement des données. Le contexte de base de données est dérivé de Microsoft. EntityFrameworkCore.DbContext et spécifie les entités à inclure dans le modèle de données.

La génération de modèles crée la classe Data/MvcMovieContext.cs de contexte de base de données :

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

Le code précédent crée une propriété DbSet<Movie> qui représente les films dans la base de données.

Injection de dépendances

ASP.NET Core est généré avec dependency injection (DI). Les services, tels que le contexte de base de données, sont enregistrés dans DI dans Program.cs. Ces services sont fournis aux composants qui en ont besoin via des paramètres de constructeur.

Dans le fichier Controllers/MoviesController.cs, le constructeur utilise une injection de dépendance pour injecter le contexte de base de données MvcMovieContext dans le contrôleur. Le contexte de base de données est utilisé dans chacune des méthodes CRUD du contrôleur.

La génération de l'échafaudage a produit le code suivant en surbrillance dans Program.cs :

var builder = WebApplication.CreateBuilder(args);

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

Le système de configuration ASP.NET Core lit la chaîne de connexion à la base de données « MvcMovieContext ».

Examiner la chaîne de connexion de la base de données générée

L'échafaudage a ajouté une chaîne de connexion au fichier appsettings.json :

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

Pour le développement local, le système de configuration ASP.NET Core lit la clé ConnectionString à partir du fichier appsettings.json.

La classe InitialCreate

Examinez le fichier de migration 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");
        }
    }
}

Dans le code précédent :

  • InitialCreate.Up crée la table Film et configure Id comme clé primaire.
  • InitialCreate.Down rétablit les modifications de schéma provoquées par la migration Up.

Injection de dépendances dans le contrôleur

Ouvrez le fichier Controllers/MoviesController.cs et examinez le constructeur :

public class MoviesController : Controller
{
    private readonly MvcMovieContext _context;

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

Le constructeur utilise une injection de dépendance pour injecter le contexte de base de données (MvcMovieContext) dans le contrôleur. Le contexte de base de données est utilisé dans chacune des méthodes CRUD du contrôleur.

Testez la page Create. Entrez et envoyez des données.

Testez les pages Edit, Details et Delete.

Modèles fortement typés et directive @model

Plus tôt dans ce didacticiel, vous avez vu comment un contrôleur peut passer des données ou des objets à une vue en utilisant le dictionnaire ViewData. Le dictionnaire ViewData est un objet dynamique qui permet d'utiliser le binding tardif de manière pratique pour transmettre des informations à une vue.

Le modèle MVC fournit la possibilité de passer des objets de modèle fortement typés à une vue. Cette approche fortement typée permet de vérifier votre code au moment de la compilation. Le mécanisme d'échafaudage a passé un modèle fortement typé dans la classe MoviesController et les vues.

Examinez la méthode Details générée dans le fichier 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);
}

Le paramètre id est généralement passé en tant que données de routage. Par exemple, https://localhost:5001/movies/details/1 définit :

  • Le contrôleur associé au contrôleur movies, le premier segment de l’URL.
  • L’action sur details, le deuxième segment de l’URL.
  • Réglez id sur 1, pour le dernier segment de l'URL.

Le id peut être transmis avec une chaîne de requête, comme dans l’exemple suivant :

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

Le paramètre id est défini comme type Nullable (int?) au cas où la valeur id n’est pas fournie.

Une expression lambda est passée à la méthode FirstOrDefaultAsync pour sélectionner les entités de film qui correspondent aux données de routage ou à la valeur de la chaîne de requête.

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

Si un film est trouvé, une instance du modèle Movie est passée à la vue Details :

return View(movie);

Examinez le contenu du fichier 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>

L’instruction @model située en haut du fichier de la vue spécifie le type d’objet attendu par la vue. Lorsque le contrôleur de film était créé, l’instruction @model suivante était incluse :

@model MvcMovie.Models.Movie

Cette directive @model autorise l’accès au film que le contrôleur a passé à la vue. L’objet Model est fortement typé. Par exemple, dans la vue Details.cshtml, le code passe chaque champ du film aux Helpers HTML DisplayNameFor et DisplayFor avec l’objet Model fortement typé. Les méthodes et les vues Create et Edit passent aussi un objet du modèle Movie.

Examinez la vue Index.cshtml et la méthode Index dans le contrôleur Movies. Notez comment le code crée un objet List quand il appelle la méthode View. Le code passe cette liste Movies de la méthode d’action Index à la vue :

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

Le code retourne les détails du problème si la propriété Movie du contexte de données est null.

Lors de la création du contrôleur des films, le scaffolding a inclus l'instruction @model suivante en haut du fichier Index.cshtml :

@model IEnumerable<MvcMovie.Models.Movie>

La directive @model permet d’accéder à la liste des films que le contrôleur a passé à la vue en utilisant un objet Model qui est fortement typé. Par exemple, dans la vue Index.cshtml, le code parcourt les films avec une instruction foreach sur l’objet Model fortement typé.

@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>

Comme l’objet Model est fortement typé en tant qu’objet IEnumerable<Movie>, chaque élément de la boucle est typé en tant que Movie. Entre autres avantages, le compilateur valide les types utilisés dans le code.

Ressources supplémentaires

Dans ce tutoriel, des classes sont ajoutées pour la gestion des films dans une base de données. Ces classes constituent la partie « Modèle » de l’application MVC.

Ces classes de modèle sont utilisées avec Entity Framework Core (EF Core) pour travailler avec une base de données. EF Core est une infrastructure de mappage relationnel d’objets qui simplifie le code d’accès aux données à écrire.

Les classes du modèle créées sont appelées classes POCO, à partir de Plain Old CLR Objects. Les classes POCO n’ont aucune dépendance vis-à-vis de EF Core. Elles définissent uniquement les propriétés des données qui seront stockées dans la base de données.

Dans ce tutoriel, les classes du modèle sont d’abord créées, puis EF Core crée la base de données.

Ajouter une classe de modèle de données

Cliquez avec le bouton droit sur le dossier Modèles>Ajouter>Classe. Nommez le fichier Movie.cs.

Mettez à jour le fichier Models/Movie.cs avec le code suivant :

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

La classe Movie contient un champ Id, qui est nécessaire à la base de données pour la clé primaire.

L’attribut DataType de ReleaseDate spécifie le type de données (Date). Avec cet attribut :

  • L’utilisateur n’est pas obligé d’entrer les informations de temps dans le champ de date.
  • Seule la date est affichée, pas les informations de temps.

Les DataAnnotations sont traitées dans un prochain didacticiel.

La présence du point d'interrogation après string indique que la propriété est nullable. Pour plus d’informations, consultez types de référence nullables.

Ajouter des packages NuGet

Dans le menu Tools, sélectionnez NuGet Gestionnaire de package>Gestionnaire de package Console (PMC).

Menu PMC

Dans le PMC, exécutez la commande suivante :

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

Les commandes précédentes ajoutent :

  • Le fournisseur EF Core SQL Server. Le package du fournisseur installe le package EF Core en tant que dépendance.
  • Utilitaires utilisés par les packages installés automatiquement durant l’étape de scaffolding, abordés plus loin dans le tutoriel.

Générez le projet en tant que vérification des erreurs du compilateur.

Générer automatiquement des pages de film

Utilisez l’outil de scaffolding pour produire Create, Read, Update et Delete (CRUD) des pages pour le modèle de film.

Dans Explorateur de solutions, cliquez avec le bouton droit sur le dossier Controllers et sélectionnez Add > Nouvel élément généré automatiquement.

affichage de l’étape ci-dessus

Dans la boîte de dialogue Ajouter un modèle automatique, sélectionnez Contrôleur MVC avec vues, utilisant Entity Framework > Ajouter.

Boîte de dialogue Ajouter une échafaudage

Terminez la boîte de dialogue Ajouter un contrôleur MVC avec des vues, à l’aide d’Entity Framework :

  • Dans la liste déroulante Classe du modèle, sélectionnez Film (MvcMovie.Models).
  • Dans la ligne Classe du contexte de données, sélectionnez le signe + (plus).
    • Dans la boîte de dialogue Ajouter un contexte de données, le nom de classe MvcMovie.Data.MvcMovieContext est généré.
    • Sélectionnez Ajouter.
  • Vues et Nom du contrôleur : conservez la valeur par défaut.
  • Sélectionnez Ajouter.

Ajouter un contexte de données – conserver les valeurs par défaut

Si vous obtenez un message d’erreur, sélectionnez Ajouter une deuxième fois pour réessayer.

La structure de code met à jour les éléments suivants :

  • Insère les références de package requises dans le fichier projet MvcMovie.csproj.
  • Inscrit le contexte de base de données dans le fichier Program.cs.
  • Ajoute une chaîne de connexion de base de données au fichier appsettings.json.

L'échafaudage crée les éléments suivants :

  • Un contrôleur de films : Controllers/MoviesController.cs
  • Razor afficher les fichiers pour les pages Créer, Supprimer, Détails, Modifier et Indexer : Views/Movies/*.cshtml
  • Une classe de contexte de base de données : Data/MvcMovieContext.cs

La création automatique de ces fichiers et mises à jour est appelée génération de modèles automatique.

Les pages générées automatiquement ne peuvent pas encore être utilisées, car la base de données n’existe pas. L’exécution de l’application et la sélection du lien Application vidéo entraînent un message d’erreur Impossible d’ouvrir la base de données ou Le type de table suivant n’existe pas : Film.

Création de l’application

Générez l'application. Le compilateur génère plusieurs avertissements sur la façon dont les valeurs null sont gérées. Pour plus d’informations, consultez ce problème GitHub et Nullable reference types.

Pour éliminer les avertissements des types de référence nullables, supprimez la ligne suivante du fichier MvcMovie.csproj :

<Nullable>enable</Nullable>

Nous espérons résoudre ce problème dans la prochaine version.

Migration initiale

Utilisez la fonctionnalité EF CoreMigrations pour créer la base de données. La fonctionnalité Migrations est un ensemble d’outils qui vous permettent de créer et de mettre à jour une base de données pour qu’elle corresponde à votre modèle de données.

Dans le menu Tools, sélectionnez NuGet Gestionnaire de package>Gestionnaire de package Console .

Dans la console Gestionnaire de package (PMC), entrez les commandes suivantes :

Add-Migration InitialCreate
Update-Database

  • Add-Migration InitialCreate: Génère un Migrations/{timestamp}_InitialCreate.cs fichier de migration. L’argument InitialCreate est le nom de la migration. Vous pouvez utiliser n’importe quel nom, mais par convention, un nom décrivant la migration est sélectionné. Étant donné qu’il s’agit de la première migration, la classe générée contient du code permettant de créer le schéma de la base de données. Le schéma de base de données est basé sur le modèle spécifié dans la classe MvcMovieContext.

  • Update-Database : Met à jour la base de données vers la dernière migration, qui a été créée par la commande précédente. Cette commande exécute la méthode Up dans le fichier Migrations/{time-stamp}_InitialCreate.cs, qui crée la base de données.

La commande Update-Database génère l’avertissement suivant :

Aucun type n’a été spécifié pour la colonne décimale 'Price' sur le type d’entité 'Movie'. Les valeurs sont tronquées en mode silencieux si elles ne sont pas compatibles avec la précision et l’échelle par défaut. Spécifiez explicitement le type de colonne SQL Server capable d’accueillir toutes les valeurs en utilisant ’HasColumnType()’.

Ignorez l’avertissement précédent, il est résolu dans un tutoriel ultérieur.

Pour plus d’informations sur les outils PMC pour EF Core, consultez EF Core tools reference - PMC dans Visual Studio.

Tester l’application

Exécutez l’application et sélectionnez sur le lien Movie App.

Si vous obtenez une exception similaire à ce qui suit, vous avez peut-être manqué l’étape de migration :

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

Note

Vous ne pourrez peut-être pas entrer de virgules décimales dans le champ Price. Pour prendre en charge la validation jQuery pour les paramètres régionaux autres que « Anglais » qui utilisent une virgule (« , ») comme décimale et des formats de date autres que le format « Anglais (États-Unis »), l’application doit être localisée. Pour obtenir des instructions sur la mondialisation, consultez ce problème GitHub.

Examinez la classe de contexte de base de données générée et l’enregistrement

Avec EF Core, l’accès aux données est effectué à l’aide d’un modèle. Un modèle est constitué de classes d’entité et d’un objet de contexte qui représente une session avec la base de données. L’objet de contexte permet l’interrogation et l’enregistrement des données. Le contexte de base de données est dérivé de Microsoft. EntityFrameworkCore.DbContext et spécifie les entités à inclure dans le modèle de données.

La génération de modèles crée la classe Data/MvcMovieContext.cs de contexte de base de données :

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

Le code précédent crée une propriété DbSet<Movie> qui représente les films dans la base de données.

Injection de dépendances

ASP.NET Core est généré avec dependency injection (DI). Les services, tels que le contexte de base de données, sont enregistrés dans DI dans Program.cs. Ces services sont fournis aux composants qui en ont besoin via des paramètres de constructeur.

Dans le fichier Controllers/MoviesController.cs, le constructeur utilise une injection de dépendance pour injecter le contexte de base de données MvcMovieContext dans le contrôleur. Le contexte de base de données est utilisé dans chacune des méthodes CRUD du contrôleur.

La génération de l'échafaudage a produit le code suivant en surbrillance dans Program.cs :

var builder = WebApplication.CreateBuilder(args);

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

Le système de configuration ASP.NET Core lit la chaîne de connexion à la base de données « MvcMovieContext ».

Examiner la chaîne de connexion de la base de données générée

L'échafaudage a ajouté une chaîne de connexion au fichier appsettings.json :

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

Pour le développement local, le système de configuration ASP.NET Core lit la clé ConnectionString à partir du fichier appsettings.json.

La classe InitialCreate

Examinez le fichier de migration 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");
        }
    }
}

Dans le code précédent :

  • InitialCreate.Up crée la table Film et configure Id comme clé primaire.
  • InitialCreate.Down rétablit les modifications de schéma provoquées par la migration Up.

Injection de dépendances dans le contrôleur

Ouvrez le fichier Controllers/MoviesController.cs et examinez le constructeur :

public class MoviesController : Controller
{
    private readonly MvcMovieContext _context;

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

Le constructeur utilise une injection de dépendance pour injecter le contexte de base de données (MvcMovieContext) dans le contrôleur. Le contexte de base de données est utilisé dans chacune des méthodes CRUD du contrôleur.

Testez la page Create. Entrez et envoyez des données.

Testez les pages Edit, Details et Delete.

Modèles fortement typés et directive @model

Plus tôt dans ce didacticiel, vous avez vu comment un contrôleur peut passer des données ou des objets à une vue en utilisant le dictionnaire ViewData. Le dictionnaire ViewData est un objet dynamique qui permet d'utiliser le binding tardif de manière pratique pour transmettre des informations à une vue.

Le modèle MVC fournit la possibilité de passer des objets de modèle fortement typés à une vue. Cette approche fortement typée permet de vérifier votre code au moment de la compilation. Le mécanisme d'échafaudage a passé un modèle fortement typé dans la classe MoviesController et les vues.

Examinez la méthode Details générée dans le fichier 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);
}

Le paramètre id est généralement passé en tant que données de routage. Par exemple, https://localhost:5001/movies/details/1 définit :

  • Le contrôleur associé au contrôleur movies, le premier segment de l’URL.
  • L’action sur details, le deuxième segment de l’URL.
  • Réglez id sur 1, pour le dernier segment de l'URL.

Le id peut être transmis avec une chaîne de requête, comme dans l’exemple suivant :

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

Le paramètre id est défini comme type Nullable (int?) au cas où la valeur id n’est pas fournie.

Une expression lambda est passée à la méthode FirstOrDefaultAsync pour sélectionner les entités de film qui correspondent aux données de routage ou à la valeur de la chaîne de requête.

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

Si un film est trouvé, une instance du modèle Movie est passée à la vue Details :

return View(movie);

Examinez le contenu du fichier 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>

L’instruction @model située en haut du fichier de la vue spécifie le type d’objet attendu par la vue. Lorsque le contrôleur de film était créé, l’instruction @model suivante était incluse :

@model MvcMovie.Models.Movie

Cette directive @model autorise l’accès au film que le contrôleur a passé à la vue. L’objet Model est fortement typé. Par exemple, dans la vue Details.cshtml, le code passe chaque champ du film aux Helpers HTML DisplayNameFor et DisplayFor avec l’objet Model fortement typé. Les méthodes et les vues Create et Edit passent aussi un objet du modèle Movie.

Examinez la vue Index.cshtml et la méthode Index dans le contrôleur Movies. Notez comment le code crée un objet List quand il appelle la méthode View. Le code passe cette liste Movies de la méthode d’action Index à la vue :

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

Lors de la création du contrôleur des films, le scaffolding a inclus l'instruction @model suivante en haut du fichier Index.cshtml :

@model IEnumerable<MvcMovie.Models.Movie>

La directive @model permet d’accéder à la liste des films que le contrôleur a passé à la vue en utilisant un objet Model qui est fortement typé. Par exemple, dans la vue Index.cshtml, le code parcourt les films avec une instruction foreach sur l’objet Model fortement typé.

@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>

Comme l’objet Model est fortement typé en tant qu’objet IEnumerable<Movie>, chaque élément de la boucle est typé en tant que Movie. Entre autres avantages, le compilateur valide les types utilisés dans le code.

Ressources supplémentaires

Dans ce tutoriel, des classes sont ajoutées pour la gestion des films dans une base de données. Ces classes constituent la partie « Modèle » de l’application MVC.

Ces classes de modèle sont utilisées avec Entity Framework Core (EF Core) pour travailler avec une base de données. EF Core est une infrastructure de mappage relationnel d’objets qui simplifie le code d’accès aux données à écrire.

Les classes du modèle créées sont appelées classes POCO, à partir de Plain Old CLR Objects. Les classes POCO n’ont aucune dépendance vis-à-vis de EF Core. Elles définissent uniquement les propriétés des données qui seront stockées dans la base de données.

Dans ce tutoriel, les classes du modèle sont d’abord créées, puis EF Core crée la base de données.

Ajouter une classe de modèle de données

Cliquez avec le bouton droit sur le dossier Modèles>Ajouter>Classe. Nommez le fichier Movie.cs.

Mettez à jour le fichier Models/Movie.cs avec le code suivant :

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

La classe Movie contient un champ Id, qui est nécessaire à la base de données pour la clé primaire.

L’attribut DataType de ReleaseDate spécifie le type de données (Date). Avec cet attribut :

  • L’utilisateur n’est pas obligé d’entrer les informations de temps dans le champ de date.
  • Seule la date est affichée, pas les informations de temps.

Les DataAnnotations sont traitées dans un prochain didacticiel.

Ajouter des packages NuGet

Dans le menu Tools, sélectionnez NuGet Gestionnaire de package>Gestionnaire de package Console (PMC).

Menu PMC

Dans le PMC, exécutez la commande suivante :

Install-Package Microsoft.EntityFrameworkCore.Design

Les commandes précédentes ajoutent :

  • Le fournisseur EF Core SQL Server. Le package du fournisseur installe le package EF Core en tant que dépendance.
  • Utilitaires utilisés par les packages installés automatiquement durant l’étape de scaffolding, abordés plus loin dans le tutoriel.

Générez le projet en tant que vérification des erreurs du compilateur.

Générer automatiquement des pages de film

Utilisez l’outil de scaffolding pour produire Create, Read, Update et Delete (CRUD) des pages pour le modèle de film.

Dans Explorateur de solutions, cliquez avec le bouton droit sur le dossier Controllers et sélectionnez Add > Nouvel élément généré automatiquement.

affichage de l’étape ci-dessus

Dans la boîte de dialogue Ajouter une structure, sélectionnez Contrôleur MVC avec vues, en utilisant Entity Framework > Ajouter.

Boîte de dialogue Ajouter une échafaudage

Terminez la boîte de dialogue Ajouter un contrôleur MVC avec des vues, à l’aide d’Entity Framework :

  • Dans la liste déroulante Classe du modèle, sélectionnez Film (MvcMovie.Models).
  • Dans la ligne Classe du contexte de données, sélectionnez le signe + (plus).
    • Dans la boîte de dialogue Ajouter un contexte de données, le nom de classe MvcMovie.Data.MvcMovieContext est généré.
    • Sélectionnez Ajouter.
  • Vues et Nom du contrôleur : conservez la valeur par défaut.
  • Sélectionnez Ajouter.

Ajouter un contexte de données – conserver les valeurs par défaut

La structure de code met à jour les éléments suivants :

  • Insère les références de package requises dans le fichier projet MvcMovie.csproj.
  • Inscrit le contexte de base de données dans Startup.ConfigureServices du fichier Startup.cs.
  • Ajoute une chaîne de connexion de base de données au fichier appsettings.json.

L'échafaudage crée les éléments suivants :

  • Un contrôleur de films : Controllers/MoviesController.cs
  • Razor afficher les fichiers pour les pages Créer, Supprimer, Détails, Modifier et Indexer : Views/Movies/*.cshtml
  • Une classe de contexte de base de données : Data/MvcMovieContext.cs

La création automatique de ces fichiers et des mises à jour de fichiers est appelée échafaudage automatique.

Les pages générées automatiquement ne peuvent pas encore être utilisées, car la base de données n’existe pas. L’exécution de l’application et la sélection du lien Application vidéo entraînent un message d’erreur Impossible d’ouvrir la base de données ou Le type de table suivant n’existe pas : Film.

Migration initiale

Utilisez la fonctionnalité EF CoreMigrations pour créer la base de données. Les migrations sont un ensemble d’outils qui créent et mettent à jour une base de données pour correspondre au modèle de données.

Dans le menu Tools, sélectionnez NuGet Gestionnaire de package>Gestionnaire de package Console .

Dans la console Gestionnaire de package (PMC), entrez les commandes suivantes :

Add-Migration InitialCreate
Update-Database

  • Add-Migration InitialCreate: Génère un Migrations/{timestamp}_InitialCreate.cs fichier de migration. L’argument InitialCreate est le nom de la migration. Vous pouvez utiliser n’importe quel nom, mais par convention, un nom décrivant la migration est sélectionné. Étant donné qu’il s’agit de la première migration, la classe générée contient du code permettant de créer le schéma de la base de données. Le schéma de base de données est basé sur le modèle spécifié dans la classe MvcMovieContext.

  • Update-Database : Met à jour la base de données vers la dernière migration, qui a été créée par la commande précédente. Cette commande exécute la méthode Up dans le fichier Migrations/{time-stamp}_InitialCreate.cs, qui crée la base de données.

La commande Update-Database génère l’avertissement suivant :

Aucun type n’a été spécifié pour la colonne décimale 'Price' sur le type d’entité 'Movie'. Les valeurs sont tronquées en mode silencieux si elles ne sont pas compatibles avec la précision et l’échelle par défaut. Spécifiez explicitement le type de colonne SQL Server capable d’accueillir toutes les valeurs en utilisant ’HasColumnType()’.

Ignorez l’avertissement précédent, il est résolu dans un tutoriel ultérieur.

Pour plus d’informations sur les outils PMC pour EF Core, consultez EF Core tools reference - PMC dans Visual Studio.

Tester l’application

Exécutez l’application et sélectionnez sur le lien Movie App.

Si vous obtenez une exception similaire à ce qui suit, vous avez peut-être manqué l’étape de migration :

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

Note

Vous ne pourrez peut-être pas entrer de virgules décimales dans le champ Price. Pour prendre en charge la validation jQuery pour les paramètres régionaux autres que « Anglais » qui utilisent une virgule (« , ») comme décimale et des formats de date autres que le format « Anglais (États-Unis »), l’application doit être localisée. Pour obtenir des instructions sur la mondialisation, consultez ce problème GitHub.

Examinez la classe de contexte de base de données générée et l’enregistrement

Avec EF Core, l’accès aux données est effectué à l’aide d’un modèle. Un modèle est constitué de classes d’entité et d’un objet de contexte qui représente une session avec la base de données. L’objet de contexte permet l’interrogation et l’enregistrement des données. Le contexte de base de données est dérivé de Microsoft. EntityFrameworkCore.DbContext et spécifie les entités à inclure dans le modèle de données.

La génération de modèles crée la classe Data/MvcMovieContext.cs de contexte de base de données :

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

Le code précédent crée une propriété DbSet<Movie> qui représente les films dans la base de données.

ASP.NET Core est généré avec dependency injection (DI). Les services, tels que le contexte de base de données, doivent être enregistrés dans le conteneur d'injection de dépendances dans Startup. Les composants qui nécessitent ces services sont fournis via des paramètres de constructeur.

Dans le fichier Controllers/MoviesController.cs, le constructeur utilise une injection de dépendance pour injecter le contexte de base de données MvcMovieContext dans le contrôleur. Le contexte de base de données est utilisé dans chacune des méthodes CRUD du contrôleur.

La génération de l'échafaudage a produit le code suivant en surbrillance dans Startup.ConfigureServices :

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

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

Le système de configuration ASP.NET Core lit la chaîne de connexion à la base de données « MvcMovieContext ».

Examiner la chaîne de connexion de la base de données générée

L'échafaudage a ajouté une chaîne de connexion au fichier appsettings.json :

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

Pour le développement local, le système de configuration ASP.NET Core lit la clé ConnectionString à partir du fichier appsettings.json.

La classe InitialCreate

Examinez le fichier de migration 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");
    }
}

Dans le code précédent :

  • InitialCreate.Up crée la table Film et configure Id comme clé primaire.
  • InitialCreate.Down rétablit les modifications de schéma provoquées par la migration Up.

Injection de dépendances dans le contrôleur

Ouvrez le fichier Controllers/MoviesController.cs et examinez le constructeur :

public class MoviesController : Controller
{
    private readonly MvcMovieContext _context;

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

Le constructeur utilise une injection de dépendance pour injecter le contexte de base de données (MvcMovieContext) dans le contrôleur. Le contexte de base de données est utilisé dans chacune des méthodes CRUD du contrôleur.

Testez la page Create. Entrez et envoyez des données.

Testez les pages Edit, Details et Delete.

Modèles fortement typés et directive @model

Plus tôt dans ce didacticiel, vous avez vu comment un contrôleur peut passer des données ou des objets à une vue en utilisant le dictionnaire ViewData. Le dictionnaire ViewData est un objet dynamique qui permet d'utiliser le binding tardif de manière pratique pour transmettre des informations à une vue.

Le modèle MVC fournit la possibilité de passer des objets de modèle fortement typés à une vue. Cette approche fortement typée permet de vérifier votre code au moment de la compilation. Le mécanisme d'échafaudage a passé un modèle fortement typé dans la classe MoviesController et les vues.

Examinez la méthode Details générée dans le fichier 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);
}

Le paramètre id est généralement passé en tant que données de routage. Par exemple, https://localhost:5001/movies/details/1 définit :

  • Le contrôleur associé au contrôleur movies, le premier segment de l’URL.
  • L’action sur details, le deuxième segment de l’URL.
  • Réglez id sur 1, pour le dernier segment de l'URL.

Le id peut être transmis avec une chaîne de requête, comme dans l’exemple suivant :

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

Le paramètre id est défini comme type Nullable (int?) au cas où la valeur id n’est pas fournie.

Une expression lambda est passée à la méthode FirstOrDefaultAsync pour sélectionner les entités de film qui correspondent aux données de routage ou à la valeur de la chaîne de requête.

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

Si un film est trouvé, une instance du modèle Movie est passée à la vue Details :

return View(movie);

Examinez le contenu du fichier 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>

L’instruction @model située en haut du fichier de la vue spécifie le type d’objet attendu par la vue. Lorsque le contrôleur de film était créé, l’instruction @model suivante était incluse :

@model MvcMovie.Models.Movie

Cette directive @model autorise l’accès au film que le contrôleur a passé à la vue. L’objet Model est fortement typé. Par exemple, dans la vue Details.cshtml, le code passe chaque champ du film aux Helpers HTML DisplayNameFor et DisplayFor avec l’objet Model fortement typé. Les méthodes et les vues Create et Edit passent aussi un objet du modèle Movie.

Examinez la vue Index.cshtml et la méthode Index dans le contrôleur Movies. Notez comment le code crée un objet List quand il appelle la méthode View. Le code passe cette liste Movies de la méthode d’action Index à la vue :

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

Lors de la création du contrôleur des films, le scaffolding a inclus l'instruction @model suivante en haut du fichier Index.cshtml :

@model IEnumerable<MvcMovie.Models.Movie>

La directive @model permet d’accéder à la liste des films que le contrôleur a passé à la vue en utilisant un objet Model qui est fortement typé. Par exemple, dans la vue Index.cshtml, le code parcourt les films avec une instruction foreach sur l’objet Model fortement typé.

@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>

Comme l’objet Model est fortement typé en tant qu’objet IEnumerable<Movie>, chaque élément de la boucle est typé en tant que Movie. Entre autres avantages, le compilateur valide les types utilisés dans le code.

Journalisation SQL de Entity Framework Core

La configuration de la journalisation est généralement fournie par la section Logging des fichiers appsettings.{Environment}.json. Pour journaliser les instructions SQL, ajoutez "Microsoft.EntityFrameworkCore.Database.Command": "Information" au fichier appsettings.Development.json :

{
  "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": "*"
}

Avec le code JSON précédent, les instructions SQL sont affichées sur la ligne de commande et dans la fenêtre de sortie Visual Studio.

Pour plus d’informations, consultez les ressources suivantes :

Ressources supplémentaires

Dans ce tutoriel, des classes sont ajoutées pour la gestion des films dans une base de données. Ces classes constituent la partie « Modèle » de l’application MVC.

Ces classes de modèle sont utilisées avec Entity Framework Core (EF Core) pour travailler avec une base de données. EF Core est une infrastructure de mappage relationnel d’objets qui simplifie le code d’accès aux données à écrire.

Les classes du modèle créées sont appelées classes POCO, à partir de Plain Old CLR Objects. Les classes POCO n’ont aucune dépendance vis-à-vis de EF Core. Elles définissent uniquement les propriétés des données qui seront stockées dans la base de données.

Dans ce tutoriel, les classes du modèle sont d’abord créées, puis EF Core crée la base de données.

Ajouter une classe de modèle de données

Cliquez avec le bouton droit sur le dossier Modèles>Ajouter>Classe. Nommez le fichier Movie.cs.

Mettez à jour le fichier Movie.cs avec le code suivant :

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

La classe Movie contient un champ Id, qui est nécessaire à la base de données pour la clé primaire.

L’attribut DataType de ReleaseDate spécifie le type de données (Date). Avec cet attribut :

  • L’utilisateur n’est pas obligé d’entrer les informations de temps dans le champ de date.
  • Seule la date est affichée, pas les informations de temps.

Les DataAnnotations sont traitées dans un prochain didacticiel.

Ajouter des packages NuGet

Dans le menu Tools, sélectionnez NuGet Gestionnaire de package>Gestionnaire de package Console (PMC).

Menu PMC

Dans le PMC, exécutez la commande suivante :

Install-Package Microsoft.EntityFrameworkCore.SqlServer

La commande précédente ajoute le fournisseur EF Core SQL Server. Le package du fournisseur installe le package EF Core en tant que dépendance. Plus loin dans ce tutoriel, d'autres packages sont installés automatiquement lors de l'étape de scaffolding.

Créer une classe de contexte de base de données

Une classe de contexte de base de données est nécessaire pour coordonner une fonctionnalité EF Core (Créer, Lire, Mettre à jour, Supprimer) pour le modèle Movie. Le contexte de base de données est dérivé de Microsoft.EntityFrameworkCore.DbContext et spécifie les entités à inclure dans le modèle de données.

Créez un dossier nommé Data.

Ajoutez un fichier Data/MvcMovieContext.cs avec le code suivant :

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

Le code précédent crée une propriété DbSet<Movie> pour le jeu d’entités. Dans la terminologie Entity Framework, un jeu d’entités correspond généralement à une table de base de données. Une entité correspond à une ligne dans la table.

Inscrire le contexte de base de données

ASP.NET Core est généré avec dependency injection (DI). Les services (tels que le contexte de base de données EF Core) doivent être enregistrés avec l’injection de dépendances au démarrage de l’application. Les composants qui nécessitent ces services (comme les Razor Pages) les reçoivent par le biais de paramètres de constructeur. Le code du constructeur qui obtient une instance de contexte de base de données est indiqué plus loin dans le tutoriel. Dans cette section, vous allez inscrire le contexte de base de données auprès du conteneur d’injection de dépendances.

En tête du using, ajoutez les instructions Startup.cs suivantes :

using MvcMovie.Data;
using Microsoft.EntityFrameworkCore;

Ajoutez le code en surbrillance suivant dans Startup.ConfigureServices :

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

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

Le nom du chaîne de connexion est transmis au contexte en appelant une méthode sur un objet DbContextOptions. Pour le développement local, le système de configuration ASP.NET Core lit le chaîne de connexion à partir du fichier appsettings.json.

Examiner le chaîne de connexion de base de données

Ajoutez un chaîne de connexion au fichier appsettings.json :

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

Générez le projet en tant que vérification des erreurs du compilateur.

Générer automatiquement des pages de film

Utilisez l’outil de génération de modèles automatique pour créer, lire, mettre à jour et supprimer des pages du modèle de film.

Dans Explorateur de solutions, cliquez avec le bouton droit sur le dossier Controllers> Ajouter > Nouvel élément scaffold.

affichage de l’étape ci-dessus

Dans la boîte de dialogue Ajouter un modèle automatique, sélectionnez Contrôleur MVC avec vues, utilisant Entity Framework > Ajouter.

Boîte de dialogue Ajouter une échafaudage

Renseignez la boîte de dialogue Ajouter un contrôleur :

  • Classe de modèle :Movie (MvcMovie.Models)
  • Classe de contexte de données :MvcMovieContext (MvcMovie.Data)

Ajouter un contexte de données

  • Affichages : conservez la valeur par défaut de chaque option activée.
  • Nom du contrôleur : conservez la valeur par défaut MoviesController.
  • Sélectionnez Ajouter

Visual Studio crée :

  • Un contrôleur de films (Controllers/MoviesController.cs)
  • Fichiers de vue Razor pour les pages Créer, Supprimer, Détails, Modifier et Index (Views/Movies/.cshtml)

La création automatique de ces fichiers est appelée échafaudage.

Vous ne pouvez pas encore utiliser les pages échafaudées, car la base de données n’existe pas. Si vous exécutez l’application et cliquez sur le lien Movie App, vous obtenez un message d’erreur Impossible d’ouvrir la base de données ou aucune table de ce nom : Movie.

Migration initiale

Utilisez la fonctionnalité EF CoreMigrations pour créer la base de données. La fonctionnalité Migrations est un ensemble d’outils qui vous permettent de créer et de mettre à jour une base de données pour qu’elle corresponde à votre modèle de données.

Dans le menu Tools, sélectionnez NuGet Gestionnaire de package>Gestionnaire de package Console (PMC).

Dans le PMC, entrez les commandes suivantes :

Add-Migration InitialCreate
Update-Database
  • Add-Migration InitialCreate: Génère un Migrations/{timestamp}_InitialCreate.cs fichier de migration. L’argument InitialCreate est le nom de la migration. Vous pouvez utiliser n’importe quel nom, mais par convention, un nom décrivant la migration est sélectionné. Étant donné qu’il s’agit de la première migration, la classe générée contient du code permettant de créer le schéma de la base de données. Le schéma de base de données est basé sur le modèle spécifié dans la classe MvcMovieContext.

  • Update-Database : Met à jour la base de données vers la dernière migration, qui a été créée par la commande précédente. Cette commande exécute la méthode Up dans le fichier Migrations/{time-stamp}_InitialCreate.cs, qui crée la base de données.

    La commande « database update » génère l’avertissement suivant :

    Aucun type n’a été spécifié pour la colonne décimale 'Price' sur le type d’entité 'Movie'. Les valeurs sont tronquées en mode silencieux si elles ne sont pas compatibles avec la précision et l’échelle par défaut. Spécifiez explicitement le type de colonne SQL Server capable d’accueillir toutes les valeurs en utilisant ’HasColumnType()’.

    Vous pouvez ignorer cet avertissement, il sera corrigé dans un prochain tutoriel.

Pour plus d’informations sur les outils PMC pour EF Core, consultez EF Core tools reference - PMC dans Visual Studio.

La classe InitialCreate

Examinez le fichier de migration 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");
    }
}

La méthode Up crée la table Movie et configure Id comme la clé primaire. La méthode Down rétablit les modifications de schéma provoquées par la migration Up.

Tester l’application

  • Exécutez l’application et cliquez sur le lien Movie App.

    Si vous obtenez une exception similaire à celle-ci :

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

Il est probable que vous n’ayez pas effectué l’étape de migration.

  • Testez la page Create. Entrez et envoyez des données.

    Note

    Vous ne pourrez peut-être pas entrer de virgules décimales dans le champ Price. Pour prendre en charge la validation jQuery pour les paramètres régionaux autres que « Anglais » qui utilisent une virgule (« , ») comme décimale et des formats de date autres que le format « Anglais (États-Unis »), l’application doit être localisée. Pour obtenir des instructions sur la mondialisation, consultez ce problème GitHub.

  • Testez les pages Edit, Details et Delete.

Injection de dépendances dans le contrôleur

Ouvrez le fichier Controllers/MoviesController.cs et examinez le constructeur :

public class MoviesController : Controller
{
    private readonly MvcMovieContext _context;

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

Le constructeur utilise une injection de dépendance pour injecter le contexte de base de données (MvcMovieContext) dans le contrôleur. Le contexte de base de données est utilisé dans chacune des méthodes CRUD du contrôleur.

Modèles fortement typés et mot clé @model

Plus tôt dans ce didacticiel, vous avez vu comment un contrôleur peut passer des données ou des objets à une vue en utilisant le dictionnaire ViewData. Le dictionnaire ViewData est un objet dynamique qui permet d'utiliser le binding tardif de manière pratique pour transmettre des informations à une vue.

Le modèle MVC fournit également la possibilité de passer des objets de modèle fortement typés à une vue. Cette approche fortement typée permet de vérifier votre code au moment de la compilation. Le mécanisme d'échafaudage a utilisé cette approche (c'est-à-dire passer un modèle fortement typé) avec la classe MoviesController et les vues.

Examinez la méthode Details générée dans le fichier 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);
}

Le paramètre id est généralement passé en tant que données de routage. Par exemple, https://localhost:5001/movies/details/1 définit :

  • Le contrôleur pour le contrôleur movies (le premier segment de l'URL).
  • L’action sur details (le deuxième segment de l’URL).
  • L’ID sur 1 (le dernier segment de l’URL).

Vous pouvez aussi passer id avec une requête de chaîne, comme suit :

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

Le paramètre id est défini comme type nullable (int?) au cas où la valeur d’ID n’est pas fournie.

Une expression lambda est passée à FirstOrDefaultAsync pour sélectionner les entités de film qui correspondent aux données de routage ou à la valeur de la chaîne de requête.

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

Si un film est trouvé, une instance du modèle Movie est passée à la vue Details :

return View(movie);

Examinez le contenu du fichier 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>

L’instruction @model située en haut du fichier de la vue spécifie le type d’objet attendu par la vue. Lorsque le contrôleur de film était créé, l’instruction @model suivante était incluse :

@model MvcMovie.Models.Movie

Cette directive @model autorise l’accès au film que le contrôleur a passé à la vue. L’objet Model est fortement typé. Par exemple, dans la vue Details.cshtml, le code passe chaque champ du film aux Helpers HTML DisplayNameFor et DisplayFor avec l’objet Model fortement typé. Les méthodes et les vues Create et Edit passent aussi un objet du modèle Movie.

Examinez la vue Index.cshtml et la méthode Index dans le contrôleur Movies. Notez comment le code crée un objet List quand il appelle la méthode View. Le code passe cette liste Movies de la méthode d’action Index à la vue :

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

Lors de la création du contrôleur des films, le scaffolding a inclus l'instruction @model suivante en haut du fichier Index.cshtml :

@model IEnumerable<MvcMovie.Models.Movie>

La directive @model vous permet d’accéder à la liste des films que le contrôleur a passés à la vue en utilisant un objet Model qui est fortement typé. Par exemple, dans la vue Index.cshtml, le code parcourt les films avec une instruction foreach sur l’objet Model fortement typé.

@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>

Comme l’objet Model est fortement typé (en tant qu’objet IEnumerable<Movie>), chaque élément de la boucle est typé en tant que Movie. Entre autres avantages, cela signifie que votre code est vérifié au moment de la compilation :

Ressources supplémentaires