Aktualizace souvisejících dat pomocí Entity Frameworku v aplikaci ASP.NET MVC (6 z 10)

Tom Dykstra

Ukázková webová aplikace Contoso University ukazuje, jak vytvářet aplikace ASP.NET MVC 4 pomocí entity Framework 5 Code First a sady Visual Studio 2012. Informace o sérii kurzů najdete v prvním kurzu v této sérii.

Poznámka

Pokud narazíte na problém, který nemůžete vyřešit, stáhněte si dokončenou kapitolu a zkuste problém reprodukovat. Obecně můžete najít řešení problému porovnáním kódu s dokončeným kódem. Informace o některých běžných chybách a jejich řešení najdete v tématu Chyby a alternativní řešení.

V předchozím kurzu jste zobrazili související data. v tomto kurzu aktualizujete související data. U většiny relací je to možné provést aktualizací příslušných polí cizího klíče. U relací M:N Entity Framework nezpřístupňuje přímo tabulku spojení, takže musíte explicitně přidávat a odebírat entity do a z příslušných navigačních vlastností.

Následující ilustrace znázorňují stránky, se kterými budete pracovat.

Snímek obrazovky se stránkou Vytvořit kurz

Snímek obrazovky se stránkou Pro úpravy instruktora

Přizpůsobení vytváření a úprav stránek pro kurzy

Když se vytvoří nová entita kurzu, musí mít vztah k existujícímu oddělení. Aby se to usnadnilo, vygenerovaný kód obsahuje metody kontroleru a zobrazení pro vytváření a úpravy, které obsahují rozevírací seznam pro výběr oddělení. V rozevíracím seznamu se nastaví vlastnost cizího Course.DepartmentID klíče, což je vše, co Entity Framework potřebuje k načtení Department navigační vlastnosti s příslušnou Department entitou. Použijete vygenerovaný kód, ale mírně ho změníte, abyste přidali zpracování chyb a rozevírací seznam seřadili.

V souboru CourseController.cs odstraňte čtyři Edit metody a a Create nahraďte je následujícím kódem:

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

Metoda PopulateDepartmentsDropDownList získá seznam všech oddělení seřazených podle názvu, vytvoří SelectList kolekci pro rozevírací seznam a předá kolekci do zobrazení ve ViewBag vlastnosti . Metoda přijímá volitelný selectedDepartment parametr, který volajícímu kódu umožňuje určit položku, která bude vybrána při vykreslení rozevíracího seznamu. Zobrazení předá tento název DepartmentIDpomocníkovi DropDownLista pomocník pak ví, že v objektu ViewBag vyhledá pojmenovaný SelectListDepartmentIDobjekt .

Metoda HttpGetCreate volá metodu PopulateDepartmentsDropDownList bez nastavení vybrané položky, protože pro nový kurz ještě není zřízeno oddělení:

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

Metoda HttpGetEdit nastaví vybranou položku na základě ID oddělení, které je již přiřazeno k upravovanýmu kurzu:

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

Metody HttpPost a CreateEdit také zahrnují kód, který nastaví vybranou položku při opětovném zobrazení stránky po chybě:

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

Tento kód zajistí, že při opětovném zobrazení stránky, aby se zobrazila chybová zpráva, zůstane vybrané libovolné oddělení.

Do pole Views\Course\Create.cshtml přidejte zvýrazněný kód, který vytvoří nové pole s číslem kurzu před pole Název . Jak bylo vysvětleno v předchozím kurzu, pole primárního klíče se ve výchozím nastavení nevygenerují, ale tento primární klíč má smysl, takže chcete, aby uživatel mohl zadat hodnotu klíče.

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

V zobrazeních Views\Course\Edit.cshtml, Views\Course\Delete.cshtml a Views\Course\Details.cshtml přidejte před pole Název pole číslo kurzu. Protože se jedná o primární klíč, zobrazí se, ale nedá se změnit.

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

Spusťte stránku Vytvořit (zobrazte stránku Rejstřík kurzu a klikněte na Vytvořit nový) a zadejte data pro nový kurz:

Course_create_page

Klikněte na Vytvořit. Zobrazí se stránka Rejstřík kurzu s novým kurzem přidaným do seznamu. Název oddělení v seznamu indexových stránek pochází z navigační vlastnosti, která ukazuje, že relace byla správně vytvořena.

Course_Index_page_showing_new_course

Spusťte stránku Upravit (zobrazte stránku Rejstřík kurzu a klikněte na Upravit v kurzu).

Course_edit_page

Změňte data na stránce a klikněte na Uložit. Zobrazí se stránka Index kurzu s aktualizovanými daty kurzu.

Přidání stránky pro úpravy pro instruktory

Když upravujete záznam instruktora, chcete mít možnost aktualizovat jeho přiřazení do kanceláře. Entita Instructor má s OfficeAssignment entitou vztah 1:0 nebo 1, což znamená, že musíte zvládnout následující situace:

  • Pokud uživatel vymaže přiřazení office a původně obsahovalo hodnotu, musíte entitu OfficeAssignment odebrat a odstranit.
  • Pokud uživatel zadá hodnotu přiřazení kanceláře a ta byla původně prázdná, musíte vytvořit novou OfficeAssignment entitu.
  • Pokud uživatel změní hodnotu přiřazení kanceláře, musíte změnit hodnotu v existující OfficeAssignment entitě.

Otevřete soubor InstructorController.cs a podívejte se na metodu HttpGetEdit :

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

Vygenerovaný kód tady není to, co chcete. Nastavuje data pro rozevírací seznam, ale potřebujete textové pole. Nahraďte tuto metodu následujícím kódem:

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

Tento kód zahodí ViewBag příkaz a přidá dychtivou načítání pro přidruženou OfficeAssignment entitu. S metodou nemůžete provádět dychtivé načítání Find , takže Where k výběru instruktora se používají metody a Single .

Nahraďte metodu HttpPostEdit následujícím kódem. , který zpracovává aktualizace přiřazení 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);
}

Kód provede následující:

  • Získá aktuální Instructor entitu z databáze pomocí dychtivého načítání pro OfficeAssignment navigační vlastnost. Je to stejné jako to, co jste provedli HttpGetEdit v metodě .

  • Aktualizace načtenou Instructor entitu s hodnotami z pořadače modelu. Použité přetížení TryUpdateModel umožňuje zařadit vlastnosti, které chcete zahrnout do seznamu bezpečných. Tím se zabrání nadměrnému účtování, jak je vysvětleno v druhém kurzu.

    if (TryUpdateModel(instructorToUpdate, "",
          new string[] { "LastName", "FirstMidName", "HireDate", "OfficeAssignment" }))
    
  • Pokud je umístění kanceláře prázdné, nastaví Instructor.OfficeAssignment vlastnost na hodnotu null, aby se související řádek v OfficeAssignment tabulce odstranil.

    if (String.IsNullOrWhiteSpace(instructorToUpdate.OfficeAssignment.Location))
    {
        instructorToUpdate.OfficeAssignment = null;
    }
    
  • Uloží změny do databáze.

V souboru Views\Instructor\Edit.cshtml přidejte za div prvky pole Hire Date (Datum nástupu ) nové pole pro úpravu umístění kanceláře:

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

Spusťte stránku (vyberte kartu Instruktoři a pak klikněte na Upravit u instruktora). Změňte umístění Office a klikněte na Uložit.

Changing_the_office_location

Přidání zadání kurzu na stránku pro úpravy instruktora

Instruktoři mohou vyučovat libovolný počet kurzů. Teď vylepšíte stránku Pro úpravy instruktora přidáním možnosti změnit zadání kurzu pomocí skupiny zaškrtávacích políček, jak je znázorněno na následujícím snímku obrazovky:

Snímek obrazovky zobrazující stránku Pro úpravy instruktora s kurzy

Relace mezi Course entitami a Instructor je M:N, což znamená, že nemáte přímý přístup k tabulce spojení. Místo toho přidáte a odeberete entity do a z Instructor.Courses vlastnosti navigace.

Uživatelské rozhraní, které umožňuje změnit, ke kterým kurzům je instruktor přiřazen, je skupina zaškrtávacích políček. Zobrazí se zaškrtávací políčko pro každý kurz v databázi a vyberou se kurzy, ke kterým je instruktor aktuálně přiřazený. Uživatel může zaškrtnutím nebo zrušením zaškrtnutí políček změnit přiřazení kurzu. Pokud by byl počet kurzů mnohem větší, pravděpodobně byste chtěli použít jinou metodu prezentace dat v zobrazení, ale k vytvoření nebo odstranění relací byste použili stejnou metodu manipulace s vlastnostmi navigace.

Pokud chcete do zobrazení poskytnout data pro seznam zaškrtávacích políček, použijete třídu modelu zobrazení. Ve složce ViewModels vytvořte soubor AssignedCourseData.cs a nahraďte stávající kód následujícím kódem:

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

V souboru InstructorController.cs nahraďte metodu HttpGetEdit následujícím kódem. Změny jsou zvýrazněné.

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

Kód přidá nedočkavé načítání pro Courses vlastnost navigace a volá novou PopulateAssignedCourseData metodu, která poskytuje informace pro pole zaškrtávacích políček pomocí AssignedCourseData třídy modelu zobrazení.

Kód v PopulateAssignedCourseData metodě čte všechny Course entity, aby mohl načíst seznam kurzů pomocí třídy modelu zobrazení. U každého kurzu kód kontroluje, jestli kurz existuje v navigační vlastnosti instruktora Courses . Aby se vytvořilo efektivní vyhledávání při kontrole, jestli je kurz přiřazený instruktorovi, jsou kurzy přiřazené instruktorovi vloženy do kolekce HashSet . Vlastnost je nastavená Assigned na true pro kurzy, které má přiřazený instruktor. Zobrazení použije tuto vlastnost k určení zaškrtávacích políček, která musí být zobrazena jako vybraná. Nakonec se seznam předá zobrazení ve ViewBag vlastnosti .

Dále přidejte kód, který se spustí, když uživatel klikne na Uložit. Nahraďte metodu HttpPostEdit následujícím kódem, který volá novou metodu Courses , která aktualizuje navigační vlastnost Instructor entity. Změny jsou zvýrazněné.

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

Vzhledem k tomu, že zobrazení neobsahuje kolekci Course entit, nemůže pořadač modelů automaticky aktualizovat Courses vlastnost navigace. Místo použití pořadače modelu k aktualizaci navigační vlastnosti Courses to uděláte v nové UpdateInstructorCourses metodě. Proto je potřeba vyloučit vlastnost z vazby Courses modelu. To nevyžaduje žádnou změnu kódu, který volá TryUpdateModel , protože používáte přetížení safelisting a Courses není v seznamu zahrnutí.

Pokud nejsou zaškrtnutá žádná políčka, inicializuje Courses kód v UpdateInstructorCourses navigační vlastnost s prázdnou kolekcí:

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

Kód pak projde všechny kurzy v databázi a zkontroluje každý kurz oproti kurzům aktuálně přiřazeným instruktorovi v porovnání s kurzy vybranými v zobrazení. Pro usnadnění efektivního vyhledávání jsou poslední dvě kolekce uloženy v HashSet objektech.

Pokud bylo políčko pro kurz zaškrtnuté, ale kurz není v Instructor.Courses navigační vlastnosti, přidá se kurz do kolekce v navigační vlastnosti.

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

Pokud políčko pro kurz nebylo zaškrtnuté, ale kurz je v Instructor.Courses navigační vlastnosti, kurz se z vlastnosti navigace odebere.

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

V souboru Views\Instructor\Edit.cshtml přidejte pole Courses s polem zaškrtávacích políček tak, že hned za div prvky OfficeAssignment pole přidáte následující zvýrazněný kód:

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

Tento kód vytvoří tabulku HTML, která má tři sloupce. V každém sloupci je zaškrtávací políčko následované popis, které se skládá z čísla a názvu kurzu. Všechna zaškrtávací políčka mají stejný název ("selectedCourses"), který informuje pořadač modelu, že se mají považovat za skupinu. Atribut value každého zaškrtávacího políčka je nastaven na hodnotu CourseID. při publikování stránky, pořadač modelu předá matici kontroleru, která se skládá z CourseID hodnot pouze pro zaškrtávací políčka, která jsou vybrána.

Při počátečním vykreslení zaškrtávacích políček mají zaškrtávací políčka určená pro kurzy přiřazené instruktorovi checked atributy, které je vyberou (zobrazí je zaškrtnuté).

Po změně přiřazení kurzu budete chtít mít možnost ověřit změny, když se web vrátí na Index stránku. Proto musíte přidat sloupec do tabulky na této stránce. V takovém případě nemusíte objekt používat ViewBag , protože informace, které chcete zobrazit, už jsou v Courses navigační vlastnosti Instructor entity, kterou předáváte na stránku jako model.

V souboru Views\Instructor\Index.cshtml přidejte nadpis Courses hned za nadpis Office , jak je znázorněno v následujícím příkladu:

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

Pak hned za buňku s podrobnostmi o umístění kanceláře přidejte novou buňku podrobností:

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

Spuštěním stránky Index instruktora zobrazte kurzy přiřazené jednotlivým instruktorům:

Instructor_index_page

Kliknutím na Upravit na instruktoru zobrazíte stránku Upravit.

Instructor_edit_page_with_courses

Změňte některá přiřazení kurzu a klikněte na Uložit. Změny, které uděláte, se projeví na stránce Index.

Poznámka: Přístup k úpravě dat kurzu instruktora funguje dobře, když je omezený počet kurzů. Pro kolekce, které jsou mnohem větší, by se vyžadovalo jiné uživatelské rozhraní a jiná metoda aktualizace.

Aktualizace metody Delete

Změňte kód v metodě HttpPost Delete tak, aby se při odstranění instruktora odstranil záznam přiřazení Office (pokud existuje):

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

Pokud se pokusíte odstranit instruktora, který je přiřazený k oddělení jako správce, zobrazí se chyba referenční integrity. Projděte si aktuální verzi tohoto kurzu , kde najdete další kód, který automaticky odebere instruktora ze všech oddělení, kde je instruktor přiřazený jako správce.

Souhrn

Dokončili jste tento úvod do práce se souvisejícími daty. Zatím jste v těchto kurzech provedli celou řadu operací CRUD, ale neřešili jste problémy se souběžností. Další kurz představí téma souběžnosti, vysvětlí možnosti pro zpracování a přidá zpracování souběžnosti do kódu CRUD, který jste už napsali pro jeden typ entity.

Odkazy na další prostředky Entity Framework najdete na konci posledního kurzu v této sérii.