Partager via


Mise à jour des données associées avec Entity Framework dans une application MVC ASP.NET (6 sur 10)

par Tom Dykstra

L’exemple d’application web Contoso University montre comment créer ASP.NET applications MVC 4 à l’aide d’Entity Framework 5 Code First et de Visual Studio 2012. Pour obtenir des informations sur la série de didacticiels, consultez le premier didacticiel de la série.

Remarque

Si vous rencontrez un problème, téléchargez le chapitre terminé et essayez de reproduire votre problème. Vous pouvez généralement trouver la solution au problème en comparant votre code au code terminé. Pour certaines erreurs courantes et comment les résoudre, consultez Erreurs et solutions de contournement.

Dans le tutoriel précédent, vous avez affiché les données associées ; dans ce tutoriel, vous allez mettre à jour les données associées. Pour la plupart des relations, cela peut être effectué en mettant à jour les champs de clé étrangère appropriés. Pour les relations plusieurs-à-plusieurs, Entity Framework n’expose pas directement la table de jointure. Vous devez donc ajouter et supprimer explicitement des entités dans et à partir des propriétés de navigation appropriées.

Les illustrations suivantes montrent les pages que vous allez utiliser.

Capture d’écran montrant la page Créer un cours.

Capture d’écran montrant la page Modifier de l’instructeur.

Personnaliser les pages Create et Edit pour les cours

Quand une entité de cours est créée, elle doit avoir une relation à un département existant. Pour faciliter cela, le code du modèle généré automatiquement inclut des méthodes de contrôleur, et des vues Create et Edit qui incluent une liste déroulante pour sélectionner le département. La liste déroulante définit la propriété de clé étrangère Course.DepartmentID, qui est tout ce dont Entity Framework a besoin pour charger la propriété de navigation Department avec l’entité Department appropriée. Vous utilisez le code du modèle généré automatiquement, mais que vous modifiez un peu pour ajouter la gestion des erreurs et trier la liste déroulante.

Dans CourseController.cs, supprimez les quatre Edit méthodes et Create remplacez-les par le code suivant :

public ActionResult Create()
{
   PopulateDepartmentsDropDownList();
   return View();
}

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(
   [Bind(Include = "CourseID,Title,Credits,DepartmentID")]
   Course course)
{
   try
   {
      if (ModelState.IsValid)
      {
         db.Courses.Add(course);
         db.SaveChanges();
         return RedirectToAction("Index");
      }
   }
   catch (DataException /* dex */)
   {
      //Log the error (uncomment dex variable name after DataException and add a line here to write a log.)
      ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists, see your system administrator.");
   }
   PopulateDepartmentsDropDownList(course.DepartmentID);
   return View(course);
}

public ActionResult Edit(int id)
{
   Course course = db.Courses.Find(id);
   PopulateDepartmentsDropDownList(course.DepartmentID);
   return View(course);
}

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(
    [Bind(Include = "CourseID,Title,Credits,DepartmentID")]
    Course course)
{
   try
   {
      if (ModelState.IsValid)
      {
         db.Entry(course).State = EntityState.Modified;
         db.SaveChanges();
         return RedirectToAction("Index");
      }
   }
   catch (DataException /* dex */)
   {
      //Log the error (uncomment dex variable name after DataException and add a line here to write a log.)
      ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists, see your system administrator.");
   }
   PopulateDepartmentsDropDownList(course.DepartmentID);
   return View(course);
}

private void PopulateDepartmentsDropDownList(object selectedDepartment = null)
{
   var departmentsQuery = from d in db.Departments
                          orderby d.Name
                          select d;
   ViewBag.DepartmentID = new SelectList(departmentsQuery, "DepartmentID", "Name", selectedDepartment);
} 

La PopulateDepartmentsDropDownList méthode obtient une liste de tous les services triés par nom, crée une SelectList collection pour une liste déroulante et transmet la collection à la vue dans une ViewBag propriété. La méthode accepte le paramètre facultatif selectedDepartment qui permet au code appelant de spécifier l’élément sélectionné lors de l’affichage de la liste déroulante. La vue passe le nom DepartmentID à l’assistanceDropDownList, et l’assistance sait ensuite rechercher dans l’objet ViewBag un SelectList nom.DepartmentID

La HttpGet Create méthode appelle la PopulateDepartmentsDropDownList méthode sans définir l’élément sélectionné, car pour un nouveau cours, le service n’est pas encore établi :

public ActionResult Create()
{
    PopulateDepartmentsDropDownList();
    return View();
}

La HttpGet Edit méthode définit l’élément sélectionné, en fonction de l’ID du service déjà affecté au cours en cours de modification :

public ActionResult Edit(int id)
{
    Course course = db.Courses.Find(id);
    PopulateDepartmentsDropDownList(course.DepartmentID);
    return View(course);
}

Les HttpPost méthodes pour les deux Create et Edit incluent également du code qui définit l’élément sélectionné lorsqu’ils réaffichent la page après une erreur :

catch (DataException /* dex */)
{
    //Log the error (uncomment dex variable name after DataException and add a line here to write a log.)
    ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists, see your system administrator.");
}
PopulateDepartmentsDropDownList(course.DepartmentID);
return View(course);

Ce code garantit que lorsque la page est réaffichée pour afficher le message d’erreur, le service sélectionné reste sélectionné.

Dans Views\Course\Create.cshtml, ajoutez le code mis en surbrillance pour créer un champ numéro de cours avant le champ Titre . Comme expliqué dans un didacticiel précédent, les champs de clé primaire ne sont pas générés par défaut, mais cette clé primaire est significative. Vous souhaitez donc que l’utilisateur puisse entrer la valeur de la clé.

@model ContosoUniversity.Models.Course

@{
    ViewBag.Title = "Create";
}

<h2>Create</h2>

@using (Html.BeginForm())
{
    @Html.AntiForgeryToken()
    @Html.ValidationSummary(true)

    <fieldset>
        <legend>Course</legend>

        <div class="editor-label">
            @Html.LabelFor(model => model.CourseID)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.CourseID)
            @Html.ValidationMessageFor(model => model.CourseID)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.Title)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Title)
            @Html.ValidationMessageFor(model => model.Title)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.Credits)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Credits)
            @Html.ValidationMessageFor(model => model.Credits)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.DepartmentID, "Department")
        </div>
        <div class="editor-field">
            @Html.DropDownList("DepartmentID", String.Empty)
            @Html.ValidationMessageFor(model => model.DepartmentID)
        </div>

        <p>
            <input type="submit" value="Create" />
        </p>
    </fieldset>
}

<div>
    @Html.ActionLink("Back to List", "Index")
</div>

@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
}

Dans Views\Course\Edit.cshtml, Views\Course\Delete.cshtml et Views\Course\Details.cshtml, ajoutez un champ numéro de cours avant le champ Titre . Comme il s’agit de la clé primaire, elle est affichée, mais elle ne peut pas être modifiée.

<div class="editor-label">
    @Html.LabelFor(model => model.CourseID)
</div>
<div class="editor-field">
    @Html.DisplayFor(model => model.CourseID)
</div>

Exécutez la page Créer (affichez la page Index de cours, puis cliquez sur Créer) et entrez des données pour un nouveau cours :

Course_create_page

Cliquez sur Créer. La page Index de cours s’affiche avec le nouveau cours ajouté à la liste. Le nom du département dans la liste de la page Index provient de la propriété de navigation, ce qui montre que la relation a été établie correctement.

Course_Index_page_showing_new_course

Exécutez la page Modifier (affichez la page Index du cours, puis cliquez sur Modifier sur un cours).

Course_edit_page

Modifiez les données dans la page et cliquez sur Save. La page Index de cours s’affiche avec les données de cours mises à jour.

Ajout d’une page de modification pour les instructeurs

Quand vous modifiez un enregistrement de formateur, vous voulez avoir la possibilité de mettre à jour l’attribution du bureau du formateur. L’entité Instructor a une relation un-à-zéro-ou-un avec l’entité OfficeAssignment , ce qui signifie que vous devez gérer les situations suivantes :

  • Si l’utilisateur efface l’attribution du bureau et qu’il a initialement une valeur, vous devez supprimer et supprimer l’entité OfficeAssignment .
  • Si l’utilisateur entre une valeur d’affectation de bureau et qu’il a été vide à l’origine, vous devez créer une entité OfficeAssignment .
  • Si l’utilisateur modifie la valeur d’une affectation de bureau, vous devez modifier la valeur dans une entité existante OfficeAssignment .

Ouvrez InstructorController.cs et examinez la HttpGet Edit méthode :

public ActionResult Edit(int id = 0)
{
    Instructor instructor = db.Instructors.Find(id);
    if (instructor == null)
    {
        return HttpNotFound();
    }
    ViewBag.InstructorID = new SelectList(db.OfficeAssignments, "InstructorID", "Location", instructor.InstructorID);
    return View(instructor);
}

Le code généré automatiquement ici n’est pas ce que vous voulez. Il configure des données pour une liste déroulante, mais vous avez besoin d’une zone de texte. Remplacez cette méthode par le code suivant :

public ActionResult Edit(int id)
{
    Instructor instructor = db.Instructors
        .Include(i => i.OfficeAssignment)
        .Where(i => i.InstructorID == id)
        .Single();
    return View(instructor);
}

Ce code supprime l’instruction ViewBag et ajoute un chargement impatient pour l’entité associée OfficeAssignment . Vous ne pouvez pas effectuer de chargement désireux avec la Find méthode, de sorte que les Where méthodes et Single les méthodes sont utilisées à la place pour sélectionner l’instructeur.

Remplacez la HttpPost Edit méthode par le code suivant. qui gère les mises à jour des affectations de bureau :

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(int id, FormCollection formCollection)
{
   var instructorToUpdate = db.Instructors
       .Include(i => i.OfficeAssignment)
       .Where(i => i.InstructorID == id)
       .Single();

   if (TryUpdateModel(instructorToUpdate, "",
      new string[] { "LastName", "FirstMidName", "HireDate", "OfficeAssignment" }))
   {
      try
      {
         if (String.IsNullOrWhiteSpace(instructorToUpdate.OfficeAssignment.Location))
         {
            instructorToUpdate.OfficeAssignment = null;
         }

         db.Entry(instructorToUpdate).State = EntityState.Modified;
         db.SaveChanges();

         return RedirectToAction("Index");
      }
      catch (DataException /* dex */)
      {
         //Log the error (uncomment dex variable name after DataException and add a line here to write a log.
         ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists, see your system administrator.");
      }
   }
   ViewBag.InstructorID = new SelectList(db.OfficeAssignments, "InstructorID", "Location", id);
   return View(instructorToUpdate);
}

Le code effectue les actions suivantes :

  • Obtient l'entité Instructor en cours à partir de la base de données à l’aide d’un chargement hâtif de la propriété de navigation OfficeAssignment. C’est la même chose que ce que vous avez fait dans la HttpGet Edit méthode.

  • Met à jour l’entité Instructor récupérée avec les valeurs du classeur de modèles. La surcharge TryUpdateModel utilisée vous permet de lister en toute sécurité les propriétés que vous souhaitez inclure. Ceci empêche la survalidation, comme expliqué dans le deuxième didacticiel.

    if (TryUpdateModel(instructorToUpdate, "",
          new string[] { "LastName", "FirstMidName", "HireDate", "OfficeAssignment" }))
    
  • Si l’emplacement du bureau est vide, il définit la propriété Instructor.OfficeAssignment sur null, de façon que la ligne correspondante dans la table OfficeAssignment soit supprimée.

    if (String.IsNullOrWhiteSpace(instructorToUpdate.OfficeAssignment.Location))
    {
        instructorToUpdate.OfficeAssignment = null;
    }
    
  • Il enregistre les modifications dans la base de données.

Dans Views\Instructor\Edit.cshtml, après les div éléments du champ Hire Date , ajoutez un nouveau champ pour modifier l’emplacement du bureau :

<div class="editor-label">
    @Html.LabelFor(model => model.OfficeAssignment.Location)
</div>
<div class="editor-field">
    @Html.EditorFor(model => model.OfficeAssignment.Location)
    @Html.ValidationMessageFor(model => model.OfficeAssignment.Location)
</div>

Exécutez la page (sélectionnez l’onglet Instructeurs , puis cliquez sur Modifier sur un instructeur). Modifiez Office Location et cliquez sur Save.

Changing_the_office_location

Ajout de devoirs de cours à la page Modifier l’instructeur

Les instructeurs peuvent enseigner dans n’importe quel nombre de cours. Maintenant, vous allez améliorer la page de modification des formateurs en ajoutant la possibilité de modifier les affectations de cours avec un groupe de cases à cocher, comme le montre la capture d’écran suivante :

Capture d’écran montrant la page Modifier de l’instructeur avec des cours.

La relation entre les Course entités est Instructor plusieurs-à-plusieurs, ce qui signifie que vous n’avez pas d’accès direct à la table de jointure. Au lieu de cela, vous allez ajouter et supprimer des entités à et à partir de la Instructor.Courses propriété de navigation.

L’interface utilisateur qui vous permet de changer les cours auxquels un formateur est affecté est un groupe de cases à cocher. Une case à cocher est affichée pour chaque cours de la base de données, et ceux auxquels le formateur est actuellement affecté sont sélectionnés. L’utilisateur peut cocher ou décocher les cases pour changer les affectations de cours. Si le nombre de cours était beaucoup plus grand, vous voudriez probablement utiliser une autre méthode de présentation des données dans la vue, mais vous utiliseriez la même méthode de manipulation des propriétés de navigation pour créer ou supprimer des relations.

Pour fournir des données à la vue pour la liste de cases à cocher, vous utilisez une classe de modèle de vue. Créez AssignedCourseData.cs dans le dossier ViewModels et remplacez le code existant par le code suivant :

namespace ContosoUniversity.ViewModels
{
    public class AssignedCourseData
    {
        public int CourseID { get; set; }
        public string Title { get; set; }
        public bool Assigned { get; set; }
    }
}

Dans InstructorController.cs, remplacez la HttpGet Edit méthode par le code suivant. Les modifications sont mises en surbrillance.

public ActionResult Edit(int id)
{
    Instructor instructor = db.Instructors
        .Include(i => i.OfficeAssignment)
        .Include(i => i.Courses)
        .Where(i => i.InstructorID == id)
        .Single();
    PopulateAssignedCourseData(instructor);
    return View(instructor);
}

private void PopulateAssignedCourseData(Instructor instructor)
{
    var allCourses = db.Courses;
    var instructorCourses = new HashSet<int>(instructor.Courses.Select(c => c.CourseID));
    var viewModel = new List<AssignedCourseData>();
    foreach (var course in allCourses)
    {
        viewModel.Add(new AssignedCourseData
        {
            CourseID = course.CourseID,
            Title = course.Title,
            Assigned = instructorCourses.Contains(course.CourseID)
        });
    }
    ViewBag.Courses = viewModel;
}

Le code ajoute un chargement hâtif pour la propriété de navigation Courses et appelle la nouvelle méthode PopulateAssignedCourseData pour fournir des informations pour le tableau de cases à cocher avec la classe de modèle de vue AssignedCourseData.

Le code de la méthodePopulateAssignedCourseData lit toutes les entités Course pour charger une liste de cours avec la classe de modèle de vue. Pour chaque cours, le code vérifie s’il existe dans la propriété de navigation Courses du formateur. Pour créer une recherche efficace lors de la vérification de l’affectation d’un cours au formateur, les cours affectés à l’instructeur sont placés dans une collection HashSet . La Assigned propriété est définie true pour les cours auxquels l’instructeur est affecté. La vue utilise cette propriété pour déterminer quelles cases doivent être affichées cochées. Enfin, la liste est passée à l’affichage dans une ViewBag propriété.

Ensuite, ajoutez le code qui est exécuté quand l’utilisateur clique sur Save. Remplacez la HttpPost Edit méthode par le code suivant, qui appelle une nouvelle méthode qui met à jour la Courses propriété de navigation de l’entité Instructor . Les modifications sont mises en surbrillance.

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(int id, FormCollection formCollection, string[] selectedCourses)
{
   var instructorToUpdate = db.Instructors
       .Include(i => i.OfficeAssignment)
       .Include(i => i.Courses)
       .Where(i => i.InstructorID == id)
       .Single();
   if (TryUpdateModel(instructorToUpdate, "", 
      new string[] { "LastName", "FirstMidName", "HireDate", "OfficeAssignment" }))
   {
      try
      {
         if (String.IsNullOrWhiteSpace(instructorToUpdate.OfficeAssignment.Location))
         {
            instructorToUpdate.OfficeAssignment = null;
         }

         UpdateInstructorCourses(selectedCourses, instructorToUpdate);

         db.Entry(instructorToUpdate).State = EntityState.Modified;
         db.SaveChanges();

         return RedirectToAction("Index");
      }
      catch (DataException /* dex */)
      {
         //Log the error (uncomment dex variable name after DataException and add a line here to write a log.
         ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists see your system administrator.");
      }
   }
   PopulateAssignedCourseData(instructorToUpdate);
   return View(instructorToUpdate);
}

private void UpdateInstructorCourses(string[] selectedCourses, Instructor instructorToUpdate)
{
   if (selectedCourses == null)
   {
      instructorToUpdate.Courses = new List<Course>();
      return;
   }

   var selectedCoursesHS = new HashSet<string>(selectedCourses);
   var instructorCourses = new HashSet<int>
       (instructorToUpdate.Courses.Select(c => c.CourseID));
   foreach (var course in db.Courses)
   {
      if (selectedCoursesHS.Contains(course.CourseID.ToString()))
      {
         if (!instructorCourses.Contains(course.CourseID))
         {
            instructorToUpdate.Courses.Add(course);
         }
      }
      else
      {
         if (instructorCourses.Contains(course.CourseID))
         {
            instructorToUpdate.Courses.Remove(course);
         }
      }
   }
}

Étant donné que la vue n’a pas de collection d’entités Course , le classeur de modèles ne peut pas mettre à jour automatiquement la Courses propriété de navigation. Au lieu d’utiliser le classeur de modèles pour mettre à jour la propriété de navigation Courses, vous allez le faire dans la nouvelle UpdateInstructorCourses méthode. Par conséquent, vous devez exclure la propriété Courses de la liaison de modèle. Cela ne nécessite aucune modification du code qui appelle TryUpdateModel , car vous utilisez la surcharge de mise en liste sécurisée et Courses n’est pas dans la liste include.

Si aucune case à cocher n’a été cochée, le code dans initialise UpdateInstructorCourses la Courses propriété de navigation avec une collection vide :

if (selectedCourses == null)
{
    instructorToUpdate.Courses = new List<Course>();
    return;
}

Le code boucle ensuite à travers tous les cours dans la base de données, et vérifie chaque cours par rapport à ceux actuellement affectés au formateur relativement à ceux qui ont été sélectionnés dans la vue. Pour faciliter des recherches efficaces, les deux dernières collections sont stockées dans des objets HashSet.

Si la case pour un cours a été cochée mais que le cours n’est pas dans la propriété de navigation Instructor.Courses, le cours est ajouté à la collection dans la propriété de navigation.

if (selectedCoursesHS.Contains(course.CourseID.ToString()))
{
    if (!instructorCourses.Contains(course.CourseID))
    {
        instructorToUpdate.Courses.Add(course);
    }
}

Si la case pour un cours a été cochée mais que le cours est dans la propriété de navigation Instructor.Courses, le cours est supprimé de la propriété de navigation.

else
{
    if (instructorCourses.Contains(course.CourseID))
    {
        instructorToUpdate.Courses.Remove(course);
    }
}

Dans Views\Instructor\Edit.cshtml, ajoutez un champ Courses avec un tableau de cases à cocher en ajoutant le code en surbrillance suivant immédiatement après les div éléments du OfficeAssignment champ :

@model ContosoUniversity.Models.Instructor

@{
    ViewBag.Title = "Edit";
}

<h2>Edit</h2>

@using (Html.BeginForm())
{
    @Html.AntiForgeryToken()
    @Html.ValidationSummary(true)

    <fieldset>
        <legend>Instructor</legend>

        @Html.HiddenFor(model => model.InstructorID)

        <div class="editor-label">
            @Html.LabelFor(model => model.LastName)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.LastName)
            @Html.ValidationMessageFor(model => model.LastName)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.FirstMidName)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.FirstMidName)
            @Html.ValidationMessageFor(model => model.FirstMidName)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.HireDate)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.HireDate)
            @Html.ValidationMessageFor(model => model.HireDate)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.OfficeAssignment.Location)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.OfficeAssignment.Location)
            @Html.ValidationMessageFor(model => model.OfficeAssignment.Location)
        </div>

        <div class="editor-field">
    <table>
        <tr>
            @{
                int cnt = 0;
                List<ContosoUniversity.ViewModels.AssignedCourseData> courses = ViewBag.Courses;

                foreach (var course in courses) {
                    if (cnt++ % 3 == 0) {
                        @:  </tr> <tr> 
                    }
                    @: <td> 
                        <input type="checkbox" 
                               name="selectedCourses" 
                               value="@course.CourseID" 
                               @(Html.Raw(course.Assigned ? "checked=\"checked\"" : "")) /> 
                        @course.CourseID @:  @course.Title
                    @:</td>
                }
                @: </tr>
            }
    </table>
</div>
        <p>
            <input type="submit" value="Save" />
        </p>
    </fieldset>
}

<div>
    @Html.ActionLink("Back to List", "Index")
</div>

@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
}

Ce code crée un tableau HTML qui a trois colonnes. Dans chaque colonne se trouve une case à cocher, suivie d’une légende qui est constituée du numéro et du titre du cours. Les cases à cocher ont tous le même nom (« selectedCourses »), qui informe le classeur de modèles qu’ils doivent être traités comme un groupe. L’attribut value de chaque case à cocher est défini sur la valeur de CourseID. Lorsque la page est publiée, le classeur de modèles transmet un tableau au contrôleur qui se compose des valeurs pour seules les CourseID cases à cocher sélectionnées.

Lorsque les cases à cocher sont initialement affichées, celles qui sont destinées aux cours affectés au formateur ont checked des attributs, ce qui les sélectionne (les affiche activées).

Après avoir modifié les devoirs de cours, vous devez être en mesure de vérifier les modifications lorsque le site revient à la Index page. Par conséquent, vous devez ajouter une colonne à la table dans cette page. Dans ce cas, vous n’avez pas besoin d’utiliser l’objet ViewBag , car les informations que vous souhaitez afficher se trouvent déjà dans la Courses propriété de navigation de l’entité Instructor que vous passez à la page en tant que modèle.

Dans Views\Instructor\Index.cshtml, ajoutez un titre Courses immédiatement après le titre Office , comme illustré dans l’exemple suivant :

<tr> 
    <th></th> 
    <th>Last Name</th> 
    <th>First Name</th> 
    <th>Hire Date</th> 
    <th>Office</th>
    <th>Courses</th>
</tr>

Ensuite, ajoutez une nouvelle cellule de détail immédiatement après la cellule de détails de l’emplacement du bureau :

@model ContosoUniversity.ViewModels.InstructorIndexData

@{
    ViewBag.Title = "Instructors";
}

<h2>Instructors</h2>

<p>
    @Html.ActionLink("Create New", "Create")
</p>
<table>
    <tr>
        <th></th>
        <th>Last Name</th>
        <th>First Name</th>
        <th>Hire Date</th>
        <th>Office</th>
        <th>Courses</th>
    </tr>
    @foreach (var item in Model.Instructors)
    {
        string selectedRow = "";
        if (item.InstructorID == ViewBag.InstructorID)
        {
            selectedRow = "selectedrow";
        } 
        <tr class="@selectedRow" valign="top">
            <td>
                @Html.ActionLink("Select", "Index", new { id = item.InstructorID }) | 
                @Html.ActionLink("Edit", "Edit", new { id = item.InstructorID }) | 
                @Html.ActionLink("Details", "Details", new { id = item.InstructorID }) | 
                @Html.ActionLink("Delete", "Delete", new { id = item.InstructorID })
            </td>
            <td>
                @item.LastName
            </td>
            <td>
                @item.FirstMidName
            </td>
            <td>
                @String.Format("{0:d}", item.HireDate)
            </td>
            <td>
                @if (item.OfficeAssignment != null)
                { 
                    @item.OfficeAssignment.Location  
                }
            </td>
            <td>
                @{
                foreach (var course in item.Courses)
                {
                    @course.CourseID @:  @course.Title <br />
                }
                }
            </td>
        </tr> 
    }
</table>

@if (Model.Courses != null)
{ 
    <h3>Courses Taught by Selected Instructor</h3> 
    <table>
        <tr>
            <th></th>
            <th>ID</th>
            <th>Title</th>
            <th>Department</th>
        </tr>

        @foreach (var item in Model.Courses)
        {
            string selectedRow = "";
            if (item.CourseID == ViewBag.CourseID)
            {
                selectedRow = "selectedrow";
            } 
        
            <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> 
}
@if (Model.Enrollments != null)
{ 
    <h3>Students Enrolled in Selected Course</h3> 
    <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> 
}

Exécutez la page Index de l’instructeur pour afficher les cours attribués à chaque instructeur :

Instructor_index_page

Cliquez sur Modifier sur un instructeur pour afficher la page Modifier.

Instructor_edit_page_with_courses

Modifiez certaines affectations de cours, puis cliquez sur Enregistrer. Les modifications que vous apportez sont reflétées dans la page Index.

Remarque : L’approche adoptée pour modifier les données de cours d’instructeur fonctionne bien lorsqu’il existe un nombre limité de cours. Pour les collections qui sont beaucoup plus volumineuses, une autre interface utilisateur et une autre méthode de mise à jour seraient nécessaires.

Mettre à jour la méthode Delete

Modifiez le code dans la méthode HttpPost Delete afin que l’enregistrement d’affectation du bureau (le cas échéant) soit supprimé lorsque l’instructeur est supprimé :

[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public ActionResult DeleteConfirmed(int id)
{
   Instructor instructor = db.Instructors
     .Include(i => i.OfficeAssignment)
     .Where(i => i.InstructorID == id)
     .Single();

   instructor.OfficeAssignment = null;
   db.Instructors.Remove(instructor);
   db.SaveChanges();
   return RedirectToAction("Index");
}

Si vous essayez de supprimer un instructeur affecté à un service en tant qu’administrateur, vous obtiendrez une erreur d’intégrité référentielle. Consultez la version actuelle de ce didacticiel pour obtenir du code supplémentaire qui supprimera automatiquement l’instructeur de n’importe quel service où l’instructeur est affecté en tant qu’administrateur.

Résumé

Vous avez maintenant terminé cette présentation de l’utilisation des données associées. Jusqu’à présent, dans ces didacticiels, vous avez effectué une gamme complète d’opérations CRUD, mais vous n’avez pas traité de problèmes d’accès concurrentiel. Le tutoriel suivant présente la rubrique relative à la concurrence, explique les options de gestion et ajoute la gestion de la concurrence au code CRUD que vous avez déjà écrit pour un type d’entité.

Vous trouverez des liens vers d’autres ressources Entity Framework à la fin du dernier didacticiel de cette série.