Sdílet prostřednictvím


Kurz: Aktualizace souvisejících dat – ASP.NET MVC pomocí EF Core

V předchozím kurzu jste zobrazili související data; v tomto kurzu aktualizujete související data aktualizací polí cizího klíče a vlastností navigace.

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

Course Edit page

Edit Instructor page

V tomto kurzu se naučíte:

  • Přizpůsobení stránek kurzů
  • Přidat stránku pro úpravy instruktorů
  • Přidání kurzů na stránku Pro úpravy
  • Aktualizovat stránku Odstranit
  • 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á Course entita, 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.

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

public IActionResult Create()
{
    PopulateDepartmentsDropDownList();
    return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("CourseID,Credits,DepartmentID,Title")] Course course)
{
    if (ModelState.IsValid)
    {
        _context.Add(course);
        await _context.SaveChangesAsync();
        return RedirectToAction(nameof(Index));
    }
    PopulateDepartmentsDropDownList(course.DepartmentID);
    return View(course);
}
public async Task<IActionResult> Edit(int? id)
{
    if (id == null)
    {
        return NotFound();
    }

    var course = await _context.Courses
        .AsNoTracking()
        .FirstOrDefaultAsync(m => m.CourseID == id);
    if (course == null)
    {
        return NotFound();
    }
    PopulateDepartmentsDropDownList(course.DepartmentID);
    return View(course);
}
[HttpPost, ActionName("Edit")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> EditPost(int? id)
{
    if (id == null)
    {
        return NotFound();
    }

    var courseToUpdate = await _context.Courses
        .FirstOrDefaultAsync(c => c.CourseID == id);

    if (await TryUpdateModelAsync<Course>(courseToUpdate,
        "",
        c => c.Credits, c => c.DepartmentID, c => c.Title))
    {
        try
        {
            await _context.SaveChangesAsync();
        }
        catch (DbUpdateException /* ex */)
        {
            //Log the error (uncomment ex variable name and write a log.)
            ModelState.AddModelError("", "Unable to save changes. " +
                "Try again, and if the problem persists, " +
                "see your system administrator.");
        }
        return RedirectToAction(nameof(Index));
    }
    PopulateDepartmentsDropDownList(courseToUpdate.DepartmentID);
    return View(courseToUpdate);
}

Edit Po metodě HttpPost vytvořte novou metodu, která načte informace o oddělení pro rozevírací seznam.

private void PopulateDepartmentsDropDownList(object selectedDepartment = null)
{
    var departmentsQuery = from d in _context.Departments
                           orderby d.Name
                           select d;
    ViewBag.DepartmentID = new SelectList(departmentsQuery.AsNoTracking(), "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í v ViewBag. 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á pomocné rutině <select> značky název "DepartmentID" a pomocník pak ví, že hledá v objektu ViewBagSelectList název "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 IActionResult Create()
{
    PopulateDepartmentsDropDownList();
    return View();
}

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

public async Task<IActionResult> Edit(int? id)
{
    if (id == null)
    {
        return NotFound();
    }

    var course = await _context.Courses
        .AsNoTracking()
        .FirstOrDefaultAsync(m => m.CourseID == id);
    if (course == null)
    {
        return NotFound();
    }
    PopulateDepartmentsDropDownList(course.DepartmentID);
    return View(course);
}

Metody HttpPost pro oba Create a Edit také zahrnout kód, který nastaví vybranou položku při opětovném zobrazení stránky po chybě. Tím se zajistí, že při opětovném zobrazení stránky se zobrazí chybová zpráva, a to bez ohledu na to, které oddělení bylo vybráno, zůstane vybrané.

Přidat. Metody AsNoTracking to Details a Delete

Pokud chcete optimalizovat výkon stránek Podrobnosti kurzu a Delete, přidejte AsNoTracking volání do Details metod HttpGet Delete .

public async Task<IActionResult> Details(int? id)
{
    if (id == null)
    {
        return NotFound();
    }

    var course = await _context.Courses
        .Include(c => c.Department)
        .AsNoTracking()
        .FirstOrDefaultAsync(m => m.CourseID == id);
    if (course == null)
    {
        return NotFound();
    }

    return View(course);
}
public async Task<IActionResult> Delete(int? id)
{
    if (id == null)
    {
        return NotFound();
    }

    var course = await _context.Courses
        .Include(c => c.Department)
        .AsNoTracking()
        .FirstOrDefaultAsync(m => m.CourseID == id);
    if (course == null)
    {
        return NotFound();
    }

    return View(course);
}

Úprava zobrazení kurzu

Do Views/Courses/Create.cshtmlrozevíracího seznamu Oddělení přidejte možnost Vybrat oddělení, změňte popis z ID oddělení na Oddělení a přidejte ověřovací zprávu.

<div class="form-group">
    <label asp-for="Department" class="control-label"></label>
    <select asp-for="DepartmentID" class="form-control" asp-items="ViewBag.DepartmentID">
        <option value="">-- Select Department --</option>
    </select>
    <span asp-validation-for="DepartmentID" class="text-danger" />
</div>

Proveďte Views/Courses/Edit.cshtmlstejnou změnu pro pole Oddělení, ve Create.cshtmlkteré jste právě provedli .

Views/Courses/Edit.cshtmlDo pole Název přidejte také pole čísla kurzu. Vzhledem k tomu, že číslo kurzu je primárním klíčem, zobrazí se, ale nedá se změnit.

<div class="form-group">
    <label asp-for="CourseID" class="control-label"></label>
    <div>@Html.DisplayFor(model => model.CourseID)</div>
</div>

V zobrazení pro úpravy už je skryté pole (<input type="hidden">) pro číslo kurzu. <label> Přidání pomocné rutiny značek eliminuje 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.

Přidejte Views/Courses/Delete.cshtmldo horní části pole číslo kurzu a změňte ID oddělení na název oddělení.

@model ContosoUniversity.Models.Course

@{
    ViewData["Title"] = "Delete";
}

<h2>Delete</h2>

<h3>Are you sure you want to delete this?</h3>
<div>
    <h4>Course</h4>
    <hr />
    <dl class="row">
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.CourseID)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.CourseID)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Title)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Title)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Credits)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Credits)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Department)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Department.Name)
        </dd>
    </dl>
    
    <form asp-action="Delete">
        <div class="form-actions no-color">
            <input type="submit" value="Delete" class="btn btn-default" /> |
            <a asp-action="Index">Back to List</a>
        </div>
    </form>
</div>

Proveďte Views/Courses/Details.cshtmlstejnou změnu, pro kterou jste právě udělali Delete.cshtml.

Testování stránek kurzu

Spusťte aplikaci, vyberte kartu Kurzy , klikněte na Vytvořit nový a zadejte data pro nový kurz:

Course Create page

Klikněte na Vytvořit. Stránka Rejstřík kurzů se zobrazí 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.

Na stránce Rejstřík kurzů klikněte na upravit kurz.

Course Edit page

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

Přidat stránku pro úpravy 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 váš kód musí zpracovávat následující situace:

  • Pokud uživatel vymaže přiřazení kanceláře a původně měl hodnotu, odstraňte entitu OfficeAssignment .

  • Pokud uživatel zadá hodnotu přiřazení kanceláře a původně byla prázdná, vytvořte novou OfficeAssignment entitu.

  • Pokud uživatel změní hodnotu přiřazení kanceláře, změňte hodnotu v existující OfficeAssignment entitě.

Aktualizace kontroleru instruktorů

Změňte InstructorsController.cskód v metodě HttpGet Edit tak, aby načítá vlastnost navigace a volání AsNoTrackingentity OfficeAssignment Instruktor:

public async Task<IActionResult> Edit(int? id)
{
    if (id == null)
    {
        return NotFound();
    }

    var instructor = await _context.Instructors
        .Include(i => i.OfficeAssignment)
        .AsNoTracking()
        .FirstOrDefaultAsync(m => m.ID == id);
    if (instructor == null)
    {
        return NotFound();
    }
    return View(instructor);
}

Nahraďte metodu HttpPost Edit následujícím kódem pro zpracování aktualizací přiřazení office:

[HttpPost, ActionName("Edit")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> EditPost(int? id)
{
    if (id == null)
    {
        return NotFound();
    }

    var instructorToUpdate = await _context.Instructors
        .Include(i => i.OfficeAssignment)
        .FirstOrDefaultAsync(s => s.ID == id);

    if (await TryUpdateModelAsync<Instructor>(
        instructorToUpdate,
        "",
        i => i.FirstMidName, i => i.LastName, i => i.HireDate, i => i.OfficeAssignment))
    {
        if (String.IsNullOrWhiteSpace(instructorToUpdate.OfficeAssignment?.Location))
        {
            instructorToUpdate.OfficeAssignment = null;
        }
        try
        {
            await _context.SaveChangesAsync();
        }
        catch (DbUpdateException /* ex */)
        {
            //Log the error (uncomment ex variable name and write a log.)
            ModelState.AddModelError("", "Unable to save changes. " +
                "Try again, and if the problem persists, " +
                "see your system administrator.");
        }
        return RedirectToAction(nameof(Index));
    }
    return View(instructorToUpdate);
}

Kód provede následující:

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

  • 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 udělali v metodě HttpGet Edit .

  • Aktualizace načtené Instructor entity s hodnotami z pořadače modelu. Přetížení TryUpdateModel umožňuje deklarovat vlastnosti, které chcete zahrnout. Tím se zabrání nadměrnému účtování, jak je vysvětleno v druhém kurzu.

    if (await TryUpdateModelAsync<Instructor>(
        instructorToUpdate,
        "",
        i => i.FirstMidName, i => i.LastName, i => i.HireDate, i => i.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.

Aktualizace zobrazení pro úpravy instruktora

Do Views/Instructors/Edit.cshtmlpole , přidejte nové pole pro úpravu umístění kanceláře, na konci před tlačítko Uložit :

<div class="form-group">
    <label asp-for="OfficeAssignment.Location" class="control-label"></label>
    <input asp-for="OfficeAssignment.Location" class="form-control" />
    <span asp-validation-for="OfficeAssignment.Location" class="text-danger" />
</div>

Spusťte aplikaci, vyberte kartu Instruktori a potom klikněte na Upravit u instruktora. Změňte umístění Office a klikněte na Uložit.

Instructor Edit page

Přidání kurzů na stránku Pro úpravy

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, jak je znázorněno na následujícím snímku obrazovky:

Instructor Edit page with courses

Vztah mezi entitami Course a Instructor entitami je M:N. Pokud chcete přidat a odebrat relace, přidáte a odeberete entity do sady entit join a z CourseAssignments ní.

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 aktuálně přiřazený instruktor. 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 entitou spojení.

Aktualizace kontroleru instruktorů

Pokud chcete do zobrazení zadat data pro seznam zaškrtávacích políček, použijete třídu modelu zobrazení.

Vytvořte AssignedCourseData.cs ve složce SchoolViewModels a nahraďte stávající kód následujícím kódem:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

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

V InstructorsController.cs, nahraďte HttpGet Edit metoda následujícím kódem. Změny jsou zvýrazněné.

public async Task<IActionResult> Edit(int? id)
{
    if (id == null)
    {
        return NotFound();
    }

    var instructor = await _context.Instructors
        .Include(i => i.OfficeAssignment)
        .Include(i => i.CourseAssignments).ThenInclude(i => i.Course)
        .AsNoTracking()
        .FirstOrDefaultAsync(m => m.ID == id);
    if (instructor == null)
    {
        return NotFound();
    }
    PopulateAssignedCourseData(instructor);
    return View(instructor);
}

private void PopulateAssignedCourseData(Instructor instructor)
{
    var allCourses = _context.Courses;
    var instructorCourses = new HashSet<int>(instructor.CourseAssignments.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)
        });
    }
    ViewData["Courses"] = viewModel;
}

Kód přidává dychtivou 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 HashSet kolekce. Vlastnost je nastavena Assigned na hodnotu true pro kurzy, ke kterým je instruktor přiřazen. Toto 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í v ViewDatasouboru .

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 a přidejte novou metodu, která aktualizuje Courses navigační vlastnost entity Instruktor.

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int? id, string[] selectedCourses)
{
    if (id == null)
    {
        return NotFound();
    }

    var instructorToUpdate = await _context.Instructors
        .Include(i => i.OfficeAssignment)
        .Include(i => i.CourseAssignments)
            .ThenInclude(i => i.Course)
        .FirstOrDefaultAsync(m => m.ID == id);

    if (await TryUpdateModelAsync<Instructor>(
        instructorToUpdate,
        "",
        i => i.FirstMidName, i => i.LastName, i => i.HireDate, i => i.OfficeAssignment))
    {
        if (String.IsNullOrWhiteSpace(instructorToUpdate.OfficeAssignment?.Location))
        {
            instructorToUpdate.OfficeAssignment = null;
        }
        UpdateInstructorCourses(selectedCourses, instructorToUpdate);
        try
        {
            await _context.SaveChangesAsync();
        }
        catch (DbUpdateException /* ex */)
        {
            //Log the error (uncomment ex variable name and write a log.)
            ModelState.AddModelError("", "Unable to save changes. " +
                "Try again, and if the problem persists, " +
                "see your system administrator.");
        }
        return RedirectToAction(nameof(Index));
    }
    UpdateInstructorCourses(selectedCourses, instructorToUpdate);
    PopulateAssignedCourseData(instructorToUpdate);
    return View(instructorToUpdate);
}
private void UpdateInstructorCourses(string[] selectedCourses, Instructor instructorToUpdate)
{
    if (selectedCourses == null)
    {
        instructorToUpdate.CourseAssignments = new List<CourseAssignment>();
        return;
    }

    var selectedCoursesHS = new HashSet<string>(selectedCourses);
    var instructorCourses = new HashSet<int>
        (instructorToUpdate.CourseAssignments.Select(c => c.Course.CourseID));
    foreach (var course in _context.Courses)
    {
        if (selectedCoursesHS.Contains(course.CourseID.ToString()))
        {
            if (!instructorCourses.Contains(course.CourseID))
            {
                instructorToUpdate.CourseAssignments.Add(new CourseAssignment { InstructorID = instructorToUpdate.ID, CourseID = course.CourseID });
            }
        }
        else
        {

            if (instructorCourses.Contains(course.CourseID))
            {
                CourseAssignment courseToRemove = instructorToUpdate.CourseAssignments.FirstOrDefault(i => i.CourseID == course.CourseID);
                _context.Remove(courseToRemove);
            }
        }
    }
}

Podpis metody se nyní liší od metody HttpGet Edit , takže název metody se změní z EditPost zpět na Edit.

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

Pokud nebyla vybrána žádná zaškrtávací políčka, kód inicializuje UpdateInstructorCoursesCourseAssignments navigační vlastnost s prázdnou kolekcí a vrátí:

private void UpdateInstructorCourses(string[] selectedCourses, Instructor instructorToUpdate)
{
    if (selectedCourses == null)
    {
        instructorToUpdate.CourseAssignments = new List<CourseAssignment>();
        return;
    }

    var selectedCoursesHS = new HashSet<string>(selectedCourses);
    var instructorCourses = new HashSet<int>
        (instructorToUpdate.CourseAssignments.Select(c => c.Course.CourseID));
    foreach (var course in _context.Courses)
    {
        if (selectedCoursesHS.Contains(course.CourseID.ToString()))
        {
            if (!instructorCourses.Contains(course.CourseID))
            {
                instructorToUpdate.CourseAssignments.Add(new CourseAssignment { InstructorID = instructorToUpdate.ID, CourseID = course.CourseID });
            }
        }
        else
        {

            if (instructorCourses.Contains(course.CourseID))
            {
                CourseAssignment courseToRemove = instructorToUpdate.CourseAssignments.FirstOrDefault(i => i.CourseID == course.CourseID);
                _context.Remove(courseToRemove);
            }
        }
    }
}

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 zaškrtnuté políčko pro kurz, ale kurz není v Instructor.CourseAssignments navigační vlastnosti, kurz se přidá do kolekce v navigační vlastnosti.

private void UpdateInstructorCourses(string[] selectedCourses, Instructor instructorToUpdate)
{
    if (selectedCourses == null)
    {
        instructorToUpdate.CourseAssignments = new List<CourseAssignment>();
        return;
    }

    var selectedCoursesHS = new HashSet<string>(selectedCourses);
    var instructorCourses = new HashSet<int>
        (instructorToUpdate.CourseAssignments.Select(c => c.Course.CourseID));
    foreach (var course in _context.Courses)
    {
        if (selectedCoursesHS.Contains(course.CourseID.ToString()))
        {
            if (!instructorCourses.Contains(course.CourseID))
            {
                instructorToUpdate.CourseAssignments.Add(new CourseAssignment { InstructorID = instructorToUpdate.ID, CourseID = course.CourseID });
            }
        }
        else
        {

            if (instructorCourses.Contains(course.CourseID))
            {
                CourseAssignment courseToRemove = instructorToUpdate.CourseAssignments.FirstOrDefault(i => i.CourseID == course.CourseID);
                _context.Remove(courseToRemove);
            }
        }
    }
}

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

private void UpdateInstructorCourses(string[] selectedCourses, Instructor instructorToUpdate)
{
    if (selectedCourses == null)
    {
        instructorToUpdate.CourseAssignments = new List<CourseAssignment>();
        return;
    }

    var selectedCoursesHS = new HashSet<string>(selectedCourses);
    var instructorCourses = new HashSet<int>
        (instructorToUpdate.CourseAssignments.Select(c => c.Course.CourseID));
    foreach (var course in _context.Courses)
    {
        if (selectedCoursesHS.Contains(course.CourseID.ToString()))
        {
            if (!instructorCourses.Contains(course.CourseID))
            {
                instructorToUpdate.CourseAssignments.Add(new CourseAssignment { InstructorID = instructorToUpdate.ID, CourseID = course.CourseID });
            }
        }
        else
        {

            if (instructorCourses.Contains(course.CourseID))
            {
                CourseAssignment courseToRemove = instructorToUpdate.CourseAssignments.FirstOrDefault(i => i.CourseID == course.CourseID);
                _context.Remove(courseToRemove);
            }
        }
    }
}

Aktualizace zobrazení instruktora

Do Views/Instructors/Edit.cshtmlpole Courses přidejte pole Courses s polem zaškrtávacích políček tak, že hned za div prvky pole Office a před div prvek tlačítka Uložit přidáte následující kód.

Poznámka

Když kód vložíte do sady Visual Studio, můžou se konce řádků změnit způsobem, který kód přeruší. Pokud se kód po vložení liší, můžete automatické formátování vrátit zpět stisknutím kombinace kláves Ctrl+Z. Tím se opraví konce řádků, aby vypadaly tak, 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. Když vyberete blok nového kódu, stiskněte třikrát klávesu Tab, aby se nový kód zarovnál s existujícím kódem. Tento problém je opravený v sadě Visual Studio 2019.

<div class="form-group">
    <div class="col-md-offset-2 col-md-10">
        <table>
            <tr>
                @{
                    int cnt = 0;
                    List<ContosoUniversity.Models.SchoolViewModels.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>

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č modelů, že se mají považovat za skupinu. Atribut hodnoty 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.

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

Spusťte aplikaci, vyberte kartu Instruktori a kliknutím na Upravit u instruktora zobrazte stránku Upravit .

Instructor Edit page with courses

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.

Aktualizovat stránku Odstranit

Odstraňte InstructorsController.csmetodu DeleteConfirmed a vložte následující kód na jeho místo.

[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> DeleteConfirmed(int id)
{
    Instructor instructor = await _context.Instructors
        .Include(i => i.CourseAssignments)
        .SingleAsync(i => i.ID == id);

    var departments = await _context.Departments
        .Where(d => d.InstructorID == id)
        .ToListAsync();
    departments.ForEach(d => d.InstructorID = null);

    _context.Instructors.Remove(instructor);

    await _context.SaveChangesAsync();
    return RedirectToAction(nameof(Index));
}

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

  • Nenačítá navigační CourseAssignments vlastnost. Musíte ho zahrnout nebo EF nebude vědět o souvisejících CourseAssignment entitách a neodstraní je. Abyste se vyhnuli tomu, že byste je tady museli číst, můžete v databázi nakonfigurovat kaskádové odstranění.

  • Pokud je instruktor, který se má odstranit, přiřazený jako správce oddělení, odebere zadání instruktora z těchto oddělení.

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

Odstraňte InstructorsController.csmetody HttpGet a HttpPost Create a pak přidejte na jejich místo následující kód:

public IActionResult Create()
{
    var instructor = new Instructor();
    instructor.CourseAssignments = new List<CourseAssignment>();
    PopulateAssignedCourseData(instructor);
    return View();
}

// POST: Instructors/Create
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("FirstMidName,HireDate,LastName,OfficeAssignment")] Instructor instructor, string[] selectedCourses)
{
    if (selectedCourses != null)
    {
        instructor.CourseAssignments = new List<CourseAssignment>();
        foreach (var course in selectedCourses)
        {
            var courseToAdd = new CourseAssignment { InstructorID = instructor.ID, CourseID = int.Parse(course) };
            instructor.CourseAssignments.Add(courseToAdd);
        }
    }
    if (ModelState.IsValid)
    {
        _context.Add(instructor);
        await _context.SaveChangesAsync();
        return RedirectToAction(nameof(Index));
    }
    PopulateAssignedCourseData(instructor);
    return View(instructor);
}

Tento kód je podobný tomu, co jste viděli pro Edit metody s tím rozdílem, že zpočátku nejsou vybrány žádné kurzy. Metoda HttpGet Create volá metodu PopulateAssignedCourseData , 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 CourseAssignments navigační vlastnosti předtím, než zkontroluje chyby ověření a přidá do databáze nového instruktora. 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 došlo k neplatnému datu) a stránka se znovu zobrazí s chybovou zprávou, všechny provedené výběry kurzu se automaticky obnoví.

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

instructor.CourseAssignments = new List<CourseAssignment>();

Jako alternativu k tomu v kódu kontroleru byste to mohli udělat v Instructor modelu 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<CourseAssignment> _courseAssignments;
public ICollection<CourseAssignment> CourseAssignments
{
    get
    {
        return _courseAssignments ?? (_courseAssignments = new List<CourseAssignment>());
    }
    set
    {
        _courseAssignments = value;
    }
}

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

Do Views/Instructor/Create.cshtmlpole , přidejte textové pole umístění kanceláře a zaškrtávací políčka pro kurzy před tlačítko Odeslat. Stejně jako v případě stránky Pro úpravy opravte formátování, pokud Visual Studio přeformátuje kód při vložení.

<div class="form-group">
    <label asp-for="OfficeAssignment.Location" class="control-label"></label>
    <input asp-for="OfficeAssignment.Location" class="form-control" />
    <span asp-validation-for="OfficeAssignment.Location" class="text-danger" />
</div>

<div class="form-group">
    <div class="col-md-offset-2 col-md-10">
        <table>
            <tr>
                @{
                    int cnt = 0;
                    List<ContosoUniversity.Models.SchoolViewModels.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>

Otestujte spuštěním aplikace a vytvořením instruktora.

Zpracování transakcí

Jak je vysvětleno v kurzu CRUD, 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 Transakce.

Získání kódu

Stáhněte nebo zobrazte dokončenou aplikaci.

Další kroky

V tomto kurzu se naučíte:

  • Přizpůsobené stránky kurzů
  • Přidání stránky pro úpravy instruktorů
  • Přidání kurzů na stránku Pro úpravy
  • Aktualizovaná stránka Odstranit
  • Přidání místa v kanceláři a kurzů na stránku Vytvořit

V dalším kurzu se dozvíte, jak řešit konflikty souběžnosti.