Freigeben über


Aktualisieren verwandter Daten mit dem Entity Framework in einer ASP.NET MVC-Anwendung (6 von 10)

Von Tom Dykstra

Die Contoso University-Beispielwebanwendung veranschaulicht, wie ASP.NET MVC 4-Anwendungen mithilfe von Entity Framework 5 Code First und Visual Studio 2012 erstellt werden. Informationen zu dieser Tutorialreihe finden Sie im ersten Tutorial der Reihe.

Hinweis

Wenn ein Problem auftritt, das Sie nicht beheben können, laden Sie das fertige Kapitel herunter, und versuchen Sie, Ihr Problem zu reproduzieren. In der Regel finden Sie die Lösung für das Problem, indem Sie Ihren Code mit dem fertigen Code vergleichen. Einige häufige Fehler und deren Lösung finden Sie unter "Fehler und Problemumgehungen".

Im vorherigen Lernprogramm haben Sie verwandte Daten angezeigt; In diesem Lernprogramm aktualisieren Sie verwandte Daten. Für die meisten Beziehungen kann dies durch Aktualisieren der entsprechenden Fremdschlüsselfelder erfolgen. Für n:n-Beziehungen macht Entity Framework die Verknüpfungstabelle nicht direkt verfügbar, daher müssen Sie Entitäten explizit zu und aus den entsprechenden Navigationseigenschaften hinzufügen und daraus entfernen.

Die folgenden Abbildungen zeigen die Seiten, mit den Sie arbeiten werden.

Screenshot der Seite

Screenshot, der die Seite

Anpassen der Seiten „Erstellen“ und „Bearbeiten“ für Kurse

Wenn eine neue Kursentität erstellt wird, muss diese in Beziehung zu einer vorhandenen Abteilung stehen. Um dies zu vereinfachen, enthält der Gerüstcode Controllermethoden und Ansichten zum „Erstellen“ und „Bearbeiten“, die eine Dropdownliste enthalten, aus denen der Fachbereich ausgewählt werden kann. Die Dropdownliste legt die Fremdschlüsseleigenschaft Course.DepartmentID fest. Mehr benötigt Entity Framework nicht, um die Navigationseigenschaft Department mit der passenden Department-Entität zu laden. Verwenden Sie den Gerüstcode, aber nehmen Sie kleine Änderungen vor, um die Fehlerbehandlung hinzuzufügen und die Dropdownliste zu sortieren.

Löschen Sie in CourseController.cs die vier Edit MethodenCreate, und ersetzen Sie sie durch den folgenden Code:

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

Die PopulateDepartmentsDropDownList Methode ruft eine Liste aller Abteilungen nach Namen ab, erstellt eine SelectList Auflistung für eine Dropdownliste und übergibt die Auflistung an die Ansicht in einer ViewBag Eigenschaft. Die Methode akzeptiert den optionalen selectedDepartment-Parameter, über den der Code das Element angeben kann, das ausgewählt wird, wenn die Dropdownliste gerendert wird. Die Ansicht übergibt den Namen DepartmentID an den DropDownList Hilfsprogramm, und der Hilfsprogramm weiß dann, im ViewBag Objekt nach einem SelectList benannten DepartmentIDObjekt zu suchen.

Die HttpGet Create Methode ruft die PopulateDepartmentsDropDownList Methode auf, ohne das ausgewählte Element festzulegen, da für einen neuen Kurs die Abteilung noch nicht eingerichtet ist:

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

Die HttpGet Edit Methode legt das ausgewählte Element basierend auf der ID der Abteilung fest, die bereits dem bearbeiteten Kurs zugewiesen ist:

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

Die HttpPost Methoden für beide und Edit Create auch Code, der das ausgewählte Element festlegt, wenn sie die Seite nach einem Fehler erneut anzeigen:

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

Dieser Code stellt sicher, dass beim erneuten Anzeigen der Seite, um die Fehlermeldung anzuzeigen, die ausgewählte Abteilung ausgewählt bleibt.

Fügen Sie in Views\Course\Create.cshtml den hervorgehobenen Code hinzu, um vor dem Feld "Titel " ein neues Feld "Kursnummer" zu erstellen. Wie in einem früheren Lernprogramm erläutert, sind Primärschlüsselfelder standardmäßig nicht gerüstet, aber dieser Primärschlüssel ist aussagekräftig, sodass der Benutzer in der Lage sein soll, den Schlüsselwert einzugeben.

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

Fügen Sie in Views\Course\Edit.cshtml, Views\Course\Delete.cshtml und Views\Course\Details.cshtml ein Kursnummernfeld vor dem Feld "Titel " hinzu. Da er der Primärschlüssel ist, wird er angezeigt, kann aber nicht geändert werden.

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

Führen Sie die Seite "Erstellen" aus (zeigen Sie die Seite "Kursindex" an, und klicken Sie auf " Neu erstellen", und geben Sie Daten für einen neuen Kurs ein:

Course_create_page

Klicken Sie auf Erstellen. Die Seite "Kursindex" wird mit dem neuen Kurs angezeigt, der der Liste hinzugefügt wurde. Der Fachbereichsname in der Indexseitenliste wurde der Navigationseigenschaft entnommen und deutet darauf hin, dass die Beziehung ordnungsgemäß festgelegt wurde.

Course_Index_page_showing_new_course

Führen Sie die Seite "Bearbeiten" aus (zeigen Sie die Seite "Kursindex" an, und klicken Sie auf "Auf einem Kurs bearbeiten ").

Course_edit_page

Ändern Sie die Daten auf der Seite, und klicken Sie auf Speichern. Die Seite "Kursindex" wird mit den aktualisierten Kursdaten angezeigt.

Hinzufügen einer Bearbeitungsseite für Kursleiter

Bei der Bearbeitung eines Dozentendatensatzes sollten Sie auch die Bürozuweisung des Dozenten aktualisieren. Die Instructor Entität hat eine 1:0-1-Beziehung mit der OfficeAssignment Entität, was bedeutet, dass Sie die folgenden Situationen behandeln müssen:

  • Wenn der Benutzer die Office-Zuweisung löscht und ursprünglich einen Wert hatte, müssen Sie die OfficeAssignment Entität entfernen und löschen.
  • Wenn der Benutzer einen Office-Zuordnungswert eingibt und ursprünglich leer war, müssen Sie eine neue OfficeAssignment Entität erstellen.
  • Wenn der Benutzer den Wert einer Bürozuweisung ändert, müssen Sie den Wert in einer vorhandenen OfficeAssignment Entität ändern.

Öffnen Sie InstructorController.cs , und sehen Sie sich die HttpGet Edit Methode an:

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

Der hier enthaltene Gerüstcode ist nicht das, was Sie möchten. Sie richten Daten für eine Dropdownliste ein, aber Sie benötigen ein Textfeld. Ersetzen Sie diese Methode durch den folgenden Code:

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

Dieser Code legt die ViewBag Anweisung ab und fügt eifrig das Laden für die zugeordnete OfficeAssignment Entität hinzu. Sie können keine eifrigen Ladevorgänge mit der Find Methode ausführen, daher werden stattdessen die Where Methoden und Single Methoden verwendet, um den Kursleiter auszuwählen.

Ersetzen Sie die HttpPost Edit Methode durch den folgenden Code. die Office-Zuweisungsaktualisierungen behandelt:

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

Der Code führt Folgendes aus:

  • Ruft die aktuelle Entität Instructor von der Datenbank über Eager Loading für die Navigationseigenschaft OfficeAssignment ab. Dies entspricht dem, was Sie in der HttpGet Edit Methode getan haben.

  • Aktualisiert die abgerufene Entität Instructor mit Werten aus der Modellbindung. Mit der verwendeten TryUpdateModel-Überladung können Sie die eigenschaften, die Sie einschließen möchten, sicher auflisten . Dadurch wird, wie im zweiten Tutorial beschrieben, vermieden, dass zu viele Angaben gemacht werden.

    if (TryUpdateModel(instructorToUpdate, "",
          new string[] { "LastName", "FirstMidName", "HireDate", "OfficeAssignment" }))
    
  • Wenn kein Standort für das Büro angegeben wird, wird die Instructor.OfficeAssignment-Eigenschaft auf NULL festgelegt, sodass die zugehörige Zeile aus der OfficeAssignment-Tabelle gelöscht wird.

    if (String.IsNullOrWhiteSpace(instructorToUpdate.OfficeAssignment.Location))
    {
        instructorToUpdate.OfficeAssignment = null;
    }
    
  • Speichert die Änderungen in der Datenbank.

Fügen Sie in Views\Instructor\Edit.cshtml nach den div Elementen für das Feld Einstellungsdatum ein neues Feld zum Bearbeiten des Bürostandorts hinzu:

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

Führen Sie die Seite aus (wählen Sie die Registerkarte "Kursleiter" aus, und klicken Sie dann auf "Instruktor bearbeiten "). Ändern Sie den Standort des Büros, und klicken Sie auf Speichern.

Changing_the_office_location

Hinzufügen von Kursaufgaben zur Seite "Kursleiter bearbeiten"

Dozenten können eine beliebige Anzahl von Kursen unterrichten. Jetzt soll die Dozentenseite „Bearbeiten“ verbessert werden, indem es ermöglicht wird, Kurszuweisungen über eine Reihe von Kontrollkästchen zu verändern. Dies wird im folgenden Screenshot dargestellt:

Screenshot der Seite

Die Beziehung zwischen den Entitäten und Instructor den Course Entitäten ist n:n, was bedeutet, dass Sie keinen direkten Zugriff auf die Verknüpfungstabelle haben. Stattdessen werden Sie Entitäten zu und aus der Instructor.Courses Navigationseigenschaft hinzufügen und daraus entfernen.

Die Benutzeroberfläche, über die Sie ändern können, welchen Kursen ein Dozent zugewiesen ist, besteht aus einer Reihe von Kontrollkästchen. Für jeden Kurs in der Datenbank wird ein Kontrollkästchen angezeigt. Die Kontrollkästchen, denen der Dozent zu diesem Zeitpunkt zugewiesen ist, sind aktiviert. Der Benutzer kann Kontrollkästchen aktivieren oder deaktivieren, um Kurszuweisungen zu ändern. Wenn die Anzahl der Kurse viel größer wäre, würden Sie wahrscheinlich eine andere Methode zum Darstellen der Daten in der Ansicht verwenden, aber Sie würden dieselbe Methode zum Bearbeiten von Navigationseigenschaften verwenden, um Beziehungen zu erstellen oder zu löschen.

Verwenden Sie eine Ansichtsmodellklasse, um Daten für die Ansicht bereitzustellen, um eine Liste mit Kontrollkästchen zu erstellen. Erstellen Sie AssignedCourseData.cs im Ordner "ViewModels", und ersetzen Sie den vorhandenen Code durch den folgenden Code:

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

Ersetzen Sie in InstructorController.cs die HttpGet Edit Methode durch den folgenden Code. Die Änderungen werden hervorgehoben.

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

Über den Code wird für die Courses-Navigationseigenschaft Eager Loading hinzugefügt und die neue PopulateAssignedCourseData-Methode aufgerufen, um über die Ansichtsmodellklasse AssignedCourseData Informationen für das Kontrollkästchenarray zur Verfügung zu stellen.

Der Code in der PopulateAssignedCourseData-Methode liest alle Course-Entitäten, um mithilfe der Ansichtsmodellklasse eine Liste der Kurse zu laden. Für jeden Kurs überprüft der Code, ob dieser in der Courses-Navigationseigenschaft des Dozenten vorhanden ist. Um eine effiziente Suche zu erstellen, wenn geprüft wird, ob dem Kursleiter ein Kurs zugewiesen ist, werden die dem Kursleiter zugewiesenen Kurse in eine HashSet-Sammlung eingefügt. Die Assigned Eigenschaft ist für Kurse festgelegt true , denen der Kursleiter zugewiesen ist. Die Ansicht verwendet diese Eigenschaft, um zu bestimmen, welche Kontrollkästchen als aktiviert angezeigt werden sollen. Schließlich wird die Liste an die Ansicht in einer ViewBag Eigenschaft übergeben.

Fügen Sie als nächstes den Code hinzu, der ausgeführt wird, wenn der Benutzer auf Speichern klickt. Ersetzen Sie die HttpPost Edit Methode durch den folgenden Code, der eine neue Methode aufruft, die die Courses Navigationseigenschaft der Instructor Entität aktualisiert. Die Änderungen werden hervorgehoben.

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

Da die Ansicht nicht über eine Sammlung von Course Entitäten verfügt, kann der Modellordner die Courses Navigationseigenschaft nicht automatisch aktualisieren. Anstatt den Modellordner zum Aktualisieren der Kursnavigationseigenschaft zu verwenden, führen Sie dies in der neuen UpdateInstructorCourses Methode aus. Aus diesem Grund müssen Sie die Courses-Eigenschaft von der Modellbindung ausschließen. Dies erfordert keine Änderung des Codes, der TryUpdateModel aufruft, da Sie die Safelisting-Überladung verwenden und Courses sich nicht in der Includeliste befinden.

Wenn keine Kontrollkästchen aktiviert wurden, initialisiert der Code UpdateInstructorCourses die Courses Navigationseigenschaft mit einer leeren Auflistung:

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

Der Code führt dann eine Schleife durch alle Kurse in der Datenbank aus und überprüft jeden Kurs im Hinblick auf die Kurse, die zu diesem Zeitpunkt dem Dozenten zugewiesen sind, und denen, die in der Ansicht aktiviert wurden. Die beiden letzten Auflistungen werden in HashSet-Objekten gespeichert, um Suchvorgänge effizienter zu gestalten.

Wenn das Kontrollkästchen für einen Kurs aktiviert ist, dieser Kurs jedoch nicht in der Instructor.Courses-Navigationseigenschaft vorhanden ist, wird dieser der Auflistung in der Navigationseigenschaft hinzugefügt.

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

Wenn das Kontrollkästchen für einen Kurs aktiviert ist, dieser Kurs jedoch nicht in der Instructor.Courses-Navigationseigenschaft vorhanden ist, wird dieser aus der Auflistung in der Navigationseigenschaft gelöscht.

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

Fügen Sie in Views\Instructor\Edit.cshtml ein Feld "Kurse " mit einem Kontrollkästchen hinzu, indem Sie den folgenden hervorgehobenen Code unmittelbar nach den div Elementen für das OfficeAssignment Feld hinzufügen:

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

Dieser Code erstellt eine HTML-Tabelle mit drei Spalten. Jede Spalte enthält ein Kontrollkästchen gefolgt von einem Titel, der aus der Kursnummer und dem Kurstitel besteht. Die Kontrollkästchen haben alle denselben Namen ("selectedCourses"), wodurch der Modellordner informiert wird, dass sie als Gruppe behandelt werden sollen. Das value Attribut jedes Kontrollkästchens wird auf den Wert der CourseID. Seite festgelegt, wenn die Seite gepostet wird, übergibt der Modellordner ein Array an den Controller, der nur aus den CourseID Werten für die ausgewählten Kontrollkästchen besteht.

Wenn die Kontrollkästchen anfänglich gerendert werden, verfügen checked diese für Kurse, die dem Kursleiter zugewiesen sind, über Attribute, die sie auswählen (zeigt sie an).

Nachdem Sie Kursaufgaben geändert haben, sollten Sie die Änderungen überprüfen können, wenn die Website zur Index Seite zurückkehrt. Daher müssen Sie der Tabelle auf dieser Seite eine Spalte hinzufügen. In diesem Fall müssen Sie das ViewBag Objekt nicht verwenden, da sich die anzuzeigenden Informationen bereits in der Courses Navigationseigenschaft der Instructor Entität befinden, die Sie als Modell an die Seite übergeben.

Fügen Sie in Views\Instructor\Index.cshtml eine Kursüberschrift unmittelbar nach der Office-Überschrift hinzu, wie im folgenden Beispiel gezeigt:

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

Fügen Sie dann unmittelbar nach der Detailzelle des Bürostandorts eine neue Detailzelle hinzu:

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

Führen Sie die Seite "Kursleiterindex " aus, um die Kurse anzuzeigen, die den einzelnen Kursleitern zugewiesen sind:

Instructor_index_page

Klicken Sie auf "Bearbeiten" eines Kursleiters, um die Seite "Bearbeiten" anzuzeigen.

Instructor_edit_page_with_courses

Ändern Sie einige Kursaufgaben, und klicken Sie auf " Speichern". Die Änderungen werden auf der Indexseite angezeigt.

Hinweis: Der Ansatz zum Bearbeiten von Kursleiterdaten funktioniert gut, wenn eine begrenzte Anzahl von Kursen vorhanden ist. Bei umfangreicheren Auflistungen wären eine andere Benutzeroberfläche und eine andere Aktualisierungsmethode erforderlich.

Aktualisieren der Delete-Methode

Ändern Sie den Code in der HttpPost Delete-Methode so, dass der Office-Aufgabendatensatz (falls vorhanden) gelöscht wird, wenn der Kursleiter gelöscht wird:

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

Wenn Sie versuchen, einen Kursleiter zu löschen, der einer Abteilung als Administrator zugewiesen ist, erhalten Sie einen Fehler bei der referenziellen Integrität. In der aktuellen Version dieses Lernprogramms finden Sie zusätzlichen Code, der den Kursleiter automatisch aus jeder Abteilung entfernt, in der der Kursleiter als Administrator zugewiesen ist.

Zusammenfassung

Sie haben nun diese Einführung zum Arbeiten mit verwandten Daten abgeschlossen. Bisher haben Sie in diesen Lernprogrammen eine ganze Reihe von CRUD-Vorgängen durchgeführt, aber Sie haben sich nicht mit Parallelitätsproblemen befasst. Im nächsten Lernprogramm wird das Thema Parallelität vorgestellt, Optionen für die Behandlung erläutert und die Parallelitätsbehandlung zum CRUD-Code hinzugefügt, den Sie bereits für einen Entitätstyp geschrieben haben.

Links zu anderen Entity Framework-Ressourcen finden Sie am Ende des letzten Lernprogramms in dieser Reihe.