Condividi tramite


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.

Screenshot che mostra la pagina Crea corso.

Screenshot che mostra la pagina Modifica insegnante.

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:

Course_create_page

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.

Course_Index_page_showing_new_course

Eseguire la pagina Modifica (visualizzare la pagina Indice corso e fare clic su Modifica in un corso).

Course_edit_page

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 navigazione OfficeAssignment. Questo è lo stesso di quello che hai fatto nel HttpGet 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 nella OfficeAssignment 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).

Changing_the_office_location

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:

Screenshot che mostra la pagina Instructor Edit con i corsi.

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:

Instructor_index_page

Fare clic su Modifica su un insegnante per visualizzare la pagina Modifica.

Instructor_edit_page_with_courses

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.