Lernprogramm: Aktualisieren verwandter Daten mit EF in einer ASP.NET MVC-App
Im vorherigen Lernprogramm haben Sie verwandte Daten angezeigt. In diesem Lernprogramm aktualisieren Sie verwandte Daten. Bei den meisten Beziehungen kann dies durch Aktualisieren von Fremdschlüsselfeldern oder Navigationseigenschaften erfolgen. Bei m:n-Beziehungen macht Entity Framework die Verknüpfungstabelle nicht direkt verfügbar, sodass Sie Entitäten zu und aus den entsprechenden Navigationseigenschaften hinzufügen und daraus entfernen.
In den folgenden Abbildungen werden die Seiten dargestellt, mit denen Sie arbeiten werden.
In diesem Tutorial:
- Anpassen von Kursseiten
- Hinzufügen von Office zur Kursleiterseite
- Hinzufügen von Kursen zur Kursleiterseite
- DeleteConfirmed aktualisieren
- Hinzufügen von einem Bürostandort und von Kursen zu der Seite „Erstellen“
Voraussetzungen
Anpassen von Kursseiten
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 Create
MethodenEdit
, 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 (RetryLimitExceededException /* dex */)
{
//Log the error (uncomment dex variable name 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)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Course course = db.Courses.Find(id);
if (course == null)
{
return HttpNotFound();
}
PopulateDepartmentsDropDownList(course.DepartmentID);
return View(course);
}
[HttpPost, ActionName("Edit")]
[ValidateAntiForgeryToken]
public ActionResult EditPost(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
var courseToUpdate = db.Courses.Find(id);
if (TryUpdateModel(courseToUpdate, "",
new string[] { "Title", "Credits", "DepartmentID" }))
{
try
{
db.SaveChanges();
return RedirectToAction("Index");
}
catch (RetryLimitExceededException /* dex */)
{
//Log the error (uncomment dex variable name 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(courseToUpdate.DepartmentID);
return View(courseToUpdate);
}
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);
}
Fügen Sie die folgende using
Anweisung am Anfang der Datei hinzu:
using System.Data.Entity.Infrastructure;
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 das DropDownList-Hilfsprogramm , und der Hilfsprogramm weiß dann, in dem ViewBag
Objekt nach einem SelectList
benannten DepartmentID
Objekt 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)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Course course = db.Courses.Find(id);
if (course == null)
{
return HttpNotFound();
}
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 (RetryLimitExceededException /* dex */)
{
//Log the error (uncomment dex variable name 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.
Die Kursansichten sind bereits mit Dropdownlisten für das Abteilungsfeld gerüstet, aber Sie möchten die Abteilungs-ID-Beschriftung für dieses Feld nicht. Nehmen Sie daher die folgende hervorgehobene Änderung an der Datei "Views\Course\Create.cshtml " vor, um die Beschriftung zu ändern.
@model ContosoUniversity.Models.Course
@{
ViewBag.Title = "Create";
}
<h2>Create</h2>
@using (Html.BeginForm())
{
@Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Course</h4>
<hr />
@Html.ValidationSummary(true)
<div class="form-group">
@Html.LabelFor(model => model.CourseID, new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.CourseID)
@Html.ValidationMessageFor(model => model.CourseID)
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Title, new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Title)
@Html.ValidationMessageFor(model => model.Title)
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Credits, new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Credits)
@Html.ValidationMessageFor(model => model.Credits)
</div>
</div>
<div class="form-group">
<label class="control-label col-md-2" for="DepartmentID">Department</label>
<div class="col-md-10">
@Html.DropDownList("DepartmentID", String.Empty)
@Html.ValidationMessageFor(model => model.DepartmentID)
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
@Html.ActionLink("Back to List", "Index")
</div>
@section Scripts {
@Scripts.Render("~/bundles/jqueryval")
}
Nehmen Sie dieselbe Änderung in "Views\Course\Edit.cshtml" vor.
Normalerweise erstellt das Gerüst kein Gerüst für einen Primärschlüssel, da der Schlüsselwert von der Datenbank generiert wird und nicht geändert werden kann und kein sinnvoller Wert ist, der benutzern angezeigt werden kann. Für Kursentitäten enthält das Gerüst ein Textfeld für das CourseID
Feld, da es versteht, dass das DatabaseGeneratedOption.None
Attribut bedeutet, dass der Benutzer den Primärschlüsselwert eingeben kann. Aber es versteht nicht, dass die Zahl sinnvoll ist, die Sie in den anderen Ansichten sehen möchten, daher müssen Sie sie manuell hinzufügen.
Fügen Sie in Views\Course\Edit.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="form-group">
@Html.LabelFor(model => model.CourseID, new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.DisplayFor(model => model.CourseID)
</div>
</div>
Es gibt bereits ein ausgeblendetes Feld (Html.HiddenFor
Hilfsfeld) für die Kursnummer in der Bearbeitungsansicht. Durch das Hinzufügen eines Html.LabelFor-Hilfsfelds wird die Notwendigkeit des ausgeblendeten Felds nicht beseitigt, da die Kursnummer nicht in die geposteten Daten aufgenommen wird, wenn der Benutzer auf der Seite "Bearbeiten" auf "Speichern" klickt.
Ändern Sie in Views\Course\Delete.cshtml und Views\Course\Details.cshtml die Bezeichnung des Abteilungsnamens von "Name" in "Abteilung", und fügen Sie vor dem Feld "Titel" ein Kursnummernfeld hinzu.
<dt>
Department
</dt>
<dd>
@Html.DisplayFor(model => model.Department.Name)
</dd>
<dt>
@Html.DisplayNameFor(model => model.CourseID)
</dt>
<dd>
@Html.DisplayFor(model => model.CourseID)
</dd>
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:
Wert | Einstellung |
---|---|
Anzahl | Geben Sie 1000 ein. |
Titel | Geben Sie Algebra ein. |
Gutschriften | Geben Sie 4 ein. |
Department | Wählen Sie "Mathematik" aus. |
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.
Führen Sie die Seite "Bearbeiten" aus (zeigen Sie die Seite "Kursindex" an, und klicken Sie auf "Auf einem Kurs bearbeiten ").
Ändern Sie die Daten auf der Seite, und klicken Sie auf Speichern. Die Seite "Kursindex" wird mit den aktualisierten Kursdaten angezeigt.
Hinzufügen von Office zur Kursleiterseite
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:
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Instructor instructor = db.Instructors.Find(id);
if (instructor == null)
{
return HttpNotFound();
}
ViewBag.ID = new SelectList(db.OfficeAssignments, "InstructorID", "Location", instructor.ID);
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)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Instructor instructor = db.Instructors
.Include(i => i.OfficeAssignment)
.Where(i => i.ID == id)
.Single();
if (instructor == null)
{
return HttpNotFound();
}
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, ActionName("Edit")]
[ValidateAntiForgeryToken]
public ActionResult EditPost(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
var instructorToUpdate = db.Instructors
.Include(i => i.OfficeAssignment)
.Where(i => i.ID == id)
.Single();
if (TryUpdateModel(instructorToUpdate, "",
new string[] { "LastName", "FirstMidName", "HireDate", "OfficeAssignment" }))
{
try
{
if (String.IsNullOrWhiteSpace(instructorToUpdate.OfficeAssignment.Location))
{
instructorToUpdate.OfficeAssignment = null;
}
db.SaveChanges();
return RedirectToAction("Index");
}
catch (RetryLimitExceededException /* dex */)
{
//Log the error (uncomment dex variable name 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.");
}
}
return View(instructorToUpdate);
}
Der Verweis erfordert RetryLimitExceededException
eine using
Anweisung; um sie hinzuzufügen – zeigen Sie mit dem Mauszeiger.RetryLimitExceededException
Die folgende Meldung wird angezeigt:
Wählen Sie " Potenzielle Fixes anzeigen" und dann "System.Data.Entity.Infrastructure" aus.
Der Code führt Folgendes aus:
Ändert den Methodennamen so
EditPost
, dass die Signatur jetzt mit derHttpGet
Methode identisch ist (dasActionName
Attribut gibt an, dass die /Edit/URL weiterhin verwendet wird).Ruft die aktuelle Entität
Instructor
von der Datenbank über Eager Loading für die NavigationseigenschaftOfficeAssignment
ab. Dies entspricht dem, was Sie in derHttpGet
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 auflisten, die Sie einschließen möchten. 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 derOfficeAssignment
-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="form-group">
@Html.LabelFor(model => model.OfficeAssignment.Location, new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.OfficeAssignment.Location)
@Html.ValidationMessageFor(model => model.OfficeAssignment.Location)
</div>
</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.
Hinzufügen von Kursen zur Kursleiterseite
Dozenten können eine beliebige Anzahl von Kursen unterrichten. Jetzt verbessern Sie die Seite "Kursleiterbearbeitung", indem Sie die Möglichkeit zum Ändern von Kursaufgaben mithilfe einer Gruppe von Kontrollkästchen hinzufügen.
Die Beziehung zwischen und Entitäten Course
Instructor
ist n:n, was bedeutet, dass Sie keinen direkten Zugriff auf die Fremdschlüsseleigenschaften haben, die sich in der Verknüpfungstabelle befinden. Stattdessen fügen Sie Entitäten zu und aus der Instructor.Courses
Navigationseigenschaft hinzu und entfernen diese.
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)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Instructor instructor = db.Instructors
.Include(i => i.OfficeAssignment)
.Include(i => i.Courses)
.Where(i => i.ID == id)
.Single();
if (instructor == null)
{
return HttpNotFound();
}
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 EditPost
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, string[] selectedCourses)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
var instructorToUpdate = db.Instructors
.Include(i => i.OfficeAssignment)
.Include(i => i.Courses)
.Where(i => i.ID == 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.SaveChanges();
return RedirectToAction("Index");
}
catch (RetryLimitExceededException /* dex */)
{
//Log the error (uncomment dex variable name 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);
}
}
}
}
Die Methodensignatur unterscheidet sich jetzt von der HttpGet
Edit
Methode, sodass sich der Methodenname von EditPost
hinten in Edit
.
Da die Ansicht nicht über eine Sammlung von Course
Entitäten verfügt, kann der Modellordner die Courses
Navigationseigenschaft nicht automatisch aktualisieren. Anstatt die Modellmappe zum Aktualisieren der Courses
Navigationseigenschaft 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 explizite Listenüberladung verwenden und Courses
nicht in der Includeliste enthalten sind.
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 Array von Kontrollkästchen hinzu, indem Sie den folgenden Code unmittelbar hinter den div
Elementen für das OfficeAssignment
Feld und vor dem div
Element für die Schaltfläche "Speichern " hinzufügen:
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<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>
</div>
Nachdem Sie den Code eingefügt haben, wenn Zeilenumbrüche und Einzug nicht wie hier aussehen, korrigieren Sie alles manuell, damit es wie hier aussieht. Der Einzug muss nicht perfekt sein, die Zeilen @</tr><tr>
, @:<td>
, @:</td>
und @</tr>
müssen jedoch, wie dargestellt, jeweils in einer einzelnen Zeile stehen. Ansonsten wird ein Laufzeitfehler ausgelöst.
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>Last Name</th>
<th>First Name</th>
<th>Hire Date</th>
<th>Office</th>
<th>Courses</th>
<th></th>
</tr>
Fügen Sie dann unmittelbar nach der Detailzelle des Bürostandorts eine neue Detailzelle hinzu:
<td>
@if (item.OfficeAssignment != null)
{
@item.OfficeAssignment.Location
}
</td>
<td>
@{
foreach (var course in item.Courses)
{
@course.CourseID @: @course.Title <br />
}
}
</td>
<td>
@Html.ActionLink("Select", "Index", new { id = item.ID }) |
@Html.ActionLink("Edit", "Edit", new { id = item.ID }) |
@Html.ActionLink("Details", "Details", new { id = item.ID }) |
@Html.ActionLink("Delete", "Delete", new { id = item.ID })
</td>
Führen Sie die Seite "Kursleiterindex " aus, um die Kurse anzuzeigen, die den einzelnen Kursleitern zugewiesen sind.
Klicken Sie auf "Bearbeiten" eines Kursleiters, um die Seite "Bearbeiten" anzuzeigen.
Ändern Sie einige Kursaufgaben, und klicken Sie auf " Speichern". Die Änderungen werden auf der Indexseite angezeigt.
Hinweis: Der hier gezeigte 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.
DeleteConfirmed aktualisieren
Löschen Sie in InstructorController.cs die DeleteConfirmed
Methode, und fügen Sie den folgenden Code an dessen Stelle ein.
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public ActionResult DeleteConfirmed(int id)
{
Instructor instructor = db.Instructors
.Include(i => i.OfficeAssignment)
.Where(i => i.ID == id)
.Single();
db.Instructors.Remove(instructor);
var department = db.Departments
.Where(d => d.InstructorID == id)
.SingleOrDefault();
if (department != null)
{
department.InstructorID = null;
}
db.SaveChanges();
return RedirectToAction("Index");
}
Dieser Code ändert die folgende Änderung:
- Wenn der Kursleiter als Administrator einer Abteilung zugewiesen ist, wird die Aufgabe des Kursleiters aus dieser Abteilung entfernt. Ohne diesen Code erhalten Sie einen Fehler bei der referenziellen Integrität, wenn Sie versucht haben, einen Kursleiter zu löschen, der als Administrator für eine Abteilung zugewiesen wurde.
Dieser Code behandelt nicht das Szenario eines Kursleiters, der als Administrator für mehrere Abteilungen zugewiesen wurde. Im letzten Lernprogramm fügen Sie Code hinzu, der verhindert, dass dieses Szenario stattfindet.
Hinzufügen von einem Bürostandort und von Kursen zu der Seite „Erstellen“
Löschen Sie in InstructorController.cs die HttpGet
und HttpPost
Create
die Methoden, und fügen Sie dann den folgenden Code an deren Stelle hinzu:
public ActionResult Create()
{
var instructor = new Instructor();
instructor.Courses = new List<Course>();
PopulateAssignedCourseData(instructor);
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "LastName,FirstMidName,HireDate,OfficeAssignment" )]Instructor instructor, string[] selectedCourses)
{
if (selectedCourses != null)
{
instructor.Courses = new List<Course>();
foreach (var course in selectedCourses)
{
var courseToAdd = db.Courses.Find(int.Parse(course));
instructor.Courses.Add(courseToAdd);
}
}
if (ModelState.IsValid)
{
db.Instructors.Add(instructor);
db.SaveChanges();
return RedirectToAction("Index");
}
PopulateAssignedCourseData(instructor);
return View(instructor);
}
Dieser Code ähnelt dem, was Sie für die Edit-Methoden gesehen haben, mit der Ausnahme, dass anfänglich keine Kurse ausgewählt sind. Die HttpGet
Create
Methode ruft die PopulateAssignedCourseData
Methode nicht auf, da möglicherweise Kurse ausgewählt sind, aber um eine leere Auflistung für die Schleife in der foreach
Ansicht bereitzustellen (andernfalls löst der Ansichtscode eine NULL-Verweis-Ausnahme aus).
Die HttpPost Create-Methode fügt jeden ausgewählten Kurs der Kursnavigationseigenschaft vor dem Vorlagencode hinzu, der auf Überprüfungsfehler überprüft und den neuen Kursleiter der Datenbank hinzufügt. Kurse werden auch dann hinzugefügt, wenn Modellfehler vorhanden sind, sodass beim Auftreten von Modellfehlern (z. B. dem Benutzerschlüssel ein ungültiges Datum) angezeigt wird, sodass beim erneuten Anzeigen der Seite mit einer Fehlermeldung alle vorgenommenen Kursauswahlen automatisch wiederhergestellt werden.
Beachten Sie, dass Sie die Courses
-Navigationseigenschaft als leere Auflistung initialisieren müssen, wenn Sie dieser Kurse hinzufügen möchten:
instructor.Courses = new List<Course>();
Wenn Sie dies nicht im Controllercode durchführen möchten, können Sie dies auch im Dozentenmodell tun, indem Sie den Eigenschaftengetter ändern, um falls nötig automatisch die Auflistung zu erstellen. Dies wird im folgenden Code dargestellt:
private ICollection<Course> _courses;
public virtual ICollection<Course> Courses
{
get
{
return _courses ?? (_courses = new List<Course>());
}
set
{
_courses = value;
}
}
Wenn Sie die Courses
-Eigenschaft auf diese Weise ändern, können Sie den expliziten Code zum Initialisieren der Eigenschaft aus dem Controller entfernen.
Fügen Sie in Views\Instructor\Create.cshtml ein Textfeld für Bürostandort und Kurskontrollkästchen nach dem Einstellungsdatum und vor der Schaltfläche "Absenden " hinzu.
<div class="form-group">
@Html.LabelFor(model => model.OfficeAssignment.Location, new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.OfficeAssignment.Location)
@Html.ValidationMessageFor(model => model.OfficeAssignment.Location)
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<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>
</div>
Nachdem Sie den Code eingefügt haben, korrigieren Sie Zeilenumbrüche und Einzug wie zuvor für die Seite "Bearbeiten".
Führen Sie die Seite "Erstellen" aus, und fügen Sie einen Kursleiter hinzu.
Behandeln von Transaktionen
Wie im Lernprogramm zur grundlegenden CRUD-Funktionalität erläutert, implementiert entity Framework standardmäßig implizit Transaktionen. Für Szenarien, in denen Sie mehr Kontrolle benötigen – z. B. wenn Sie Vorgänge außerhalb von Entity Framework in eine Transaktion einbeziehen möchten – finden Sie unter Arbeiten mit Transaktionen auf MSDN.
Abrufen des Codes
Herunterladen des abgeschlossenen Projekts
Zusätzliche Ressourcen
Links zu anderen Entity Framework-Ressourcen finden Sie in ASP.NET Datenzugriff – Empfohlene Ressourcen.
Nächster Schritt
In diesem Tutorial:
- Angepasste Kursseiten
- Office zur Seite "Kursleiter" hinzugefügt
- Kursen zur Seite "Kursleiter" hinzugefügt
- DeleteConfirmed aktualisiert
- Office-Standort und -Kurse zur Seite "Erstellen" hinzugefügt
Wechseln Sie zum nächsten Artikel, um zu erfahren, wie Sie ein asynchrones Programmiermodell implementieren.