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 nezpřístupňuje entity Entity Framework přímo, takže do a z příslušných navigačních vlastností přidáte a odeberete entity.

Následující ilustrace ukazují některé stránky, se kterými budete pracovat.

Course_create_page

Instructor_edit_page_with_courses

Úprava instruktora pomocí kurzů

V tomto kurzu se naučíte:

  • Přizpůsobení stránek kurzů
  • Přidání office na stránku instruktorů
  • Přidání kurzů na stránku instruktorů
  • Aktualizace DeleteConfirmed
  • Přidání umístění a kurzů office 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 se to usnadnilo, vygenerovaný kód obsahuje metody kontroleru a zobrazení Pro vytvoření a úpravy, 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 k načtení Department navigační vlastnosti s příslušnou Department entitou. Vygenerovaný kód použijete, ale mírně ho změníte tak, že přidáte zpracování chyb a seřadíte rozevírací seznam.

V 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 pomocné rutině DropDownList a pomocník pak ví, že hledá v objektu ViewBag pojmenovaný SelectList DepartmentID.DepartmentID

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

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

Metoda HttpGet Edit nastaví vybranou položku na základě ID oddělení, které je již přiřazeno k úpravě kurzu:

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 obě Create a Edit také zahrnují 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 když se stránka znovu zobrazí, aby se zobrazila chybová zpráva bez ohledu na to, které oddělení bylo vybráno, zůstane vybrané.

Zobrazení kurzu jsou už vygenerovaná rozevíracími seznamy pro pole oddělení, ale nechcete, aby titulky Id oddělení pro toto pole byly změněny , a proto změňte titulek na soubor Views\Course\Create.cshtml .

@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 zobrazení\Course\Edit.cshtml.

Za normálních okolností scaffolder nevygeneruje primární klíč, protože hodnota klíče je vygenerována databází a nedá se změnit a není smysluplnou hodnotou, která se má uživatelům zobrazit. Pro entity kurzu scaffolder obsahuje textové pole pro CourseID pole, 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, že protože číslo je smysluplné, chcete ho zobrazit v ostatních zobrazeních, takže ho musíte přidat ručně.

Do zobrazení\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ž je skryté pole (Html.HiddenFor pomocná rutina). Přidání pomocné rutiny 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 zobrazeních\Course\Delete.cshtml a Views\Course\Details.cshtml změňte titulek názvu oddělení z "Název" na "Oddělení" a před pole Název přidejte pole čísla 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 Rejstřík kurzu a klikněte na Vytvořit nový) a zadejte data pro nový kurz:

Hodnota Nastavení
Počet Zadejte 1000.
Nadpis Zadejte Algebra.
Kredity Zadejte 4.
Oddělení Vyberte matematiku.

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ě navázána.

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

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í office na stránku instruktorů

Při úpravě záznamu instruktora chcete mít možnost aktualizovat zadání instruktora v kanceláři. Entita Instructor má relaci 1:0 nebo 1 s entitou OfficeAssignment , což znamená, že musíte zpracovat následující situace:

  • Pokud uživatel vymaže přiřazení kanceláře a původně měla hodnotu, musíte entitu OfficeAssignment odebrat a odstranit.
  • Pokud uživatel zadá hodnotu přiřazení kanceláře a původně byla 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 InstructorController.cs a podívejte se na metodu HttpGet Edit:

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

Vygenerovaný kód tady není to, co chcete. Nastavuje data pro rozevírací seznam, ale to, co potřebujete, je textové pole. Tuto metodu nahraďte 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í ViewBag příkaz a přidá dychtivé načítání přidružené OfficeAssignment entity. S metodou Find nemůžete provádět dychtivé načítání, takže Where se Single místo toho používají k výběru instruktora.

Nahraďte metodu HttpPost Edit 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 příkaz vyžaduje using , aby se přidal – 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, protože EditPost podpis je teď stejný jako HttpGet metoda ( ActionName atribut určuje, že se stále používá adresa URL /Edit/).

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

  • Aktualizuje načtenou Instructor entitu hodnotami z pořadače modelu. Použité přetížení TryUpdateModel umožňuje zobrazit seznam vlastností, které chcete zahrnout. 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 Views\Instructor\Edit.cshtml za div prvky pro pole Datum přijetí přidejte 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 Instruktory 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ů

Instruktori mohou učit 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.

Relace mezi entitami Course a Instructor entitami 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 navigační vlastnosti.

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 ty, ke kterým je instruktor aktuálně přiřazený, jsou vybrané. Uživatel může zaškrtnutím nebo zrušením zaškrtnutí políček změnit zadání 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 navigačními vlastnostmi.

K zadání dat do zobrazení seznamu zaškrtávacích políček použijete třídu modelu zobrazení. Ve složce ViewModels vytvořte 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 InstructorController.cs nahraďte metodu HttpGet Edit 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ává dychtivé načítání pro Courses navigační vlastnost a volá novou PopulateAssignedCourseData metodu, která poskytuje informace pro pole zaškrtávacího políčka pomocí AssignedCourseData třídy modelu zobrazení.

Kód v PopulateAssignedCourseData metodě čte všechny Course entity za účelem načtení seznamu kurzů pomocí třídy modelu zobrazení. Pro každý kurz kód zkontroluje, jestli kurz existuje ve vlastnosti navigace instruktora Courses . Pokud chcete vytvořit efektivní vyhledávání při kontrole, jestli je kurz přiřazen instruktorovi, kurzy přiřazené instruktorovi se vloží do kolekce HashSet . Vlastnost je nastavena Assigned na true kurzy, které je instruktor přiřazen. Zobrazení použije tuto vlastnost k určení, která zaškrtávací políčka 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, která aktualizuje Courses 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 nyní liší od HttpGet Edit metody, takže název metody se změní z EditPost zpět na Edit.

Vzhledem k tomu, že zobrazení neobsahuje kolekci Course entit, pořadač modelů nemůže automaticky aktualizovat Courses navigační vlastnost. Místo použití pořadače modelu k aktualizaci Courses navigační vlastnosti to uděláte v nové UpdateInstructorCourses metodě. Proto je nutné vlastnost vyloučit Courses z vazby 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 nebyla vybrána žádná zaškrtávací políčka, kód inicializuje UpdateInstructorCourses Courses navigační vlastnost s prázdnou kolekcí:

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

Kód pak projde všemi kurzy v databázi a zkontroluje jednotlivé kurzy proti kurzům, které jsou aktuálně přiřazené instruktorovi, a ty, které byly vybrány v zobrazení. Pro usnadnění efektivního vyhledávání jsou tyto dvě kolekce uloženy v HashSet objektech.

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

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

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

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

V 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 bezprostředně za div prvky pole OfficeAssignment 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>

Po vložení kódu, pokud konce řádků a odsazení nevypadají jako tady, ručně opravte všechno tak, aby vypadalo, jak vidíte tady. Odsazení nemusí být dokonalé, ale @</tr><tr>@:<td>@:</td>@</tr> všechna řádky musí být na jednom řádku, jak je znázorněno, nebo 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é titulkem, 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č modelů, že se mají považovat za skupinu. Atribut value každého zaškrtávacího CourseID. políčka je nastaven na hodnotu 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 zaškrtnutá.

Po počátečním vykreslení zaškrtávacích políček mají kurzy přiřazené instruktorovi checked atributy, které je vybere (zobrazí se zaškrtnuté).

Po změně zadání kurzu budete chtít být schopni ověřit změny, když se web vrátí na Index stránku. Proto musíte do tabulky na této stránce přidat sloupec. 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 views\Instructor\Index.cshtml přidejte nadpis Courses bezprostředně 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>

Potom přidejte novou buňku podrobností bezprostředně za buňku s podrobnostmi o poloze kanceláře:

<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 indexu instruktora zobrazte kurzy přiřazené jednotlivým instruktorům.

Kliknutím na Upravit u instruktora zobrazíte stránku Upravit.

Změňte některá zadání kurzu a klikněte na Uložit. Provedené změny se projeví na stránce Rejstříku.

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

Aktualizace DeleteConfirmed

V InstructorController.cs odstraňte metodu DeleteConfirmed a vložte na jeho místo 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 libovolného oddělení, odebere z tohoto oddělení přiřazení instruktora. Bez tohoto kódu se zobrazí chyba referenční integrity, pokud jste se pokusili odstranit instruktora, který byl přiřazený jako správce pro oddělení.

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 k danému scénáři došlo.

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

V InstructorController.cs odstraňte HttpGet a HttpPost Create metody 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 pro metody Edit s tím rozdílem, že zpočátku nejsou vybrány žádné kurzy. Metoda HttpGet Create volá metodu PopulateAssignedCourseData ne, protože mohou být vybrány kurzy, ale za účelem poskytnutí prázdné kolekce smyčky foreach v zobrazení (jinak kód zobrazení vyvolá výjimku odkazu null).

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ý instruktor do databáze. Kurzy se přidají i v případě, že dojde k chybám modelu, aby se při výskytu chyb modelu (například v uživatelském klíči zobrazilo neplatné datum), 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 vlastnost inicializovat jako prázdnou kolekci:

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

Jako alternativu k tomu v kódu kontroleru byste to mohli udělat v modelu instruktora tak, že změníte 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 tímto způsobem upravíte Courses vlastnost, můžete odebrat explicitní inicializační kód vlastnosti v kontroleru.

Do zobrazení\Instruktor\Create.cshtml přidejte textové pole umístění kanceláře a zaškrtávací políčka kurzu za pole datum přijetí 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í tak, jak jste to udělali dříve u stránky Pro úpravy.

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

Zpracování transakcí

Jak je vysvětleno v kurzu Základní funkce CRUD, ve výchozím nastavení Entity Framework implicitně implementuje transakce. Scénáře, ve kterých potřebujete větší kontrolu – například pokud chcete zahrnout operace provedené mimo Entity Framework v transakci – viz Práce s transakcemi na 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 Přístup k datům – doporučené zdroje informací.

Další krok

V tomto kurzu se naučíte:

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

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