Tutoriel : Lire les données associées - ASP.NET MVC avec EF Core
Dans le didacticiel précédent, vous a élaboré le modèle de données School. Dans ce didacticiel, vous allez lire et afficher les données associées, à savoir les données qu’Entity Framework charge dans les propriétés de navigation.
Les illustrations suivantes montrent les pages que vous allez utiliser.
Dans ce tutoriel, vous allez :
- Découvrir comment charger les données associées
- Créer une page Courses
- Créer une page Instructors
- En savoir plus sur le chargement explicite
Prérequis
Découvrir comment charger les données associées
Il existe plusieurs façons de permettre à un logiciel de mappage relationnel objet (ORM) comme Entity Framework de charger les données associées dans les propriétés de navigation d’une entité :
Chargement hâtif : Quand l’entité est lue, ses données associées sont également récupérées. Cela génère en général une requête de jointure unique qui récupère toutes les données nécessaires. Vous spécifiez un chargement hâtif dans Entity Framework Core à l’aide des méthodes
Include
etThenInclude
.Vous pouvez récupérer une partie des données dans des requêtes distinctes et EF « corrige » les propriétés de navigation. Autrement dit, EF ajoute automatiquement les entités récupérées séparément là où elles doivent figurer dans les propriétés de navigation des entités précédemment récupérées. Pour la requête qui récupère les données associées, vous pouvez utiliser la méthode
Load
à la place d’une méthode renvoyant une liste ou un objet, telle queToList
ouSingle
.Chargement explicite : Quand l’entité est lue pour la première fois, les données associées ne sont pas récupérées. Vous écrivez un code qui récupère les données associées si elles sont nécessaires. Comme dans le cas du chargement hâtif avec des requêtes distinctes, le chargement explicite génère plusieurs requêtes envoyées à la base de données. La différence tient au fait qu’avec le chargement explicite, le code spécifie les propriétés de navigation à charger. Dans Entity Framework Core 1.1, vous pouvez utiliser la méthode
Load
pour effectuer le chargement explicite. Par exemple :Chargement différé : Quand l’entité est lue pour la première fois, les données associées ne sont pas récupérées. Toutefois, la première fois que vous essayez d’accéder à une propriété de navigation, les données requises pour cette propriété de navigation sont récupérées automatiquement. Une requête est envoyée à la base de données chaque fois que vous essayez d’obtenir des données à partir d’une propriété de navigation pour la première fois. Entity Framework Core 1.0 ne prend pas en charge le chargement différé.
Considérations relatives aux performances
Si vous savez que vous avez besoin des données associées pour toutes les entités récupérées, le chargement hâtif souvent offre des performances optimales, car une seule requête envoyée à la base de données est généralement plus efficace que les requêtes distinctes pour chaque entité récupérée. Par exemple, supposons que chaque département a dix cours associés. Le chargement hâtif de toutes les données associées générerait une seule requête (de jointure) et un seul aller-retour à la base de données. Une requête distincte pour les cours pour chaque département entraînerait onze allers-retours à la base de données. Les allers-retours supplémentaires à la base de données sont particulièrement nuisibles pour les performances lorsque la latence est élevée.
En revanche, dans certains scénarios, les requêtes distinctes s’avèrent plus efficaces. Le chargement hâtif de toutes les données associées dans une seule requête peut entraîner une jointure très complexe à générer, que SQL Server ne peut pas traiter efficacement. Ou, si vous avez besoin d’accéder aux propriétés de navigation d’entité uniquement pour un sous-ensemble des entités que vous traitez, des requêtes distinctes peuvent être plus performantes, car le chargement hâtif de tous les éléments en amont entraînerait la récupération de plus de données qu’il vous faut. Si les performances sont essentielles, il est préférable de tester les performances des deux façons afin d’effectuer le meilleur choix.
Créer une page Courses
L’entité Course
inclut une propriété de navigation qui contient l’entité Department
du service auquel le cours est affecté. Pour afficher le nom du service affecté dans une liste de cours, vous devez obtenir la propriété Name
de l’entité Department
qui figure dans la propriété de navigation Course.Department
.
Créez un contrôleur nommé CoursesController
pour le type d’entité Course
, en utilisant les mêmes options pour le générateur de modèles automatique Contrôleur MVC avec vues, utilisant Entity Framework que vous avez utilisées précédemment pour le StudentsController
, comme indiqué dans l’illustration suivante :
Ouvrez CoursesController.cs
et examinez la méthode Index
. La génération de modèles automatique a spécifié un chargement hâtif pour la propriété de navigation Department
à l’aide de la méthode Include
.
Remplacez la méthode Index
par le code suivant qui utilise un nom plus approprié pour IQueryable
qui renvoie les entités Course (courses
à la place de schoolContext
) :
public async Task<IActionResult> Index()
{
var courses = _context.Courses
.Include(c => c.Department)
.AsNoTracking();
return View(await courses.ToListAsync());
}
Ouvrez Views/Courses/Index.cshtml
et remplacez le code du modèle par le code suivant. Les modifications apparaissent en surbrillance :
@model IEnumerable<ContosoUniversity.Models.Course>
@{
ViewData["Title"] = "Courses";
}
<h2>Courses</h2>
<p>
<a asp-action="Create">Create New</a>
</p>
<table class="table">
<thead>
<tr>
<th>
@Html.DisplayNameFor(model => model.CourseID)
</th>
<th>
@Html.DisplayNameFor(model => model.Title)
</th>
<th>
@Html.DisplayNameFor(model => model.Credits)
</th>
<th>
@Html.DisplayNameFor(model => model.Department)
</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var item in Model)
{
<tr>
<td>
@Html.DisplayFor(modelItem => item.CourseID)
</td>
<td>
@Html.DisplayFor(modelItem => item.Title)
</td>
<td>
@Html.DisplayFor(modelItem => item.Credits)
</td>
<td>
@Html.DisplayFor(modelItem => item.Department.Name)
</td>
<td>
<a asp-action="Edit" asp-route-id="@item.CourseID">Edit</a> |
<a asp-action="Details" asp-route-id="@item.CourseID">Details</a> |
<a asp-action="Delete" asp-route-id="@item.CourseID">Delete</a>
</td>
</tr>
}
</tbody>
</table>
Vous avez apporté les modifications suivantes au code généré automatiquement :
Changement de l’en-tête : Index a été remplacé par Courses.
Ajout d’une colonne Number qui affiche la valeur de la propriété
CourseID
. Par défaut, les clés primaires ne sont pas générées automatiquement, car elles ne sont normalement pas significatives pour les utilisateurs finaux. Toutefois, dans le cas présent, la clé primaire est significative et vous voulez l’afficher.Modification de la colonne Department afin d’afficher le nom du département. Le code affiche la propriété
Name
de l’entitéDepartment
qui est chargée dans la propriété de navigationDepartment
:@Html.DisplayFor(modelItem => item.Department.Name)
Exécutez l’application et sélectionnez l’onglet Courses pour afficher la liste avec les noms des départements.
Créer une page Instructors
Dans cette section, vous allez créer un contrôleur et une vue pour l’entité Instructor
afin d’afficher la page Instructors :
Cette page lit et affiche les données associées comme suit :
La liste des formateurs affiche les données associées de l’entité
OfficeAssignment
. Il existe une relation un-à-zéro-ou-un entre les entitésInstructor
etOfficeAssignment
. Vous allez utiliser un chargement hâtif pour les entitésOfficeAssignment
. Comme expliqué précédemment, le chargement hâtif est généralement plus efficace lorsque vous avez besoin des données associées pour toutes les lignes extraites de la table primaire. Dans ce cas, vous souhaitez afficher les affectations de bureaux pour tous les formateurs affichés.Quand l’utilisateur sélectionne un formateur, les entités
Course
associées sont affichées. Il existe une relation plusieurs-à-plusieurs entre les entitésInstructor
etCourse
. Vous utilisez le chargement hâtif pour les entitésCourse
et leurs entitésDepartment
associées. Dans ce cas, des requêtes distinctes peuvent être plus efficaces, car vous avez besoin de cours uniquement pour le formateur sélectionné. Toutefois, cet exemple montre comment utiliser le chargement hâtif pour des propriétés de navigation dans des entités qui se trouvent elles-mêmes dans des propriétés de navigation.Quand l’utilisateur sélectionne un cours, les données associées du jeu d’entités
Enrollments
s’affichent. Il existe une relation un-à-plusieurs entre les entitésCourse
etEnrollment
. Vous allez utiliser des requêtes distinctes pour les entitésEnrollment
et les entitésStudent
qui leur sont associées.
Créer un modèle de vue pour la vue d’index des formateurs
La page Instructors affiche des données de trois tables différentes. Par conséquent, vous allez créer un modèle de vue qui comprend trois propriétés, chacune contenant les données d’une des tables.
Dans le dossier SchoolViewModels, créez InstructorIndexData.cs
et remplacez le code existant par le code suivant :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace ContosoUniversity.Models.SchoolViewModels
{
public class InstructorIndexData
{
public IEnumerable<Instructor> Instructors { get; set; }
public IEnumerable<Course> Courses { get; set; }
public IEnumerable<Enrollment> Enrollments { get; set; }
}
}
Créer les vues et le contrôleur de formateurs
Créez un contrôleur de formateurs avec des actions de lecture/écriture EF comme indiqué dans l’illustration suivante :
Ouvrez InstructorsController.cs
et ajoutez une instruction using pour l’espace de noms ViewModel :
using ContosoUniversity.Models.SchoolViewModels;
Remplacez la méthode Index par le code suivant pour effectuer un chargement hâtif des données associées et le placer dans le modèle de vue.
public async Task<IActionResult> Index(int? id, int? courseID)
{
var viewModel = new InstructorIndexData();
viewModel.Instructors = await _context.Instructors
.Include(i => i.OfficeAssignment)
.Include(i => i.CourseAssignments)
.ThenInclude(i => i.Course)
.ThenInclude(i => i.Enrollments)
.ThenInclude(i => i.Student)
.Include(i => i.CourseAssignments)
.ThenInclude(i => i.Course)
.ThenInclude(i => i.Department)
.AsNoTracking()
.OrderBy(i => i.LastName)
.ToListAsync();
if (id != null)
{
ViewData["InstructorID"] = id.Value;
Instructor instructor = viewModel.Instructors.Where(
i => i.ID == id.Value).Single();
viewModel.Courses = instructor.CourseAssignments.Select(s => s.Course);
}
if (courseID != null)
{
ViewData["CourseID"] = courseID.Value;
viewModel.Enrollments = viewModel.Courses.Where(
x => x.CourseID == courseID).Single().Enrollments;
}
return View(viewModel);
}
La méthode accepte des données de route facultatives (id
) et un paramètre de chaîne de requête (courseID
) qui fournissent les valeurs d’ID du formateur sélectionné et du cours sélectionné. Ces paramètres sont fournis par les liens hypertexte Select dans la page.
Le code commence par créer une instance du modèle de vue et la placer dans la liste des formateurs. Le code spécifie un chargement hâtif pour les propriétés de navigation Instructor.OfficeAssignment
et Instructor.CourseAssignments
. Dans la propriété CourseAssignments
, la propriété Course
est chargée et, dans ce cadre, les propriétés Enrollments
et Department
sont chargées, et dans chaque entité Enrollment
, la propriété Student
est chargée.
viewModel.Instructors = await _context.Instructors
.Include(i => i.OfficeAssignment)
.Include(i => i.CourseAssignments)
.ThenInclude(i => i.Course)
.ThenInclude(i => i.Enrollments)
.ThenInclude(i => i.Student)
.Include(i => i.CourseAssignments)
.ThenInclude(i => i.Course)
.ThenInclude(i => i.Department)
.AsNoTracking()
.OrderBy(i => i.LastName)
.ToListAsync();
Étant donné que la vue nécessite toujours l’entité OfficeAssignment
, il est plus efficace de l’extraire dans la même requête. Les entités Course sont requises lorsqu’un formateur est sélectionné dans la page web, de sorte qu’une requête individuelle est meilleure que plusieurs requêtes seulement si la page s’affiche plus souvent avec un cours sélectionné que sans.
Le code répète CourseAssignments
et Course
, car vous avez besoin de deux propriétés de Course
. La première chaîne d’appels ThenInclude
obtient CourseAssignment.Course
, Course.Enrollments
et Enrollment.Student
.
Vous pouvez en apprendre plus sur l’inclusion de plusieurs niveaux de données connexes ici.
viewModel.Instructors = await _context.Instructors
.Include(i => i.OfficeAssignment)
.Include(i => i.CourseAssignments)
.ThenInclude(i => i.Course)
.ThenInclude(i => i.Enrollments)
.ThenInclude(i => i.Student)
.Include(i => i.CourseAssignments)
.ThenInclude(i => i.Course)
.ThenInclude(i => i.Department)
.AsNoTracking()
.OrderBy(i => i.LastName)
.ToListAsync();
À ce stade dans le code, un autre ThenInclude
serait pour les propriétés de navigation de Student
, dont vous n’avez pas besoin. Toutefois, l’appel de Include
recommence avec les propriétés Instructor
, donc vous devez parcourir la chaîne à nouveau, cette fois en spécifiant Course.Department
à la place de Course.Enrollments
.
viewModel.Instructors = await _context.Instructors
.Include(i => i.OfficeAssignment)
.Include(i => i.CourseAssignments)
.ThenInclude(i => i.Course)
.ThenInclude(i => i.Enrollments)
.ThenInclude(i => i.Student)
.Include(i => i.CourseAssignments)
.ThenInclude(i => i.Course)
.ThenInclude(i => i.Department)
.AsNoTracking()
.OrderBy(i => i.LastName)
.ToListAsync();
Le code suivant s’exécute quand un formateur a été sélectionné. Le formateur sélectionné est récupéré à partir de la liste des formateurs dans le modèle d’affichage. La propriété Courses
du modèle d’affichage est ensuite chargée avec les entités Course
de la propriété de navigation CourseAssignments
de ce formateur.
if (id != null)
{
ViewData["InstructorID"] = id.Value;
Instructor instructor = viewModel.Instructors.Where(
i => i.ID == id.Value).Single();
viewModel.Courses = instructor.CourseAssignments.Select(s => s.Course);
}
La méthode Where
renvoie une collection, mais dans ce cas, les critères transmis à cette méthode entraînent le renvoi d’une seule entité Instructor. La méthode Single
convertit la collection en une seule entité Instructor
, ce qui vous permet d’accéder à la propriété CourseAssignments
de cette entité. La propriété CourseAssignments
contient des entités CourseAssignment
, à partir desquelles vous souhaitez uniquement les entités Course
associées.
Vous utilisez la méthode Single
sur une collection lorsque vous savez que la collection aura un seul élément. La méthode Single
lève une exception si la collection transmise est vide ou s’il y a plusieurs éléments. Une alternative est SingleOrDefault
, qui renvoie une valeur par défaut (Null dans ce cas) si la collection est vide. Toutefois, dans ce cas, cela entraînerait encore une exception (en tentant de trouver une propriété Courses
sur une référence null) et le message d’exception indiquerait moins clairement la cause du problème. Lorsque vous appelez la méthode Single
, vous pouvez également transmettre la condition Where au lieu d’appeler séparément la méthode Where
:
.Single(i => i.ID == id.Value)
À la place de :
.Where(i => i.ID == id.Value).Single()
Ensuite, si un cours a été sélectionné, le cours sélectionné est récupéré à partir de la liste des cours dans le modèle de vue. Ensuite, la propriété Enrollments
du modèle de vue est chargée avec les entités Enrollment à partir de la propriété de navigation Enrollments
de ce cours.
if (courseID != null)
{
ViewData["CourseID"] = courseID.Value;
viewModel.Enrollments = viewModel.Courses.Where(
x => x.CourseID == courseID).Single().Enrollments;
}
Suivi et non-suivi
Les requêtes sans suivi sont utiles lorsque les résultats sont utilisés dans un scénario en lecture seule. Leur exécution est généralement plus rapide, car il n’est pas nécessaire de configurer les informations de suivi des modifications. Si les entités récupérées à partir de la base de données n’ont pas besoin d’être mises à jour, une requête sans suivi est susceptible de fonctionner mieux qu’une requête avec suivi.
Dans certains cas, une requête avec suivi est plus efficace qu’une requête sans suivi. Pour plus d’informations, consultez Requêtes avec suivi et non-suivi.
Modifier la vue d’index des formateurs
Dans Views/Instructors/Index.cshtml
, remplacez le code du modèle par le code suivant. Les modifications sont mises en surbrillance.
@model ContosoUniversity.Models.SchoolViewModels.InstructorIndexData
@{
ViewData["Title"] = "Instructors";
}
<h2>Instructors</h2>
<p>
<a asp-action="Create">Create New</a>
</p>
<table class="table">
<thead>
<tr>
<th>Last Name</th>
<th>First Name</th>
<th>Hire Date</th>
<th>Office</th>
<th>Courses</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var item in Model.Instructors)
{
string selectedRow = "";
if (item.ID == (int?)ViewData["InstructorID"])
{
selectedRow = "table-success";
}
<tr class="@selectedRow">
<td>
@Html.DisplayFor(modelItem => item.LastName)
</td>
<td>
@Html.DisplayFor(modelItem => item.FirstMidName)
</td>
<td>
@Html.DisplayFor(modelItem => item.HireDate)
</td>
<td>
@if (item.OfficeAssignment != null)
{
@item.OfficeAssignment.Location
}
</td>
<td>
@foreach (var course in item.CourseAssignments)
{
@course.Course.CourseID @course.Course.Title <br />
}
</td>
<td>
<a asp-action="Index" asp-route-id="@item.ID">Select</a> |
<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>
@model ContosoUniversity.Models.SchoolViewModels.InstructorIndexData
@{
ViewData["Title"] = "Instructors";
}
<h2>Instructors</h2>
<p>
<a asp-action="Create">Create New</a>
</p>
<table class="table">
<thead>
<tr>
<th>Last Name</th>
<th>First Name</th>
<th>Hire Date</th>
<th>Office</th>
<th>Courses</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var item in Model.Instructors)
{
string selectedRow = "";
if (item.ID == (int?)ViewData["InstructorID"])
{
selectedRow = "success";
}
<tr class="@selectedRow">
<td>
@Html.DisplayFor(modelItem => item.LastName)
</td>
<td>
@Html.DisplayFor(modelItem => item.FirstMidName)
</td>
<td>
@Html.DisplayFor(modelItem => item.HireDate)
</td>
<td>
@if (item.OfficeAssignment != null)
{
@item.OfficeAssignment.Location
}
</td>
<td>
@foreach (var course in item.CourseAssignments)
{
@course.Course.CourseID @course.Course.Title <br />
}
</td>
<td>
<a asp-action="Index" asp-route-id="@item.ID">Select</a> |
<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>
Vous avez apporté les modifications suivantes au code existant :
Vous avez changé la classe de modèle en
InstructorIndexData
.Vous avez changé le titre de la page en remplaçant Index par Instructors.
Il ajoute une colonne Office qui affiche
item.OfficeAssignment.Location
uniquement siitem.OfficeAssignment
n’est pas Null. (Comme il s’agit d’une relation un-à-zéro-ou-un, il se peut qu’il n’y ait pas d’entité OfficeAssignment associée.)@if (item.OfficeAssignment != null) { @item.OfficeAssignment.Location }
Vous avez ajouté une colonne Courses qui affiche les cours animés par chaque formateur. Pour plus d’informations, consultez la section Conversion de ligne explicite de l’article relatif à la syntaxe Razor.
Ajout de code qui ajoute conditionnellement une classe CSS Bootstrap à l’élément
tr
de l’instructeur sélectionné. Cette classe définit une couleur d’arrière-plan pour la ligne sélectionnée.Vous avez ajouté un nouveau lien hypertexte étiqueté Select immédiatement avant les autres liens dans chaque ligne, ce qui entraîne l’envoi de l’ID du formateur sélectionné à la méthode
Index
.<a asp-action="Index" asp-route-id="@item.ID">Select</a> |
Exécutez l’application et sélectionnez le lien Instructors. La page affiche la propriété Location des entités OfficeAssignment associées et une cellule de table vide lorsqu’il n’existe aucune entité OfficeAssignment associée.
Dans le fichier Views/Instructors/Index.cshtml
, après l’élément de fermeture de table (à la fin du fichier), ajoutez le code suivant. Ce code affiche la liste des cours associés à un formateur quand un formateur est sélectionné.
@if (Model.Courses != null)
{
<h3>Courses Taught by Selected Instructor</h3>
<table class="table">
<tr>
<th></th>
<th>Number</th>
<th>Title</th>
<th>Department</th>
</tr>
@foreach (var item in Model.Courses)
{
string selectedRow = "";
if (item.CourseID == (int?)ViewData["CourseID"])
{
selectedRow = "success";
}
<tr class="@selectedRow">
<td>
@Html.ActionLink("Select", "Index", new { courseID = item.CourseID })
</td>
<td>
@item.CourseID
</td>
<td>
@item.Title
</td>
<td>
@item.Department.Name
</td>
</tr>
}
</table>
}
Ce code lit la propriété Courses
du modèle de vue pour afficher la liste des cours. Il fournit également un lien hypertexte Select qui envoie l’ID du cours sélectionné à la méthode d’action Index
.
Actualisez la page et sélectionnez un formateur. Vous voyez à présent une grille qui affiche les cours affectés au formateur sélectionné et, pour chaque cours, vous voyez le nom du département affecté.
Après le bloc de code que vous venez d’ajouter, ajoutez le code suivant. Ceci affiche la liste des étudiants qui sont inscrits à un cours quand ce cours est sélectionné.
@if (Model.Enrollments != null)
{
<h3>
Students Enrolled in Selected Course
</h3>
<table class="table">
<tr>
<th>Name</th>
<th>Grade</th>
</tr>
@foreach (var item in Model.Enrollments)
{
<tr>
<td>
@item.Student.FullName
</td>
<td>
@Html.DisplayFor(modelItem => item.Grade)
</td>
</tr>
}
</table>
}
Ce code lit la propriété Enrollments
du modèle de vue pour afficher la liste des étudiants inscrits dans ce cours.
Actualisez la page à nouveau et sélectionnez un formateur. Ensuite, sélectionnez un cours pour afficher la liste des étudiants inscrits et leurs notes.
À propos du chargement explicite
Lorsque vous avez récupéré la liste des formateurs dans InstructorsController.cs
, vous avez spécifié un chargement hâtif pour la propriété de navigation CourseAssignments
.
Supposons que vous vous attendiez à ce que les utilisateurs ne souhaitent que rarement voir les inscriptions pour un formateur et un cours sélectionnés. Dans ce cas, vous pouvez charger les données d’inscription uniquement si elles sont demandées. Pour voir un exemple illustrant comment effectuer un chargement explicite, remplacez la méthode Index
par le code suivant, qui supprime le chargement hâtif pour Enrollments
et charge explicitement cette propriété. Les modifications du code apparaissent en surbrillance.
public async Task<IActionResult> Index(int? id, int? courseID)
{
var viewModel = new InstructorIndexData();
viewModel.Instructors = await _context.Instructors
.Include(i => i.OfficeAssignment)
.Include(i => i.CourseAssignments)
.ThenInclude(i => i.Course)
.ThenInclude(i => i.Department)
.OrderBy(i => i.LastName)
.ToListAsync();
if (id != null)
{
ViewData["InstructorID"] = id.Value;
Instructor instructor = viewModel.Instructors.Where(
i => i.ID == id.Value).Single();
viewModel.Courses = instructor.CourseAssignments.Select(s => s.Course);
}
if (courseID != null)
{
ViewData["CourseID"] = courseID.Value;
var selectedCourse = viewModel.Courses.Where(x => x.CourseID == courseID).Single();
await _context.Entry(selectedCourse).Collection(x => x.Enrollments).LoadAsync();
foreach (Enrollment enrollment in selectedCourse.Enrollments)
{
await _context.Entry(enrollment).Reference(x => x.Student).LoadAsync();
}
viewModel.Enrollments = selectedCourse.Enrollments;
}
return View(viewModel);
}
Le nouveau code supprime les appels de la méthode ThenInclude
pour les données d’inscription à partir du code qui extrait les entités de formateur. Il dépose également AsNoTracking
. Si un formateur et un cours sont sélectionnés, le code en évidence récupère les entités Enrollment
pour le cours sélectionné et les entités Student
pour chaque entité Enrollment
.
Exécutez l’application, accédez à la page d’index des formateurs et vous ne verrez aucune différence pour ce qui est affiché dans la page, bien que vous ayez modifié la façon dont les données sont récupérées.
Obtenir le code
Télécharger ou afficher l’application complète.
Étapes suivantes
Dans ce tutoriel, vous allez :
- Chargement des données associées découvert
- Page Courses créée
- Page Instructors créée
- Chargement explicite découvert
Passez au tutoriel suivant pour découvrir comment mettre à jour les données associées.