Aggiornamento di dati correlati con Entity Framework in un'applicazione MVC ASP.NET (6 di 10)
di Tom Dykstra
L'applicazione Web di esempio Contoso University illustra come creare ASP.NET applicazioni MVC 4 usando Entity Framework 5 Code First e Visual Studio 2012. Per informazioni sulla serie di esercitazioni, vedere la prima esercitazione della serie.
Nota
Se si verifica un problema che non è possibile risolvere, scaricare il capitolo completato e provare a riprodurre il problema. In genere è possibile trovare la soluzione al problema confrontando il codice con il codice completato. Per alcuni errori comuni e come risolverli, vedere Errori e soluzioni alternative.
Nell'esercitazione precedente sono stati visualizzati i dati correlati; in questa esercitazione verranno aggiornati i dati correlati. Per la maggior parte delle relazioni, questa operazione può essere eseguita aggiornando i campi di chiave esterna appropriati. Per le relazioni molti-a-molti, Entity Framework non espone direttamente la tabella join, pertanto è necessario aggiungere e rimuovere in modo esplicito le entità da e verso le proprietà di navigazione appropriate.
Le figure seguenti illustrano le pagine che verranno usate.
Personalizzare le pagine di creazione e di modifica per i corsi
Quando viene creata, una nuova entità corso deve essere in relazione con un dipartimento esistente. Per semplificare il raggiungimento di questo obiettivo, il codice con scaffolding include i metodi del controller e le visualizzazioni di creazione e modifica includono un elenco a discesa per la selezione del dipartimento. L'elenco a discesa imposta la Course.DepartmentID
proprietà di chiave esterna ed è tutto ciò che è necessario per caricare la Department
proprietà di navigazione con l'entità appropriata Department
. Verrà usato il codice con scaffolding, che però verrà modificato leggermente per aggiungere la gestione degli errori e l'ordinamento dell'elenco a discesa.
In CourseController.cs eliminare i quattro Edit
metodi e Create
e sostituirli con il codice seguente:
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);
}
Il PopulateDepartmentsDropDownList
metodo ottiene un elenco di tutti i reparti ordinati in base al nome, crea una SelectList
raccolta per un elenco a discesa e passa la raccolta alla visualizzazione in una ViewBag
proprietà. Il metodo accetta il parametro facoltativo selectedDepartment
, che consente al codice chiamante di specificare l'elemento che deve essere selezionato quando viene eseguito il rendering dell'elenco a discesa. La vista passerà il nome DepartmentID
all'helperDropDownList
e l'helper saprà quindi cercare nell'oggetto ViewBag
un SelectList
oggetto denominato DepartmentID
.
Il HttpGet
Create
metodo chiama il PopulateDepartmentsDropDownList
metodo senza impostare l'elemento selezionato, perché per un nuovo corso il reparto non è ancora stato stabilito:
public ActionResult Create()
{
PopulateDepartmentsDropDownList();
return View();
}
Il HttpGet
Edit
metodo imposta l'elemento selezionato, in base all'ID del reparto già assegnato al corso da modificare:
public ActionResult Edit(int id)
{
Course course = db.Courses.Find(id);
PopulateDepartmentsDropDownList(course.DepartmentID);
return View(course);
}
I HttpPost
metodi per entrambi Create
e Edit
includono anche il codice che imposta l'elemento selezionato quando redisplayno la pagina dopo un errore:
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);
Questo codice garantisce che quando la pagina viene rieseguita per visualizzare il messaggio di errore, il reparto selezionato rimane selezionato.
In Views\Course\Create.cshtml aggiungere il codice evidenziato per creare un nuovo campo numero corso prima del campo Titolo . Come illustrato in un'esercitazione precedente, i campi chiave primaria non vengono scaffolding per impostazione predefinita, ma questa chiave primaria è significativa, quindi si vuole che l'utente sia in grado di immettere il valore della chiave.
@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")
}
In Views\Course\Edit.cshtml, Views\Course\Delete.cshtml e Views\Course\Details.cshtml aggiungere un campo numero di corso prima del campo Titolo . Poiché è la chiave primaria, viene visualizzata, ma non può essere modificata.
<div class="editor-label">
@Html.LabelFor(model => model.CourseID)
</div>
<div class="editor-field">
@Html.DisplayFor(model => model.CourseID)
</div>
Eseguire la pagina Crea (visualizzare la pagina Indice corso e fare clic su Crea nuovo) e immettere i dati per un nuovo corso:
Cliccare su Crea. La pagina Indice corso viene visualizzata con il nuovo corso aggiunto all'elenco. Il nome del dipartimento nell'elenco della pagina di indice deriva dalla proprietà di navigazione, che mostra che la relazione è stata stabilita correttamente.
Eseguire la pagina Modifica (visualizzare la pagina Indice corso e fare clic su Modifica in un corso).
Modificare i dati nella pagina e fare clic su Save (Salva). La pagina Indice corso viene visualizzata con i dati aggiornati del corso.
Aggiunta di una pagina di modifica per insegnanti
Quando si modifica il record di un insegnante, è necessario essere in grado di aggiornare l'assegnazione dell'ufficio. L'entità Instructor
ha una relazione uno-a-zero-o-uno con l'entità OfficeAssignment
, il che significa che è necessario gestire le situazioni seguenti:
- Se l'utente cancella l'assegnazione dell'ufficio e originariamente ha un valore, è necessario rimuovere ed eliminare l'entità
OfficeAssignment
. - Se l'utente immette un valore di assegnazione di office e originariamente era vuoto, è necessario creare una nuova
OfficeAssignment
entità. - Se l'utente modifica il valore di un'assegnazione di ufficio, è necessario modificare il valore in un'entità esistente
OfficeAssignment
.
Aprire InstructorController.cs ed esaminare il HttpGet
Edit
metodo :
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);
}
Il codice con scaffolding non è quello desiderato. Si tratta di configurare i dati per un elenco a discesa, ma è necessaria una casella di testo. Sostituire questo metodo con il codice seguente:
public ActionResult Edit(int id)
{
Instructor instructor = db.Instructors
.Include(i => i.OfficeAssignment)
.Where(i => i.InstructorID == id)
.Single();
return View(instructor);
}
Questo codice elimina l'istruzione ViewBag
e aggiunge il caricamento eager per l'entità associata OfficeAssignment
. Non è possibile eseguire il caricamento eager con il Find
metodo , quindi i Where
metodi e Single
vengono usati per selezionare l'insegnante.
Sostituire il HttpPost
Edit
metodo con il codice seguente. che gestisce gli aggiornamenti delle assegnazioni di Office:
[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);
}
Il codice esegue le seguenti attività:
Ottiene l'entità
Instructor
corrente dal database tramite il caricamento eager per la proprietà di navigazioneOfficeAssignment
. Questo è lo stesso di quello che hai fatto nelHttpGet
Edit
metodo .Aggiorna l'entità
Instructor
recuperata con valori dallo strumento di associazione di modelli. L'overload TryUpdateModel usato consente di inserire nell'elenco sicuro le proprietà da includere. In questo modo è possibile evitare l'overposting, come illustrato nella seconda esercitazione.if (TryUpdateModel(instructorToUpdate, "", new string[] { "LastName", "FirstMidName", "HireDate", "OfficeAssignment" }))
Se la posizione dell'ufficio è vuota, imposta la
Instructor.OfficeAssignment
proprietà su Null in modo che la riga correlata nellaOfficeAssignment
tabella venga eliminata.if (String.IsNullOrWhiteSpace(instructorToUpdate.OfficeAssignment.Location)) { instructorToUpdate.OfficeAssignment = null; }
Salva le modifiche nel database.
In Views\Instructor\Edit.cshtml, dopo gli elementi per il div
campo Hire Date (Data di assunzione), aggiungere un nuovo campo per modificare la posizione dell'ufficio:
<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>
Eseguire la pagina (selezionare la scheda Instructors e quindi fare clic su Modifica su un insegnante). Modificare Office Location (Posizione ufficio) e fare clic su Save (Salva).
Aggiunta di assegnazioni di corso alla pagina Modifica insegnante
Gli insegnanti possono tenere un numero qualsiasi di corsi. A questo punto la pagina di modifica dell'insegnante verrà migliorata aggiungendo la possibilità di modificare le assegnazioni di corso tramite un gruppo di caselle di controllo, come illustrato nello screenshot seguente:
La relazione tra le Course
entità e Instructor
è molti-a-molti, il che significa che non si ha accesso diretto alla tabella di join. Verranno invece aggiunte e rimosse entità da e verso la Instructor.Courses
proprietà di navigazione.
L'interfaccia utente che consente di modificare i corsi assegnati a un insegnante è costituita da un gruppo di caselle di controllo. È visualizzata una casella di controllo per ogni corso nel database e le caselle corrispondenti ai corsi attualmente assegnati all'insegnante sono selezionate. L'utente può selezionare e deselezionare le caselle di controllo per modificare le assegnazioni dei corsi. Se il numero di corsi fosse molto maggiore, è probabile che si voglia usare un metodo diverso per presentare i dati nella visualizzazione, ma si userebbe lo stesso metodo per modificare le proprietà di navigazione per creare o eliminare relazioni.
Per fornire i dati alla visualizzazione dell'elenco di caselle di controllo, si userà una classe modello di visualizzazione. Creare AssignedCourseData.cs nella cartella ViewModels e sostituire il codice esistente con il codice seguente:
namespace ContosoUniversity.ViewModels
{
public class AssignedCourseData
{
public int CourseID { get; set; }
public string Title { get; set; }
public bool Assigned { get; set; }
}
}
In InstructorController.cs sostituire il HttpGet
Edit
metodo con il codice seguente. Le modifiche sono evidenziate.
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;
}
Il codice aggiunge il caricamento eager per la proprietà di navigazione Courses
e chiama il nuovo metodo PopulateAssignedCourseData
per fornire informazioni per la matrice di caselle di controllo tramite la classe modello di visualizzazione AssignedCourseData
.
Il codice nel PopulateAssignedCourseData
metodo legge tutte le Course
entità per caricare un elenco di corsi usando la classe del modello di visualizzazione. Per ogni corso, il codice verifica se è presente nella proprietà di navigazione Courses
dell'insegnante. Per creare una ricerca efficiente quando si verifica se un corso viene assegnato all'insegnante, i corsi assegnati all'insegnante vengono inseriti in una raccolta HashSet . La Assigned
proprietà è impostata su true
per i corsi a cui viene assegnato l'insegnante. La visualizzazione usa questa proprietà per determinare quali caselle di controllo devono essere visualizzate come selezionate. Infine, l'elenco viene passato alla visualizzazione in una ViewBag
proprietà .
Aggiungere quindi il codice che viene eseguito quando l'utente fa clic su Save (Salva). Sostituire il HttpPost
Edit
metodo con il codice seguente, che chiama un nuovo metodo che aggiorna la Courses
proprietà di navigazione dell'entità Instructor
. Le modifiche sono evidenziate.
[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);
}
}
}
}
Poiché la vista non dispone di una raccolta di Course
entità, lo strumento di associazione di modelli non può aggiornare automaticamente la Courses
proprietà di navigazione. Anziché usare lo strumento di associazione di modelli per aggiornare la proprietà di navigazione Courses, questa operazione verrà eseguita nel nuovo UpdateInstructorCourses
metodo. È pertanto necessario escludere la proprietà Courses
dall'associazione di modelli. Ciò non richiede alcuna modifica al codice che chiama TryUpdateModel perché si usa l'overload safelisting e Courses
non è incluso nell'elenco di inclusioni.
Se non sono state selezionate caselle di controllo, il codice in UpdateInstructorCourses
inizializza la Courses
proprietà di navigazione con un insieme vuoto:
if (selectedCourses == null)
{
instructorToUpdate.Courses = new List<Course>();
return;
}
Il codice quindi esegue il ciclo di tutti i corsi nel database e controlla ogni corso a fronte di quelli assegnati all'insegnante rispetto a quelli selezionati nella visualizzazione. Per facilitare l'esecuzione di ricerche efficienti, le ultime due raccolte sono archiviate all'interno di oggetti HashSet
.
Se la casella di controllo di un corso è selezionata ma il corso non è presente nella proprietà di navigazione Instructor.Courses
, il corso viene aggiunto alla raccolta nella proprietà di navigazione.
if (selectedCoursesHS.Contains(course.CourseID.ToString()))
{
if (!instructorCourses.Contains(course.CourseID))
{
instructorToUpdate.Courses.Add(course);
}
}
Se la casella di controllo di un corso non è selezionata ma il corso è presente nella proprietà di navigazione Instructor.Courses
, il corso viene rimosso dalla proprietà di navigazione.
else
{
if (instructorCourses.Contains(course.CourseID))
{
instructorToUpdate.Courses.Remove(course);
}
}
In Views\Instructor\Edit.cshtml aggiungere un campo Courses con una matrice di caselle di controllo aggiungendo il codice evidenziato seguente immediatamente dopo gli div
elementi per il OfficeAssignment
campo:
@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")
}
Questo codice crea una tabella HTML con tre colonne. In ogni colonna è presente una casella di controllo seguita da una didascalia costituita dal numero di corso e dal titolo. Tutte le caselle di controllo hanno lo stesso nome ("selectedCourses"), che informa il gestore di associazione di modelli che devono essere considerate come un gruppo. L'attributo value
di ogni casella di controllo è impostato sul valore di CourseID.
Quando viene pubblicata la pagina, il gestore di associazione di modelli passa una matrice al controller costituito dai CourseID
valori solo per le caselle di controllo selezionate.
Quando viene inizialmente eseguito il rendering delle caselle di controllo, quelle destinate ai corsi assegnati all'insegnante hanno checked
attributi, che le seleziona (le visualizza selezionate).
Dopo aver modificato le assegnazioni dei corsi, sarà possibile verificare le modifiche quando il sito torna alla Index
pagina. Pertanto, è necessario aggiungere una colonna alla tabella in tale pagina. In questo caso non è necessario usare l'oggetto ViewBag
, perché le informazioni da visualizzare sono già presenti nella Courses
proprietà di navigazione dell'entità Instructor
passata alla pagina come modello.
In Views\Instructor\Index.cshtml aggiungere un'intestazione Courses immediatamente dopo l'intestazione di Office , come illustrato nell'esempio seguente:
<tr>
<th></th>
<th>Last Name</th>
<th>First Name</th>
<th>Hire Date</th>
<th>Office</th>
<th>Courses</th>
</tr>
Aggiungere quindi una nuova cella di dettaglio immediatamente dopo la cella dei dettagli della posizione dell'ufficio:
@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>
}
Eseguire la pagina Instructor Index per visualizzare i corsi assegnati a ogni insegnante:
Fare clic su Modifica su un insegnante per visualizzare la pagina Modifica.
Modificare alcune assegnazioni di corso e fare clic su Salva. Le modifiche effettuate si riflettono nella pagina di indice.
Nota: l'approccio adottato per modificare i dati del corso dell'insegnante funziona bene quando è presente un numero limitato di corsi. Per raccolte molto più grandi, sarebbero necessari un'interfaccia utente diversa e un altro metodo di aggiornamento.
Aggiornare il metodo Delete
Modificare il codice nel metodo HttpPost Delete in modo che il record di assegnazione dell'ufficio (se presente) venga eliminato quando l'insegnante viene eliminato:
[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");
}
Se si tenta di eliminare un insegnante assegnato a un reparto come amministratore, verrà visualizzato un errore di integrità referenziale. Vedere la versione corrente di questa esercitazione per il codice aggiuntivo che rimuoverà automaticamente l'insegnante da qualsiasi reparto in cui l'insegnante viene assegnato come amministratore.
Riepilogo
Questa introduzione è stata completata per l'uso dei dati correlati. Finora in queste esercitazioni è stata eseguita una gamma completa di operazioni CRUD, ma non sono stati affrontati problemi di concorrenza. L'esercitazione successiva presenta l'argomento della concorrenza, illustra le opzioni per gestirlo e aggiunge la gestione della concorrenza al codice CRUD già scritto per un tipo di entità.
I collegamenti ad altre risorse di Entity Framework sono disponibili alla fine dell'ultima esercitazione di questa serie.