Sdílet prostřednictvím


Kurz: Aktualizace souvisejících dat pomocí EF v aplikaci ASP.NET MVC

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

Následující ilustrace znázorňují některé ze stránek, se kterými budete pracovat.

Course_create_page

Instructor_edit_page_with_courses

Úprava instruktora s využitím kurzů

V tomto kurzu jste:

  • Přizpůsobení stránek kurzů
  • Stránka Přidání office na instruktory
  • Přidání kurzů na stránku instruktorů
  • Update DeleteConfirmed
  • Přidání umístění kanceláře a kurzů na stránku Vytvořit

Požadavky

Přizpůsobení stránek kurzů

Když se vytvoří nová entita kurzu, musí mít vztah k existujícímu oddělení. Aby to bylo možné, obsahuje vygenerovaný kód metody kontroleru a zobrazení Vytvořit a Upravit, která obsahují rozevírací seznam pro výběr oddělení. Rozevírací seznam nastaví vlastnost cizího Course.DepartmentID klíče a to je vše, co Entity Framework potřebuje, aby bylo možné načíst Department vlastnost navigace 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 seřadili rozevírací seznam.

V souboru CourseController.cs odstraňte čtyři Create metody a Edit 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 (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);
} 

Na začátek souboru přidejte následující using příkaz:

using System.Data.Entity.Infrastructure;

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ý umožňuje volajícímu kódu určit položku, která bude vybrána při vykreslení rozevíracího seznamu. Zobrazení předá název DepartmentID pomocné rutině DropDownList a pomocník pak ví, že v objektu ViewBag hledá 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ým kurzům:

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

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

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

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

Zobrazení kurzu jsou už vygenerovaná s rozevíracími seznamy pro pole oddělení, ale nechcete, aby id oddělení popis pro toto pole, a proto proveďte následující zvýrazněnou změnu souboru Views\Course\Create.cshtml a změňte popis.

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

Proveďte stejnou změnu v souboru Views\Course\Edit.cshtml.

Za normálních okolností správce nevygeneruje primární klíč, protože hodnota klíče je generována databází a nedá se změnit a není smysluplnou hodnotou, která by se uživatelům zobrazila. V případě entit kurzu obsahuje správce pole textové pole CourseID , protože chápe, že atribut znamená, že DatabaseGeneratedOption.None uživatel by měl být schopen zadat hodnotu primárního klíče. Ale nerozumí tomu, protože číslo je smysluplné, chcete ho vidět v ostatních zobrazeních, takže ho musíte přidat ručně.

V části Views\Course\Edit.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="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>

V zobrazení pro úpravy už existuje skryté pole (Html.HiddenFor pomocná rutina) pro číslo kurzu. Přidání pomocníka Html.LabelFor neodstraní potřebu skrytého pole, protože nezpůsobí zahrnutí čísla kurzu do publikovaných dat, když uživatel klikne na Uložit na stránce Upravit.

V části Views\Course\Delete.cshtml and Views\Course\Details.cshtml změňte název oddělení popis z "Název" na "Oddělení" a před pole Název přidejte pole s číslem kurzu.

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

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

Hodnota Nastavení
Číslo Zadejte 1000.
Nadpis Zadejte Algebra.
Kredity Zadejte 4.
Oddělení Vyberte Matematika.

Klikněte na Vytvořit. Zobrazí se stránka Index 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ě navázána.

Spusťte stránku Upravit (zobrazte stránku Index kurzu a klikněte na Upravit na kurzu).

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

Stránka Přidání office na 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:nula 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ě mělo 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í office, musíte změnit hodnotu v existující OfficeAssignment entitě.

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

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

Zde vygenerovaný kód 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)
{
    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);
}

Tento kód zahodí příkaz a přidá dychtivé ViewBag načítání přidružené OfficeAssignment entity. Pomocí metody nemůžete provádět dychtivé načítání Find , takže Where k výběru instruktora se místo toho použijí metody a Single .

Nahraďte metodu HttpPostEdit následujícím kódem. který zpracovává aktualizace přiřazení office:

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

Odkaz na RetryLimitExceededException vyžaduje příkaz using . Pokud ho chcete přidat, najeďte myší na RetryLimitExceededException. Zobrazí se následující zpráva:  Zpráva o výjimce opakování

Vyberte Zobrazit potenciální opravy a pak použijte System.Data.Entity.Infrastructure.

Řešení výjimky opakování

Kód provede následující:

  • Změní název metody na EditPost , protože podpis je teď stejný jako HttpGet metoda ( ActionName atribut určuje, že adresa URL /Edit/ se stále používá).

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

  • Aktualizace načtenou Instructor entitu s hodnotami z pořadače modelu. Použité přetížení TryUpdateModel umožňuje zobrazit seznam vlastností, které chcete zahrnout. Zabráníte tak 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 odstranil související řádek v OfficeAssignment tabulce.

    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 pro pole Datum přijetí nové pole pro úpravu umístění kanceláře:

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

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

Přidání kurzů na stránku instruktorů

Instruktoři mohou vyučovat libovolný počet kurzů. Teď stránku Úpravy instruktora vylepšíte přidáním možnosti změnit zadání kurzu pomocí skupiny zaškrtávacích políček.

Relace mezi Course entitami a Instructor je M:N, což znamená, že nemáte přímý přístup k vlastnostem cizího klíče, které jsou v 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 jsou zaškrtnuté kurzy, ke kterým je instruktor aktuálně přiřazený. Uživatel může zaškrtnout nebo zrušit zaškrtnutí políček a 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)
{
    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;
}

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

Podpis metody se teď liší od HttpGetEdit metody, takže se název metody změní zpět EditPost na Edit.

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 Courses navigační vlastnosti 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 explicitní přetížení seznamu 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 přidáním následujícího kódu hned za div prvky OfficeAssignment pole a před div prvek pro tlačítko Uložit :

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

Pokud po vložení kódu nevypadají konce řádků a odsazení jako tady, opravte vše ručně tak, aby vypadalo jako tady. Odsazení nemusí být dokonalé, ale @</tr><tr>řádky , @:<td>, @:</td>, a @</tr> musí být každý na jednom řádku, jak je znázorněno, jinak se zobrazí chyba za běhu.

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>Last Name</th> 
    <th>First Name</th> 
    <th>Hire Date</th> 
    <th>Office</th>
    <th>Courses</th>
    <th></th> 
</tr>

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

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

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

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

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, který zde používáte k úpravě dat o kurzech 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 odstraněníPotvrzené

V souboru InstructorController.cs odstraňte metodu DeleteConfirmed a místo něj vložte následující kód.

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

Tento kód provede následující změnu:

  • Pokud je instruktor přiřazený jako správce jakéhokoli oddělení, odebere přiřazení instruktora z tohoto oddělení. Bez tohoto kódu by se vám při pokusu o odstranění instruktora, který byl přiřazený jako správce pro oddělení, zobrazila chyba referenční integrity.

Tento kód nezpracuje scénář jednoho instruktora přiřazeného jako správce pro více oddělení. V posledním kurzu přidáte kód, který zabrání tomu, aby se tento scénář provedl.

Přidání umístění kanceláře a kurzů na stránku Vytvořit

V souboru InstructorController.cs odstraňte HttpGet metody a HttpPostCreate a přidejte na jejich místo následující kód:

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

Tento kód je podobný tomu, co jste viděli u metod Edit s tím rozdílem, že zpočátku nejsou vybrané žádné kurzy. Metoda HttpGetCreate volá metodu PopulateAssignedCourseData ne proto, že by mohly být vybrány kurzy, ale aby poskytla prázdnou kolekci pro smyčku foreach v zobrazení (jinak by kód zobrazení vyvolal výjimku nulového odkazu).

Metoda HttpPost Create přidá každý vybraný kurz do navigační vlastnosti Courses před kód šablony, který kontroluje chyby ověření a přidá nového instruktora do databáze. Kurzy se přidávají i v případě, že dojde k chybám modelu, takže když dojde k chybám modelu (například uživatel zadal klíč k neplatnému datu), aby se při opětovném zobrazení stránky s chybovou zprávou automaticky obnovily všechny provedené výběry kurzu.

Všimněte si, že abyste mohli přidat kurzy do Courses navigační vlastnosti, musíte inicializovat vlastnost jako prázdnou kolekci:

instructor.Courses = new List<Course>();

Jako alternativu k tomu, abyste to udělali v kódu kontroleru, můžete to udělat v modelu Instruktor změnou getter vlastnosti tak, aby automaticky vytvořil kolekci, pokud neexistuje, jak je znázorněno v následujícím příkladu:

private ICollection<Course> _courses;
public virtual ICollection<Course> Courses 
{ 
    get
    {
        return _courses ?? (_courses = new List<Course>());
    }
    set
    {
        _courses = value;
    } 
}

Pokud upravíte Courses vlastnost tímto způsobem, můžete v kontroleru odebrat explicitní inicializační kód vlastnosti.

V souboru Views\Instructor\Create.cshtml přidejte textové pole umístění kanceláře a zaškrtávací políčka kurzu za pole data pronájmu a před tlačítko Odeslat .

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

Po vložení kódu opravte konce řádků a odsazení, jak jste to udělali dříve u stránky Upravit.

Spusťte stránku Vytvořit a přidejte instruktora.

Zpracování transakcí

Jak je vysvětleno v kurzu Základní funkce CRUD, Entity Framework ve výchozím nastavení implicitně implementuje transakce. Scénáře, ve kterých potřebujete větší kontrolu – například pokud chcete do transakce zahrnout operace prováděné mimo Entity Framework – najdete informace v tématu Práce s transakcemi na webu MSDN.

Získání kódu

Stažení dokončeného projektu

Další materiály

Odkazy na další prostředky Entity Framework najdete v tématu ASP.NET Data Access – Doporučené zdroje informací.

Další krok

V tomto kurzu jste:

  • Přizpůsobené stránky kurzů
  • Přidání office na stránku instruktorů
  • Přidání kurzů na stránku instruktorů
  • Aktualizace OdstraněníPotvrzené
  • Přidání umístění kanceláře a kurzů na stránku Vytvořit

V dalším článku se dozvíte, jak implementovat asynchronní programovací model.