Sdílet prostřednictvím


Část 7, Razor Stránky s EF Core v ASP.NET Core – aktualizace souvisejících dat

Tom Dykstra, Jon P Smith a Rick Anderson

Webová aplikace Contoso University ukazuje, jak vytvářet Razor webové aplikace Pages pomocí EF Core sady Visual Studio. Informace o sérii kurzů najdete v prvním kurzu.

Pokud narazíte na problémy, které nemůžete vyřešit, stáhněte si dokončenou aplikaci a porovnejte tento kód s tím, co jste vytvořili podle kurzu.

V tomto kurzu se dozvíte, jak aktualizovat související data. Následující ilustrace znázorňují některé dokončené stránky.

Stránka Upravit kurzStránka Pro úpravy instruktora

Aktualizace stránek Pro vytvoření a úpravy kurzu

Vygenerovaný kód pro stránky Vytvoření a úpravy kurzu obsahuje rozevírací seznam Oddělení, který zobrazuje DepartmentID, an int. V rozevíracím seznamu by se měl zobrazit název oddělení, takže obě tyto stránky potřebují seznam názvů oddělení. Chcete-li poskytnout tento seznam, použijte základní třídu pro stránky Vytvořit a upravit.

Vytvoření základní třídy pro vytvoření a úpravy kurzu

Vytvořte Pages/Courses/DepartmentNamePageModel.cs soubor s následujícím kódem:

using ContosoUniversity.Data;
using ContosoUniversity.Models;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.EntityFrameworkCore;
using System.Linq;

namespace ContosoUniversity.Pages.Courses
{
    public class DepartmentNamePageModel : PageModel
    {
        public SelectList DepartmentNameSL { get; set; }

        public void PopulateDepartmentsDropDownList(SchoolContext _context,
            object selectedDepartment = null)
        {
            var departmentsQuery = from d in _context.Departments
                                   orderby d.Name // Sort by name.
                                   select d;

            DepartmentNameSL = new SelectList(departmentsQuery.AsNoTracking(),
                nameof(Department.DepartmentID),
                nameof(Department.Name),
                selectedDepartment);
        }
    }
}

Předchozí kód vytvoří seznam SelectList názvů oddělení. Je-li selectedDepartment zadáno, je toto oddělení vybráno v sadě SelectList.

Třídy modelu vytvoření a úpravy stránky budou odvozeny z DepartmentNamePageModel.

Aktualizace modelu stránky Vytvoření kurzu

Kurz je přiřazen k oddělení. Základní třída pro stránky Vytvořit a Upravit poskytuje SelectList pro výběr oddělení. Rozevírací seznam, který používá SelectList vlastnost cizího Course.DepartmentID klíče (FK). EF CoreCourse.DepartmentID používá jazyk FK k načtení Department navigační vlastnosti.

Vytvoření kurzu

Aktualizujte Pages/Courses/Create.cshtml.cs následujícím kódem:

using ContosoUniversity.Models;
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;

namespace ContosoUniversity.Pages.Courses
{
    public class CreateModel : DepartmentNamePageModel
    {
        private readonly ContosoUniversity.Data.SchoolContext _context;

        public CreateModel(ContosoUniversity.Data.SchoolContext context)
        {
            _context = context;
        }

        public IActionResult OnGet()
        {
            PopulateDepartmentsDropDownList(_context);
            return Page();
        }

        [BindProperty]
        public Course Course { get; set; }

        public async Task<IActionResult> OnPostAsync()
        {
            var emptyCourse = new Course();

            if (await TryUpdateModelAsync<Course>(
                 emptyCourse,
                 "course",   // Prefix for form value.
                 s => s.CourseID, s => s.DepartmentID, s => s.Title, s => s.Credits))
            {
                _context.Courses.Add(emptyCourse);
                await _context.SaveChangesAsync();
                return RedirectToPage("./Index");
            }

            // Select DepartmentID if TryUpdateModelAsync fails.
            PopulateDepartmentsDropDownList(_context, emptyCourse.DepartmentID);
            return Page();
        }
      }
}

Pokud chcete zobrazit komentáře ke kódu přeložené do jiných jazyků, než je angličtina, dejte nám vědět v této diskuzi na GitHubu.

Předchozí kód:

  • Odvozuje z DepartmentNamePageModel.
  • Slouží TryUpdateModelAsync k zabránění nadměrnému umístění.
  • Odebere ViewData["DepartmentID"]. Jedná se DepartmentNameSL SelectList o model silného typu a bude používán stránkou Razor . Modely silného typu jsou upřednostňované před slabě typovanými modely. Další informace naleznete v tématu Slabě typovaná data (ViewData a ViewBag).

Aktualizace stránky Vytvoření Razor kurzu

Aktualizujte Pages/Courses/Create.cshtml následujícím kódem:

@page
@model ContosoUniversity.Pages.Courses.CreateModel
@{
    ViewData["Title"] = "Create Course";
}
<h2>Create</h2>
<h4>Course</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form method="post">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="Course.CourseID" class="control-label"></label>
                <input asp-for="Course.CourseID" class="form-control" />
                <span asp-validation-for="Course.CourseID" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Course.Title" class="control-label"></label>
                <input asp-for="Course.Title" class="form-control" />
                <span asp-validation-for="Course.Title" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Course.Credits" class="control-label"></label>
                <input asp-for="Course.Credits" class="form-control" />
                <span asp-validation-for="Course.Credits" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Course.Department" class="control-label"></label>
                <select asp-for="Course.DepartmentID" class="form-control"
                        asp-items="@Model.DepartmentNameSL">
                    <option value="">-- Select Department --</option>
                </select>
                <span asp-validation-for="Course.DepartmentID" class="text-danger" />
            </div>
            <div class="form-group">
                <input type="submit" value="Create" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>
<div>
    <a asp-page="Index">Back to List</a>
</div>
@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

Předchozí kód provede následující změny:

  • Změní titulek z DepartmentID na Department.
  • "ViewBag.DepartmentID" Nahradí za DepartmentNameSL (ze základní třídy).
  • Přidá možnost Vybrat oddělení. Tato změna v rozevíracím seznamu vykreslí možnost Vybrat oddělení, pokud ještě není vybráno žádné oddělení, a ne v prvním oddělení.
  • Přidá ověřovací zprávu, když oddělení není vybrané.

Stránka Razor používá pomocníka pro výběr značek:

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

Otestujte stránku Vytvořit. Na stránce Vytvořit se místo ID oddělení zobrazí název oddělení.

Aktualizace modelu stránky Upravit kurz

Aktualizujte Pages/Courses/Edit.cshtml.cs následujícím kódem:

using ContosoUniversity.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System.Threading.Tasks;

namespace ContosoUniversity.Pages.Courses
{
    public class EditModel : DepartmentNamePageModel
    {
        private readonly ContosoUniversity.Data.SchoolContext _context;

        public EditModel(ContosoUniversity.Data.SchoolContext context)
        {
            _context = context;
        }

        [BindProperty]
        public Course Course { get; set; }

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

            Course = await _context.Courses
                .Include(c => c.Department).FirstOrDefaultAsync(m => m.CourseID == id);

            if (Course == null)
            {
                return NotFound();
            }

            // Select current DepartmentID.
            PopulateDepartmentsDropDownList(_context, Course.DepartmentID);
            return Page();
        }

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

            var courseToUpdate = await _context.Courses.FindAsync(id);

            if (courseToUpdate == null)
            {
                return NotFound();
            }

            if (await TryUpdateModelAsync<Course>(
                 courseToUpdate,
                 "course",   // Prefix for form value.
                   c => c.Credits, c => c.DepartmentID, c => c.Title))
            {
                await _context.SaveChangesAsync();
                return RedirectToPage("./Index");
            }

            // Select DepartmentID if TryUpdateModelAsync fails.
            PopulateDepartmentsDropDownList(_context, courseToUpdate.DepartmentID);
            return Page();
        }       
    }
}

Změny se podobají změnám provedeným v modelu vytvořit stránku. V předchozím kódu PopulateDepartmentsDropDownList předá ID oddělení, které toto oddělení vybere v rozevíracím seznamu.

Aktualizace stránky Upravit Razor kurz

Aktualizujte Pages/Courses/Edit.cshtml následujícím kódem:

@page
@model ContosoUniversity.Pages.Courses.EditModel

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

<h2>Edit</h2>

<h4>Course</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form method="post">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <input type="hidden" asp-for="Course.CourseID" />
            <div class="form-group">
                <label asp-for="Course.CourseID" class="control-label"></label>
                <div>@Html.DisplayFor(model => model.Course.CourseID)</div>
            </div>
            <div class="form-group">
                <label asp-for="Course.Title" class="control-label"></label>
                <input asp-for="Course.Title" class="form-control" />
                <span asp-validation-for="Course.Title" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Course.Credits" class="control-label"></label>
                <input asp-for="Course.Credits" class="form-control" />
                <span asp-validation-for="Course.Credits" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Course.Department" class="control-label"></label>
                <select asp-for="Course.DepartmentID" class="form-control" 
                        asp-items="@Model.DepartmentNameSL"></select>
                <span asp-validation-for="Course.DepartmentID" class="text-danger"></span>
            </div>
            <div class="form-group">
                <input type="submit" value="Save" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>

<div>
    <a asp-page="./Index">Back to List</a>
</div>

@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

Předchozí kód provede následující změny:

  • Zobrazí ID kurzu. Obecně se nezobrazuje primární klíč (PK) entity. Sady PKS jsou obvykle pro uživatele nesmyslné. V tomto případě je PK číslo kurzu.
  • Změní titulek rozevíracího seznamu Oddělení z ID oddělení na Oddělení.
  • "ViewBag.DepartmentID" DepartmentNameSLNahradí třídou , která je v základní třídě.

Stránka obsahuje skryté pole (<input type="hidden">) pro číslo kurzu. <label> Přidání pomocné rutiny asp-for="Course.CourseID" značek neodstraní potřebu skrytého pole. <input type="hidden"> je vyžadováno, aby číslo kurzu bylo zahrnuto do odeslaných dat, když uživatel vybere Možnost Uložit.

Aktualizace modelů stránek kurzu

AsNoTracking může zvýšit výkon, pokud sledování není vyžadováno.

Aktualizujte Pages/Courses/Delete.cshtml.cs a Pages/Courses/Details.cshtml.cs přidejte AsNoTracking do OnGetAsync metod:

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

    Course = await _context.Courses
        .AsNoTracking()
        .Include(c => c.Department)
        .FirstOrDefaultAsync(m => m.CourseID == id);

    if (Course == null)
    {
        return NotFound();
    }
    return Page();
}

Aktualizace stránek kurzu Razor

Aktualizujte Pages/Courses/Delete.cshtml následujícím kódem:

@page
@model ContosoUniversity.Pages.Courses.DeleteModel

@{
    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.Course.CourseID)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Course.CourseID)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Course.Title)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Course.Title)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Course.Credits)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Course.Credits)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Course.Department)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Course.Department.Name)
        </dd>
    </dl>
    
    <form method="post">
        <input type="hidden" asp-for="Course.CourseID" />
        <input type="submit" value="Delete" class="btn btn-danger" /> |
        <a asp-page="./Index">Back to List</a>
    </form>
</div>

Proveďte stejné změny na stránce Podrobnosti.

@page
@model ContosoUniversity.Pages.Courses.DetailsModel

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

<h2>Details</h2>

<div>
    <h4>Course</h4>
    <hr />
    <dl class="row">
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Course.CourseID)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Course.CourseID)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Course.Title)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Course.Title)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Course.Credits)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Course.Credits)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Course.Department)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Course.Department.Name)
        </dd>
    </dl>
</div>
<div>
    <a asp-page="./Edit" asp-route-id="@Model.Course.CourseID">Edit</a> |
    <a asp-page="./Index">Back to List</a>
</div>

Testování stránek kurzu

Otestujte stránky pro vytvoření, úpravy, podrobnosti a odstranění.

Aktualizace stránek pro vytváření a úpravy instruktora

Instruktori mohou učit libovolný počet kurzů. Následující obrázek ukazuje stránku pro úpravy instruktora s polem zaškrtávacích políček kurzu.

Stránka Pro úpravy instruktora s kurzy

Zaškrtávací políčka umožňují změny kurzů, ke kterým je instruktor přiřazený. Zaškrtávací políčko se zobrazí pro každý kurz v databázi. Jsou vybrány kurzy, ke kterým je instruktor přiřazený. Uživatel může zaškrtnutím nebo zrušením zaškrtnutí políček změnit zadání kurzu. Pokud byl počet kurzů mnohem větší, může lepší fungovat jiné uživatelské rozhraní. Metoda správy relace M:N se zde ale nezměnila. Pokud chcete vytvořit nebo odstranit relace, manipulujete s entitou spojení.

Vytvoření třídy pro přiřazená data kurzů

Vytvořte Models/SchoolViewModels/AssignedCourseData.cs pomocí následujícího kódu:

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

Třída AssignedCourseData obsahuje data pro vytvoření zaškrtávacích políček pro kurzy přiřazené instruktorovi.

Vytvoření základní třídy modelu stránky instruktora

Pages/Instructors/InstructorCoursesPageModel.cs Vytvořte základní třídu:

using ContosoUniversity.Data;
using ContosoUniversity.Models;
using ContosoUniversity.Models.SchoolViewModels;
using Microsoft.AspNetCore.Mvc.RazorPages;
using System.Collections.Generic;
using System.Linq;

namespace ContosoUniversity.Pages.Instructors
{
    public class InstructorCoursesPageModel : PageModel
    {
        public List<AssignedCourseData> AssignedCourseDataList;

        public void PopulateAssignedCourseData(SchoolContext context,
                                               Instructor instructor)
        {
            var allCourses = context.Courses;
            var instructorCourses = new HashSet<int>(
                instructor.Courses.Select(c => c.CourseID));
            AssignedCourseDataList = new List<AssignedCourseData>();
            foreach (var course in allCourses)
            {
                AssignedCourseDataList.Add(new AssignedCourseData
                {
                    CourseID = course.CourseID,
                    Title = course.Title,
                    Assigned = instructorCourses.Contains(course.CourseID)
                });
            }
        }
    }
}

Je InstructorCoursesPageModel základní třídou pro modely stránek Edit a Create. PopulateAssignedCourseData přečte všechny Course entity, které se mají naplnit AssignedCourseDataList. Pro každý kurz kód nastaví CourseID, titul a zda je instruktor přiřazen k kurzu. Sada hash se používá k efektivnímu vyhledávání.

Zpracování umístění kanceláře

Další relací, kterou musí stránka pro úpravy zpracovat, je relace 1:0 nebo 1, kterou má entita Instruktor s entitou OfficeAssignment . Kód pro úpravy instruktora musí zpracovávat následující scénáře:

  • Pokud uživatel vymaže přiřazení kanceláře, odstraňte entitu OfficeAssignment .
  • Pokud uživatel zadá přiřazení kanceláře a bude prázdný, vytvořte novou OfficeAssignment entitu.
  • Pokud uživatel změní přiřazení kanceláře, aktualizujte entitu OfficeAssignment .

Aktualizace modelu stránky Pro úpravy instruktora

Aktualizujte Pages/Instructors/Edit.cshtml.cs následujícím kódem:

using ContosoUniversity.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace ContosoUniversity.Pages.Instructors
{
    public class EditModel : InstructorCoursesPageModel
    {
        private readonly ContosoUniversity.Data.SchoolContext _context;

        public EditModel(ContosoUniversity.Data.SchoolContext context)
        {
            _context = context;
        }

        [BindProperty]
        public Instructor Instructor { get; set; }

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

            Instructor = await _context.Instructors
                .Include(i => i.OfficeAssignment)
                .Include(i => i.Courses)
                .AsNoTracking()
                .FirstOrDefaultAsync(m => m.ID == id);

            if (Instructor == null)
            {
                return NotFound();
            }
            PopulateAssignedCourseData(_context, Instructor);
            return Page();
        }

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

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

            if (instructorToUpdate == null)
            {
                return NotFound();
            }

            if (await TryUpdateModelAsync<Instructor>(
                instructorToUpdate,
                "Instructor",
                i => i.FirstMidName, i => i.LastName,
                i => i.HireDate, i => i.OfficeAssignment))
            {
                if (String.IsNullOrWhiteSpace(
                    instructorToUpdate.OfficeAssignment?.Location))
                {
                    instructorToUpdate.OfficeAssignment = null;
                }
                UpdateInstructorCourses(selectedCourses, instructorToUpdate);
                await _context.SaveChangesAsync();
                return RedirectToPage("./Index");
            }
            UpdateInstructorCourses(selectedCourses, instructorToUpdate);
            PopulateAssignedCourseData(_context, instructorToUpdate);
            return Page();
        }

        public 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 _context.Courses)
            {
                if (selectedCoursesHS.Contains(course.CourseID.ToString()))
                {
                    if (!instructorCourses.Contains(course.CourseID))
                    {
                        instructorToUpdate.Courses.Add(course);
                    }
                }
                else
                {
                    if (instructorCourses.Contains(course.CourseID))
                    {
                        var courseToRemove = instructorToUpdate.Courses.Single(
                                                        c => c.CourseID == course.CourseID);
                        instructorToUpdate.Courses.Remove(courseToRemove);
                    }
                }
            }
        }
    }
}

Předchozí kód:

  • Získá aktuální Instructor entitu z databáze pomocí dychtivého načítání pro OfficeAssignment vlastnosti navigace a Courses vlastnosti navigace.
  • Aktualizuje načtenou Instructor entitu hodnotami z pořadače modelu. TryUpdateModelAsync zabraňuje nadměrnému umístění.
  • Pokud je umístění kanceláře prázdné, nastaví Instructor.OfficeAssignment se na hodnotu null. Pokud Instructor.OfficeAssignment je null, související řádek v OfficeAssignment tabulce se odstraní.
  • Volání PopulateAssignedCourseData za účelem poskytnutí informací o zaškrtávacích polících pomocí AssignedCourseData třídy OnGetAsync modelu zobrazení
  • OnPostAsync VoláníUpdateInstructorCourses, která použijí informace z zaškrtávacích políček na entitu instruktora, která se upravuje.
  • Volání PopulateAssignedCourseData a UpdateInstructorCourses přihlášení OnPostAsync , pokud TryUpdateModelAsync selže. Tato metoda volá obnovení přiřazených dat kurzu zadaných na stránce při opětovném zobrazení s chybovou zprávou.

Razor Vzhledem k tomu, že stránka neobsahuje kolekci entit kurzu, 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, která je provedena v nové UpdateInstructorCourses metodě. Proto je nutné vlastnost vyloučit Courses z vazby modelu. To nevyžaduje žádnou změnu kódu, který volá TryUpdateModelAsync , protože používáte přetížení s deklarovanými vlastnostmi a Courses není v seznamu zahrnutí.

Pokud nebyla vybrána žádná zaškrtávací políčka, kód inicializuje UpdateInstructorCourses prázdnou instructorToUpdate.Courses kolekci a vrátí:

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

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

Pokud je zaškrtnuté políčko pro kurz, 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 není 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))
    {
        var courseToRemove = instructorToUpdate.Courses.Single(
                                        c => c.CourseID == course.CourseID);
        instructorToUpdate.Courses.Remove(courseToRemove);
    }
}

Aktualizace stránky Pro úpravy Razor instruktora

Aktualizujte Pages/Instructors/Edit.cshtml následujícím kódem:

@page
@model ContosoUniversity.Pages.Instructors.EditModel
@{
    ViewData["Title"] = "Edit";
}
<h2>Edit</h2>
<h4>Instructor</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form method="post">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <input type="hidden" asp-for="Instructor.ID" />
            <div class="form-group">
                <label asp-for="Instructor.LastName" class="control-label"></label>
                <input asp-for="Instructor.LastName" class="form-control" />
                <span asp-validation-for="Instructor.LastName" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Instructor.FirstMidName" class="control-label"></label>
                <input asp-for="Instructor.FirstMidName" class="form-control" />
                <span asp-validation-for="Instructor.FirstMidName" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Instructor.HireDate" class="control-label"></label>
                <input asp-for="Instructor.HireDate" class="form-control" />
                <span asp-validation-for="Instructor.HireDate" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Instructor.OfficeAssignment.Location" class="control-label"></label>
                <input asp-for="Instructor.OfficeAssignment.Location" class="form-control" />
                <span asp-validation-for="Instructor.OfficeAssignment.Location" class="text-danger" />
            </div>
            <div class="form-group">
                <div class="table">
                    <table>
                        <tr>
                            @{
                                int cnt = 0;

                                foreach (var course in Model.AssignedCourseDataList)
                                {
                                    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>
            <div class="form-group">
                <input type="submit" value="Save" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>

<div>
    <a asp-page="./Index">Back to List</a>
</div>

@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

Předchozí kód vytvoří tabulku HTML se třemi sloupci. Každý sloupec má zaškrtávací políčko a titulek obsahující číslo a název kurzu. Všechna zaškrtávací políčka mají stejný název ("selectedCourses"). Použití stejného názvu informuje pořadač modelu, aby s nimi zacházal jako se skupinou. Atribut hodnoty každého zaškrtávacího políčka je nastaven na CourseIDhodnotu . Když se stránka publikuje, pořadač modelu předá pole, které se skládá z CourseID hodnot pouze pro zaškrtnutá políčka.

Po počátečním vykreslení zaškrtávacích políček se vyberou kurzy přiřazené instruktorovi.

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 bylo vhodnější a efektivnější použít jiné uživatelské rozhraní a jinou metodu aktualizace.

Spusťte aplikaci a otestujte aktualizovanou stránku Pro úpravy instruktorů. Změňte některá zadání kurzu. Změny se projeví na stránce rejstříku.

Aktualizace stránky Pro vytvoření instruktora

Aktualizujte model stránky Pro vytvoření instruktora a kód podobný stránce Upravit:

using ContosoUniversity.Data;
using ContosoUniversity.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace ContosoUniversity.Pages.Instructors
{
    public class CreateModel : InstructorCoursesPageModel
    {
        private readonly ContosoUniversity.Data.SchoolContext _context;
        private readonly ILogger<InstructorCoursesPageModel> _logger;

        public CreateModel(SchoolContext context,
                          ILogger<InstructorCoursesPageModel> logger)
        {
            _context = context;
            _logger = logger;
        }

        public IActionResult OnGet()
        {
            var instructor = new Instructor();
            instructor.Courses = new List<Course>();

            // Provides an empty collection for the foreach loop
            // foreach (var course in Model.AssignedCourseDataList)
            // in the Create Razor page.
            PopulateAssignedCourseData(_context, instructor);
            return Page();
        }

        [BindProperty]
        public Instructor Instructor { get; set; }

        public async Task<IActionResult> OnPostAsync(string[] selectedCourses)
        {
            var newInstructor = new Instructor();

            if (selectedCourses.Length > 0)
            {
                newInstructor.Courses = new List<Course>();
                // Load collection with one DB call.
                _context.Courses.Load();
            }

            // Add selected Courses courses to the new instructor.
            foreach (var course in selectedCourses)
            {
                var foundCourse = await _context.Courses.FindAsync(int.Parse(course));
                if (foundCourse != null)
                {
                    newInstructor.Courses.Add(foundCourse);
                }
                else
                {
                    _logger.LogWarning("Course {course} not found", course);
                }
            }

            try
            {
                if (await TryUpdateModelAsync<Instructor>(
                                newInstructor,
                                "Instructor",
                                i => i.FirstMidName, i => i.LastName,
                                i => i.HireDate, i => i.OfficeAssignment))
                {
                    _context.Instructors.Add(newInstructor);
                    await _context.SaveChangesAsync();
                    return RedirectToPage("./Index");
                }
                return RedirectToPage("./Index");
            }
            catch (Exception ex)
            {
                _logger.LogError(ex.Message);
            }

            PopulateAssignedCourseData(_context, newInstructor);
            return Page();
        }
    }
}

Předchozí kód:

  • Přidá protokolování pro upozornění a chybové zprávy.

  • Volání Load, která načte všechny kurzy v jednom databázovém volání. U malých kolekcí se jedná o optimalizaci při použití FindAsync. FindAsync vrátí sledovaný entitu bez požadavku na databázi.

    public async Task<IActionResult> OnPostAsync(string[] selectedCourses)
    {
        var newInstructor = new Instructor();
    
        if (selectedCourses.Length > 0)
        {
            newInstructor.Courses = new List<Course>();
            // Load collection with one DB call.
            _context.Courses.Load();
        }
    
        // Add selected Courses courses to the new instructor.
        foreach (var course in selectedCourses)
        {
            var foundCourse = await _context.Courses.FindAsync(int.Parse(course));
            if (foundCourse != null)
            {
                newInstructor.Courses.Add(foundCourse);
            }
            else
            {
                _logger.LogWarning("Course {course} not found", course);
            }
        }
    
        try
        {
            if (await TryUpdateModelAsync<Instructor>(
                            newInstructor,
                            "Instructor",
                            i => i.FirstMidName, i => i.LastName,
                            i => i.HireDate, i => i.OfficeAssignment))
            {
                _context.Instructors.Add(newInstructor);
                await _context.SaveChangesAsync();
                return RedirectToPage("./Index");
            }
            return RedirectToPage("./Index");
        }
        catch (Exception ex)
        {
            _logger.LogError(ex.Message);
        }
    
        PopulateAssignedCourseData(_context, newInstructor);
        return Page();
    }
    
  • _context.Instructors.Add(newInstructor) vytvoří novou Instructor relaci M :N bez explicitního mapování tabulky spojení. V EF 5.0 bylo přidáno M:N.

Otestujte stránku pro vytvoření instruktora.

Aktualizujte stránku Pro vytvoření Razor instruktora pomocí kódu podobného stránce Upravit:

@page
@model ContosoUniversity.Pages.Instructors.CreateModel

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

<h2>Create</h2>

<h4>Instructor</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form method="post">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="Instructor.LastName" class="control-label"></label>
                <input asp-for="Instructor.LastName" class="form-control" />
                <span asp-validation-for="Instructor.LastName" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Instructor.FirstMidName" class="control-label"></label>
                <input asp-for="Instructor.FirstMidName" class="form-control" />
                <span asp-validation-for="Instructor.FirstMidName" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Instructor.HireDate" class="control-label"></label>
                <input asp-for="Instructor.HireDate" class="form-control" />
                <span asp-validation-for="Instructor.HireDate" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Instructor.OfficeAssignment.Location" class="control-label"></label>
                <input asp-for="Instructor.OfficeAssignment.Location" class="form-control" />
                <span asp-validation-for="Instructor.OfficeAssignment.Location" class="text-danger" />
            </div>
            <div class="form-group">
                <div class="table">
                    <table>
                        <tr>
                            @{
                                int cnt = 0;

                                foreach (var course in Model.AssignedCourseDataList)
                                {
                                    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>
            <div class="form-group">
                <input type="submit" value="Create" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>

<div>
    <a asp-page="Index">Back to List</a>
</div>

@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

Aktualizace stránky Pro odstranění instruktora

Aktualizujte Pages/Instructors/Delete.cshtml.cs následujícím kódem:

using ContosoUniversity.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using System.Linq;
using System.Threading.Tasks;

namespace ContosoUniversity.Pages.Instructors
{
    public class DeleteModel : PageModel
    {
        private readonly ContosoUniversity.Data.SchoolContext _context;

        public DeleteModel(ContosoUniversity.Data.SchoolContext context)
        {
            _context = context;
        }

        [BindProperty]
        public Instructor Instructor { get; set; }

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

            Instructor = await _context.Instructors.FirstOrDefaultAsync(m => m.ID == id);

            if (Instructor == null)
            {
                return NotFound();
            }
            return Page();
        }

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

            Instructor instructor = await _context.Instructors
                .Include(i => i.Courses)
                .SingleAsync(i => i.ID == id);

            if (instructor == null)
            {
                return RedirectToPage("./Index");
            }

            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 RedirectToPage("./Index");
        }
    }
}

Předchozí kód provede následující změny:

  • Používá dychtivé načítání pro Courses navigační vlastnost. Courses musí být zahrnuté nebo se při odstranění instruktora neodstraní. Abyste se vyhnuli nutnosti je číst, nakonfigurujte kaskádové odstranění v databázi.

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

Spusťte aplikaci a otestujte stránku Odstranit.

Další kroky

V tomto kurzu se dozvíte, jak aktualizovat související data. Následující ilustrace znázorňují některé dokončené stránky.

Stránka Upravit kurzStránka Pro úpravy instruktora

Aktualizace stránek Pro vytvoření a úpravy kurzu

Vygenerovaný kód pro stránky Vytvoření a úpravy kurzu obsahuje rozevírací seznam Oddělení, který zobrazuje ID oddělení (celé číslo). V rozevíracím seznamu by se měl zobrazit název oddělení, takže obě tyto stránky potřebují seznam názvů oddělení. Chcete-li poskytnout tento seznam, použijte základní třídu pro stránky Vytvořit a upravit.

Vytvoření základní třídy pro vytvoření a úpravy kurzu

Vytvořte Pages/Courses/DepartmentNamePageModel.cs soubor s následujícím kódem:

using ContosoUniversity.Data;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.EntityFrameworkCore;
using System.Linq;

namespace ContosoUniversity.Pages.Courses
{
    public class DepartmentNamePageModel : PageModel
    {
        public SelectList DepartmentNameSL { get; set; }

        public void PopulateDepartmentsDropDownList(SchoolContext _context,
            object selectedDepartment = null)
        {
            var departmentsQuery = from d in _context.Departments
                                   orderby d.Name // Sort by name.
                                   select d;

            DepartmentNameSL = new SelectList(departmentsQuery.AsNoTracking(),
                        "DepartmentID", "Name", selectedDepartment);
        }
    }
}

Předchozí kód vytvoří seznam SelectList názvů oddělení. Je-li selectedDepartment zadáno, je toto oddělení vybráno v sadě SelectList.

Třídy modelu vytvoření a úpravy stránky budou odvozeny z DepartmentNamePageModel.

Aktualizace modelu stránky Vytvoření kurzu

Kurz je přiřazen k oddělení. Základní třída pro stránky Vytvořit a Upravit poskytuje SelectList pro výběr oddělení. Rozevírací seznam, který používá SelectList vlastnost cizího Course.DepartmentID klíče (FK). EF CoreCourse.DepartmentID používá jazyk FK k načtení Department navigační vlastnosti.

Vytvoření kurzu

Aktualizujte Pages/Courses/Create.cshtml.cs následujícím kódem:

using ContosoUniversity.Models;
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;

namespace ContosoUniversity.Pages.Courses
{
    public class CreateModel : DepartmentNamePageModel
    {
        private readonly ContosoUniversity.Data.SchoolContext _context;

        public CreateModel(ContosoUniversity.Data.SchoolContext context)
        {
            _context = context;
        }

        public IActionResult OnGet()
        {
            PopulateDepartmentsDropDownList(_context);
            return Page();
        }

        [BindProperty]
        public Course Course { get; set; }

        public async Task<IActionResult> OnPostAsync()
        {
            var emptyCourse = new Course();

            if (await TryUpdateModelAsync<Course>(
                 emptyCourse,
                 "course",   // Prefix for form value.
                 s => s.CourseID, s => s.DepartmentID, s => s.Title, s => s.Credits))
            {
                _context.Courses.Add(emptyCourse);
                await _context.SaveChangesAsync();
                return RedirectToPage("./Index");
            }

            // Select DepartmentID if TryUpdateModelAsync fails.
            PopulateDepartmentsDropDownList(_context, emptyCourse.DepartmentID);
            return Page();
        }
      }
}

Pokud chcete zobrazit komentáře ke kódu přeložené do jiných jazyků, než je angličtina, dejte nám vědět v této diskuzi na GitHubu.

Předchozí kód:

  • Odvozuje z DepartmentNamePageModel.
  • Slouží TryUpdateModelAsync k zabránění nadměrnému umístění.
  • Odebere ViewData["DepartmentID"]. DepartmentNameSL ze základní třídy je model silného typu a bude používán stránkou Razor . Modely silného typu jsou upřednostňované před slabě typovanými modely. Další informace naleznete v tématu Slabě typovaná data (ViewData a ViewBag).

Aktualizace stránky Vytvoření Razor kurzu

Aktualizujte Pages/Courses/Create.cshtml následujícím kódem:

@page
@model ContosoUniversity.Pages.Courses.CreateModel
@{
    ViewData["Title"] = "Create Course";
}
<h2>Create</h2>
<h4>Course</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form method="post">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="Course.CourseID" class="control-label"></label>
                <input asp-for="Course.CourseID" class="form-control" />
                <span asp-validation-for="Course.CourseID" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Course.Title" class="control-label"></label>
                <input asp-for="Course.Title" class="form-control" />
                <span asp-validation-for="Course.Title" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Course.Credits" class="control-label"></label>
                <input asp-for="Course.Credits" class="form-control" />
                <span asp-validation-for="Course.Credits" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Course.Department" class="control-label"></label>
                <select asp-for="Course.DepartmentID" class="form-control"
                        asp-items="@Model.DepartmentNameSL">
                    <option value="">-- Select Department --</option>
                </select>
                <span asp-validation-for="Course.DepartmentID" class="text-danger" />
            </div>
            <div class="form-group">
                <input type="submit" value="Create" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>
<div>
    <a asp-page="Index">Back to List</a>
</div>
@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

Předchozí kód provede následující změny:

  • Změní titulek z DepartmentID na Department.
  • "ViewBag.DepartmentID" Nahradí za DepartmentNameSL (ze základní třídy).
  • Přidá možnost Vybrat oddělení. Tato změna v rozevíracím seznamu vykreslí možnost Vybrat oddělení, pokud ještě není vybráno žádné oddělení, a ne v prvním oddělení.
  • Přidá ověřovací zprávu, když oddělení není vybrané.

Stránka Razor používá pomocníka pro výběr značek:

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

Otestujte stránku Vytvořit. Na stránce Vytvořit se místo ID oddělení zobrazí název oddělení.

Aktualizace modelu stránky Upravit kurz

Aktualizujte Pages/Courses/Edit.cshtml.cs následujícím kódem:

using ContosoUniversity.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System.Threading.Tasks;

namespace ContosoUniversity.Pages.Courses
{
    public class EditModel : DepartmentNamePageModel
    {
        private readonly ContosoUniversity.Data.SchoolContext _context;

        public EditModel(ContosoUniversity.Data.SchoolContext context)
        {
            _context = context;
        }

        [BindProperty]
        public Course Course { get; set; }

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

            Course = await _context.Courses
                .Include(c => c.Department).FirstOrDefaultAsync(m => m.CourseID == id);

            if (Course == null)
            {
                return NotFound();
            }

            // Select current DepartmentID.
            PopulateDepartmentsDropDownList(_context, Course.DepartmentID);
            return Page();
        }

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

            var courseToUpdate = await _context.Courses.FindAsync(id);

            if (courseToUpdate == null)
            {
                return NotFound();
            }

            if (await TryUpdateModelAsync<Course>(
                 courseToUpdate,
                 "course",   // Prefix for form value.
                   c => c.Credits, c => c.DepartmentID, c => c.Title))
            {
                await _context.SaveChangesAsync();
                return RedirectToPage("./Index");
            }

            // Select DepartmentID if TryUpdateModelAsync fails.
            PopulateDepartmentsDropDownList(_context, courseToUpdate.DepartmentID);
            return Page();
        }       
    }
}

Změny se podobají změnám provedeným v modelu vytvořit stránku. V předchozím kódu PopulateDepartmentsDropDownList předá ID oddělení, které toto oddělení vybere v rozevíracím seznamu.

Aktualizace stránky Upravit Razor kurz

Aktualizujte Pages/Courses/Edit.cshtml následujícím kódem:

@page
@model ContosoUniversity.Pages.Courses.EditModel

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

<h2>Edit</h2>

<h4>Course</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form method="post">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <input type="hidden" asp-for="Course.CourseID" />
            <div class="form-group">
                <label asp-for="Course.CourseID" class="control-label"></label>
                <div>@Html.DisplayFor(model => model.Course.CourseID)</div>
            </div>
            <div class="form-group">
                <label asp-for="Course.Title" class="control-label"></label>
                <input asp-for="Course.Title" class="form-control" />
                <span asp-validation-for="Course.Title" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Course.Credits" class="control-label"></label>
                <input asp-for="Course.Credits" class="form-control" />
                <span asp-validation-for="Course.Credits" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Course.Department" class="control-label"></label>
                <select asp-for="Course.DepartmentID" class="form-control" 
                        asp-items="@Model.DepartmentNameSL"></select>
                <span asp-validation-for="Course.DepartmentID" class="text-danger"></span>
            </div>
            <div class="form-group">
                <input type="submit" value="Save" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>

<div>
    <a asp-page="./Index">Back to List</a>
</div>

@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

Předchozí kód provede následující změny:

  • Zobrazí ID kurzu. Obecně se nezobrazuje primární klíč (PK) entity. Sady PKS jsou obvykle pro uživatele nesmyslné. V tomto případě je PK číslo kurzu.
  • Změní titulek rozevíracího seznamu Oddělení z ID oddělení na Oddělení.
  • "ViewBag.DepartmentID" Nahradí za DepartmentNameSL (ze základní třídy).

Stránka obsahuje skryté pole (<input type="hidden">) pro číslo kurzu. <label> Přidání pomocné rutiny asp-for="Course.CourseID" značek neodstraní potřebu skrytého pole. <input type="hidden"> je vyžadováno, aby číslo kurzu bylo zahrnuto do publikovaných dat, když uživatel klikne na Uložit.

Aktualizace podrobností kurzu a odstranění stránek

AsNoTracking může zvýšit výkon, pokud sledování není vyžadováno.

Aktualizace modelů stránek kurzu

Aktualizujte Pages/Courses/Delete.cshtml.cs následujícím kódem, který chcete přidat AsNoTracking:

using ContosoUniversity.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using System.Threading.Tasks;

namespace ContosoUniversity.Pages.Courses
{
    public class DeleteModel : PageModel
    {
        private readonly ContosoUniversity.Data.SchoolContext _context;

        public DeleteModel(ContosoUniversity.Data.SchoolContext context)
        {
            _context = context;
        }

        [BindProperty]
        public Course Course { get; set; }

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

            Course = await _context.Courses
                .AsNoTracking()
                .Include(c => c.Department)
                .FirstOrDefaultAsync(m => m.CourseID == id);

            if (Course == null)
            {
                return NotFound();
            }
            return Page();
        }

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

            Course = await _context.Courses.FindAsync(id);

            if (Course != null)
            {
                _context.Courses.Remove(Course);
                await _context.SaveChangesAsync();
            }

            return RedirectToPage("./Index");
        }
    }
}

Proveďte stejnou změnu v Pages/Courses/Details.cshtml.cs souboru:

using ContosoUniversity.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using System.Threading.Tasks;

namespace ContosoUniversity.Pages.Courses
{
    public class DetailsModel : PageModel
    {
        private readonly ContosoUniversity.Data.SchoolContext _context;

        public DetailsModel(ContosoUniversity.Data.SchoolContext context)
        {
            _context = context;
        }

        public Course Course { get; set; }

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

            Course = await _context.Courses
                 .AsNoTracking()
                 .Include(c => c.Department)
                 .FirstOrDefaultAsync(m => m.CourseID == id);

            if (Course == null)
            {
                return NotFound();
            }
            return Page();
        }
    }
}

Aktualizace stránek kurzu Razor

Aktualizujte Pages/Courses/Delete.cshtml následujícím kódem:

@page
@model ContosoUniversity.Pages.Courses.DeleteModel

@{
    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.Course.CourseID)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Course.CourseID)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Course.Title)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Course.Title)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Course.Credits)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Course.Credits)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Course.Department)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Course.Department.Name)
        </dd>
    </dl>
    
    <form method="post">
        <input type="hidden" asp-for="Course.CourseID" />
        <input type="submit" value="Delete" class="btn btn-danger" /> |
        <a asp-page="./Index">Back to List</a>
    </form>
</div>

Proveďte stejné změny na stránce Podrobnosti.

@page
@model ContosoUniversity.Pages.Courses.DetailsModel

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

<h2>Details</h2>

<div>
    <h4>Course</h4>
    <hr />
    <dl class="row">
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Course.CourseID)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Course.CourseID)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Course.Title)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Course.Title)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Course.Credits)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Course.Credits)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Course.Department)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Course.Department.Name)
        </dd>
    </dl>
</div>
<div>
    <a asp-page="./Edit" asp-route-id="@Model.Course.CourseID">Edit</a> |
    <a asp-page="./Index">Back to List</a>
</div>

Testování stránek kurzu

Otestujte stránky pro vytvoření, úpravy, podrobnosti a odstranění.

Aktualizace stránek pro vytváření a úpravy instruktora

Instruktori mohou učit libovolný počet kurzů. Následující obrázek ukazuje stránku pro úpravy instruktora s polem zaškrtávacích políček kurzu.

Stránka Pro úpravy instruktora s kurzy

Zaškrtávací políčka umožňují změny kurzů, ke kterým je instruktor přiřazený. Zaškrtávací políčko se zobrazí pro každý kurz v databázi. Jsou vybrány kurzy, ke kterým je instruktor přiřazený. Uživatel může zaškrtnutím nebo zrušením zaškrtnutí políček změnit zadání kurzu. Pokud byl počet kurzů mnohem větší, může lepší fungovat jiné uživatelské rozhraní. Metoda správy relace M:N se zde ale nezměnila. Pokud chcete vytvořit nebo odstranit relace, manipulujete s entitou spojení.

Vytvoření třídy pro přiřazená data kurzů

Vytvořte Models/SchoolViewModels/AssignedCourseData.cs pomocí následujícího kódu:

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

Třída AssignedCourseData obsahuje data pro vytvoření zaškrtávacích políček pro kurzy přiřazené instruktorovi.

Vytvoření základní třídy modelu stránky instruktora

Pages/Instructors/InstructorCoursesPageModel.cs Vytvořte základní třídu:

using ContosoUniversity.Data;
using ContosoUniversity.Models;
using ContosoUniversity.Models.SchoolViewModels;
using Microsoft.AspNetCore.Mvc.RazorPages;
using System.Collections.Generic;
using System.Linq;

namespace ContosoUniversity.Pages.Instructors
{
    public class InstructorCoursesPageModel : PageModel
    {

        public List<AssignedCourseData> AssignedCourseDataList;

        public void PopulateAssignedCourseData(SchoolContext context, 
                                               Instructor instructor)
        {
            var allCourses = context.Courses;
            var instructorCourses = new HashSet<int>(
                instructor.CourseAssignments.Select(c => c.CourseID));
            AssignedCourseDataList = new List<AssignedCourseData>();
            foreach (var course in allCourses)
            {
                AssignedCourseDataList.Add(new AssignedCourseData
                {
                    CourseID = course.CourseID,
                    Title = course.Title,
                    Assigned = instructorCourses.Contains(course.CourseID)
                });
            }
        }

        public void UpdateInstructorCourses(SchoolContext context, 
            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
                                .SingleOrDefault(i => i.CourseID == course.CourseID);
                        context.Remove(courseToRemove);
                    }
                }
            }
        }
    }
}

Jedná se InstructorCoursesPageModel o základní třídu, kterou použijete pro modely stránek Edit a Create. PopulateAssignedCourseData přečte všechny Course entity, které se mají naplnit AssignedCourseDataList. Pro každý kurz kód nastaví CourseID, titul a zda je instruktor přiřazen k kurzu. Sada hash se používá k efektivnímu vyhledávání.

Razor Vzhledem k tomu, že stránka 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í s deklarovanými vlastnostmi a CourseAssignments není v seznamu zahrnutí.

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

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

Kód pak projde všemi kurzy v databázi a zkontroluje každý kurz proti kurzům, které jsou aktuálně přiřazené instruktorovi, a ty, které byly vybrány na stránce. 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.

if (selectedCoursesHS.Contains(course.CourseID.ToString()))
{
    if (!instructorCourses.Contains(course.CourseID))
    {
        instructorToUpdate.CourseAssignments.Add(
            new CourseAssignment
            {
                InstructorID = instructorToUpdate.ID,
                CourseID = course.CourseID
            });
    }
}

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

else
{
    if (instructorCourses.Contains(course.CourseID))
    {
        CourseAssignment courseToRemove
            = instructorToUpdate
                .CourseAssignments
                .SingleOrDefault(i => i.CourseID == course.CourseID);
        context.Remove(courseToRemove);
    }
}

Zpracování umístění kanceláře

Další relací, kterou musí stránka pro úpravy zpracovat, je relace 1:0 nebo 1, kterou má entita Instruktor s entitou OfficeAssignment . Kód pro úpravy instruktora musí zpracovávat následující scénáře:

  • Pokud uživatel vymaže přiřazení kanceláře, odstraňte entitu OfficeAssignment .
  • Pokud uživatel zadá přiřazení kanceláře a bude prázdný, vytvořte novou OfficeAssignment entitu.
  • Pokud uživatel změní přiřazení kanceláře, aktualizujte entitu OfficeAssignment .

Aktualizace modelu stránky Pro úpravy instruktora

Aktualizujte Pages/Instructors/Edit.cshtml.cs následujícím kódem:

using ContosoUniversity.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System;
using System.Threading.Tasks;

namespace ContosoUniversity.Pages.Instructors
{
    public class EditModel : InstructorCoursesPageModel
    {
        private readonly ContosoUniversity.Data.SchoolContext _context;

        public EditModel(ContosoUniversity.Data.SchoolContext context)
        {
            _context = context;
        }

        [BindProperty]
        public Instructor Instructor { get; set; }

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

            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(_context, Instructor);
            return Page();
        }

        public async Task<IActionResult> OnPostAsync(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(s => s.ID == id);

            if (instructorToUpdate == null)
            {
                return NotFound();
            }

            if (await TryUpdateModelAsync<Instructor>(
                instructorToUpdate,
                "Instructor",
                i => i.FirstMidName, i => i.LastName,
                i => i.HireDate, i => i.OfficeAssignment))
            {
                if (String.IsNullOrWhiteSpace(
                    instructorToUpdate.OfficeAssignment?.Location))
                {
                    instructorToUpdate.OfficeAssignment = null;
                }
                UpdateInstructorCourses(_context, selectedCourses, instructorToUpdate);
                await _context.SaveChangesAsync();
                return RedirectToPage("./Index");
            }
            UpdateInstructorCourses(_context, selectedCourses, instructorToUpdate);
            PopulateAssignedCourseData(_context, instructorToUpdate);
            return Page();
        }
    }
}

Předchozí kód:

  • Získá aktuální Instructor entitu z databáze pomocí dychtivého načítání pro OfficeAssignmentvlastnosti , CourseAssignmenta CourseAssignment.Course navigace.
  • Aktualizuje načtenou Instructor entitu hodnotami z pořadače modelu. TryUpdateModel zabraňuje nadměrnému umístění.
  • Pokud je umístění kanceláře prázdné, nastaví Instructor.OfficeAssignment se na hodnotu null. Pokud Instructor.OfficeAssignment je null, související řádek v OfficeAssignment tabulce se odstraní.
  • Volání PopulateAssignedCourseData za účelem poskytnutí informací o zaškrtávacích polících pomocí AssignedCourseData třídy OnGetAsync modelu zobrazení
  • OnPostAsync VoláníUpdateInstructorCourses, která použijí informace z zaškrtávacích políček na entitu instruktora, která se upravuje.
  • Volání PopulateAssignedCourseData a UpdateInstructorCourses přihlášení OnPostAsync , pokud TryUpdateModel selže. Tato metoda volá obnovení přiřazených dat kurzu zadaných na stránce při opětovném zobrazení s chybovou zprávou.

Aktualizace stránky Pro úpravy Razor instruktora

Aktualizujte Pages/Instructors/Edit.cshtml následujícím kódem:

@page
@model ContosoUniversity.Pages.Instructors.EditModel
@{
    ViewData["Title"] = "Edit";
}
<h2>Edit</h2>
<h4>Instructor</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form method="post">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <input type="hidden" asp-for="Instructor.ID" />
            <div class="form-group">
                <label asp-for="Instructor.LastName" class="control-label"></label>
                <input asp-for="Instructor.LastName" class="form-control" />
                <span asp-validation-for="Instructor.LastName" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Instructor.FirstMidName" class="control-label"></label>
                <input asp-for="Instructor.FirstMidName" class="form-control" />
                <span asp-validation-for="Instructor.FirstMidName" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Instructor.HireDate" class="control-label"></label>
                <input asp-for="Instructor.HireDate" class="form-control" />
                <span asp-validation-for="Instructor.HireDate" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Instructor.OfficeAssignment.Location" class="control-label"></label>
                <input asp-for="Instructor.OfficeAssignment.Location" class="form-control" />
                <span asp-validation-for="Instructor.OfficeAssignment.Location" class="text-danger" />
            </div>
            <div class="form-group">
                <div class="table">
                    <table>
                        <tr>
                            @{
                                int cnt = 0;

                                foreach (var course in Model.AssignedCourseDataList)
                                {
                                    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>
            <div class="form-group">
                <input type="submit" value="Save" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>

<div>
    <a asp-page="./Index">Back to List</a>
</div>

@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

Předchozí kód vytvoří tabulku HTML se třemi sloupci. Každý sloupec má zaškrtávací políčko a titulek obsahující číslo a název kurzu. Všechna zaškrtávací políčka mají stejný název ("selectedCourses"). Použití stejného názvu informuje pořadač modelu, aby s nimi zacházal jako se skupinou. Atribut hodnoty každého zaškrtávacího políčka je nastaven na CourseIDhodnotu . Když se stránka publikuje, pořadač modelu předá pole, které se skládá z CourseID hodnot pouze pro zaškrtnutá políčka.

Po počátečním vykreslení zaškrtávacích políček se vyberou kurzy přiřazené instruktorovi.

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 bylo vhodnější a efektivnější použít jiné uživatelské rozhraní a jinou metodu aktualizace.

Spusťte aplikaci a otestujte aktualizovanou stránku Pro úpravy instruktorů. Změňte některá zadání kurzu. Změny se projeví na stránce rejstříku.

Aktualizace stránky Pro vytvoření instruktora

Aktualizujte model stránky Razor a stránku pro vytvoření instruktora pomocí kódu podobného stránce Upravit:

using ContosoUniversity.Models;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace ContosoUniversity.Pages.Instructors
{
    public class CreateModel : InstructorCoursesPageModel
    {
        private readonly ContosoUniversity.Data.SchoolContext _context;

        public CreateModel(ContosoUniversity.Data.SchoolContext context)
        {
            _context = context;
        }

        public IActionResult OnGet()
        {
            var instructor = new Instructor();
            instructor.CourseAssignments = new List<CourseAssignment>();

            // Provides an empty collection for the foreach loop
            // foreach (var course in Model.AssignedCourseDataList)
            // in the Create Razor page.
            PopulateAssignedCourseData(_context, instructor);
            return Page();
        }

        [BindProperty]
        public Instructor Instructor { get; set; }

        public async Task<IActionResult> OnPostAsync(string[] selectedCourses)
        {
            var newInstructor = new Instructor();
            if (selectedCourses != null)
            {
                newInstructor.CourseAssignments = new List<CourseAssignment>();
                foreach (var course in selectedCourses)
                {
                    var courseToAdd = new CourseAssignment
                    {
                        CourseID = int.Parse(course)
                    };
                    newInstructor.CourseAssignments.Add(courseToAdd);
                }
            }

            if (await TryUpdateModelAsync<Instructor>(
                newInstructor,
                "Instructor",
                i => i.FirstMidName, i => i.LastName,
                i => i.HireDate, i => i.OfficeAssignment))
            {
                _context.Instructors.Add(newInstructor);                
                await _context.SaveChangesAsync();
                return RedirectToPage("./Index");
            }
            PopulateAssignedCourseData(_context, newInstructor);
            return Page();
        }
    }
}
@page
@model ContosoUniversity.Pages.Instructors.CreateModel

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

<h2>Create</h2>

<h4>Instructor</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form method="post">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="Instructor.LastName" class="control-label"></label>
                <input asp-for="Instructor.LastName" class="form-control" />
                <span asp-validation-for="Instructor.LastName" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Instructor.FirstMidName" class="control-label"></label>
                <input asp-for="Instructor.FirstMidName" class="form-control" />
                <span asp-validation-for="Instructor.FirstMidName" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Instructor.HireDate" class="control-label"></label>
                <input asp-for="Instructor.HireDate" class="form-control" />
                <span asp-validation-for="Instructor.HireDate" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Instructor.OfficeAssignment.Location" class="control-label"></label>
                <input asp-for="Instructor.OfficeAssignment.Location" class="form-control" />
                <span asp-validation-for="Instructor.OfficeAssignment.Location" class="text-danger" />
            </div>
            <div class="form-group">
                <div class="table">
                    <table>
                        <tr>
                            @{
                                int cnt = 0;

                                foreach (var course in Model.AssignedCourseDataList)
                                {
                                    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>
            <div class="form-group">
                <input type="submit" value="Create" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>

<div>
    <a asp-page="Index">Back to List</a>
</div>

@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

Otestujte stránku pro vytvoření instruktora.

Aktualizace stránky Pro odstranění instruktora

Aktualizujte Pages/Instructors/Delete.cshtml.cs následujícím kódem:

using ContosoUniversity.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using System.Linq;
using System.Threading.Tasks;

namespace ContosoUniversity.Pages.Instructors
{
    public class DeleteModel : PageModel
    {
        private readonly ContosoUniversity.Data.SchoolContext _context;

        public DeleteModel(ContosoUniversity.Data.SchoolContext context)
        {
            _context = context;
        }

        [BindProperty]
        public Instructor Instructor { get; set; }

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

            Instructor = await _context.Instructors.FirstOrDefaultAsync(m => m.ID == id);

            if (Instructor == null)
            {
                return NotFound();
            }
            return Page();
        }

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

            Instructor instructor = await _context.Instructors
                .Include(i => i.CourseAssignments)
                .SingleAsync(i => i.ID == id);

            if (instructor == null)
            {
                return RedirectToPage("./Index");
            }

            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 RedirectToPage("./Index");
        }
    }
}

Předchozí kód provede následující změny:

  • Používá dychtivé načítání pro CourseAssignments navigační vlastnost. CourseAssignments musí být zahrnuté nebo se při odstranění instruktora neodstraní. Abyste se vyhnuli nutnosti je číst, nakonfigurujte kaskádové odstranění v databázi.

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

Spusťte aplikaci a otestujte stránku Odstranit.

Další kroky

Tento kurz ukazuje aktualizaci souvisejících dat. Pokud narazíte na problémy, které nemůžete vyřešit, stáhněte nebo zobrazte dokončenou aplikaci. Pokyny ke stažení

Následující ilustrace znázorňují některé dokončené stránky.

Stránka Upravit kurzStránka Pro úpravy instruktora

Prozkoumejte a otestujte stránky kurzu Pro vytvoření a úpravy. Vytvořte nový kurz. Oddělení je vybráno primárním klíčem (celé číslo), nikoli jeho názvem. Upravte nový kurz. Po dokončení testování odstraňte nový kurz.

Vytvoření základní třídy pro sdílení společného kódu

Stránky Courses/Create and Courses/Edit potřebují seznam názvů oddělení. Pages/Courses/DepartmentNamePageModel.cshtml.cs Vytvořte základní třídu pro stránky Create and Edit:

using ContosoUniversity.Data;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.EntityFrameworkCore;
using System.Linq;

namespace ContosoUniversity.Pages.Courses
{
    public class DepartmentNamePageModel : PageModel
    {
        public SelectList DepartmentNameSL { get; set; }

        public void PopulateDepartmentsDropDownList(SchoolContext _context,
            object selectedDepartment = null)
        {
            var departmentsQuery = from d in _context.Departments
                                   orderby d.Name // Sort by name.
                                   select d;

            DepartmentNameSL = new SelectList(departmentsQuery.AsNoTracking(),
                        "DepartmentID", "Name", selectedDepartment);
        }
    }
}

Předchozí kód vytvoří seznam SelectList názvů oddělení. Je-li selectedDepartment zadáno, je toto oddělení vybráno v sadě SelectList.

Třídy modelu vytvoření a úpravy stránky budou odvozeny z DepartmentNamePageModel.

Přizpůsobení stránek kurzů

Když se vytvoří nová entita kurzu, musí mít vztah k existujícímu oddělení. Pokud chcete přidat oddělení při vytváření kurzu, základní třída pro vytvoření a úpravy obsahuje rozevírací seznam pro výběr oddělení. Rozevírací seznam nastaví vlastnost cizího Course.DepartmentID klíče (FK). EF CoreCourse.DepartmentID používá jazyk FK k načtení Department navigační vlastnosti.

Vytvoření kurzu

Aktualizujte model vytvořit stránku následujícím kódem:

using ContosoUniversity.Models;
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;

namespace ContosoUniversity.Pages.Courses
{
    public class CreateModel : DepartmentNamePageModel
    {
        private readonly ContosoUniversity.Data.SchoolContext _context;

        public CreateModel(ContosoUniversity.Data.SchoolContext context)
        {
            _context = context;
        }

        public IActionResult OnGet()
        {
            PopulateDepartmentsDropDownList(_context);
            return Page();
        }

        [BindProperty]
        public Course Course { get; set; }

        public async Task<IActionResult> OnPostAsync()
        {
            if (!ModelState.IsValid)
            {
                return Page();
            }

            var emptyCourse = new Course();

            if (await TryUpdateModelAsync<Course>(
                 emptyCourse,
                 "course",   // Prefix for form value.
                 s => s.CourseID, s => s.DepartmentID, s => s.Title, s => s.Credits))
            {
                _context.Courses.Add(emptyCourse);
                await _context.SaveChangesAsync();
                return RedirectToPage("./Index");
            }

            // Select DepartmentID if TryUpdateModelAsync fails.
            PopulateDepartmentsDropDownList(_context, emptyCourse.DepartmentID);
            return Page();
        }
      }
}

Předchozí kód:

  • Odvozuje z DepartmentNamePageModel.
  • Slouží TryUpdateModelAsync k zabránění nadměrnému umístění.
  • ViewData["DepartmentID"] Nahradí za DepartmentNameSL (ze základní třídy).

ViewData["DepartmentID"] je nahrazen silným typem DepartmentNameSL. Modely silného typu jsou upřednostňované před slabě typovanými modely. Další informace naleznete v tématu Slabě typovaná data (ViewData a ViewBag).

Aktualizace stránky Vytvoření kurzů

Aktualizujte Pages/Courses/Create.cshtml následujícím kódem:

@page
@model ContosoUniversity.Pages.Courses.CreateModel
@{
    ViewData["Title"] = "Create Course";
}
<h2>Create</h2>
<h4>Course</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form method="post">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="Course.CourseID" class="control-label"></label>
                <input asp-for="Course.CourseID" class="form-control" />
                <span asp-validation-for="Course.CourseID" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Course.Title" class="control-label"></label>
                <input asp-for="Course.Title" class="form-control" />
                <span asp-validation-for="Course.Title" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Course.Credits" class="control-label"></label>
                <input asp-for="Course.Credits" class="form-control" />
                <span asp-validation-for="Course.Credits" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Course.Department" class="control-label"></label>
                <select asp-for="Course.DepartmentID" class="form-control"
                        asp-items="@Model.DepartmentNameSL">
                    <option value="">-- Select Department --</option>
                </select>
                <span asp-validation-for="Course.DepartmentID" class="text-danger" />
            </div>
            <div class="form-group">
                <input type="submit" value="Create" class="btn btn-default" />
            </div>
        </form>
    </div>
</div>
<div>
    <a asp-page="Index">Back to List</a>
</div>
@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

Předchozí kód provede následující změny:

  • Změní titulek z DepartmentID na Department.
  • "ViewBag.DepartmentID" Nahradí za DepartmentNameSL (ze základní třídy).
  • Přidá možnost Vybrat oddělení. Tato změna místo prvního oddělení vykreslí možnost Vybrat oddělení.
  • Přidá ověřovací zprávu, když oddělení není vybrané.

Stránka Razor používá pomocníka pro výběr značek:

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

Otestujte stránku Vytvořit. Na stránce Vytvořit se místo ID oddělení zobrazí název oddělení.

Aktualizujte stránku Úpravy kurzů.

Nahraďte kód v souboru Pages/Courses/Edit.cshtml.cs následujícím kódem:

using ContosoUniversity.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System.Threading.Tasks;

namespace ContosoUniversity.Pages.Courses
{
    public class EditModel : DepartmentNamePageModel
    {
        private readonly ContosoUniversity.Data.SchoolContext _context;

        public EditModel(ContosoUniversity.Data.SchoolContext context)
        {
            _context = context;
        }

        [BindProperty]
        public Course Course { get; set; }

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

            Course = await _context.Courses
                .Include(c => c.Department).FirstOrDefaultAsync(m => m.CourseID == id);

            if (Course == null)
            {
                return NotFound();
            }

            // Select current DepartmentID.
            PopulateDepartmentsDropDownList(_context,Course.DepartmentID);
            return Page();
        }

        public async Task<IActionResult> OnPostAsync(int? id)
        {
            if (!ModelState.IsValid)
            {
                return Page();
            }

            var courseToUpdate = await _context.Courses.FindAsync(id);

            if (await TryUpdateModelAsync<Course>(
                 courseToUpdate,
                 "course",   // Prefix for form value.
                   c => c.Credits, c => c.DepartmentID, c => c.Title))
            {
                await _context.SaveChangesAsync();
                return RedirectToPage("./Index");
            }

            // Select DepartmentID if TryUpdateModelAsync fails.
            PopulateDepartmentsDropDownList(_context, courseToUpdate.DepartmentID);
            return Page();
        }       
    }
}

Změny se podobají změnám provedeným v modelu vytvořit stránku. V předchozím kódu PopulateDepartmentsDropDownList předá ID oddělení, které vybere oddělení zadané v rozevíracím seznamu.

Aktualizujte Pages/Courses/Edit.cshtml následujícím kódem:

@page
@model ContosoUniversity.Pages.Courses.EditModel

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

<h2>Edit</h2>

<h4>Course</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form method="post">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <input type="hidden" asp-for="Course.CourseID" />
            <div class="form-group">
                <label asp-for="Course.CourseID" class="control-label"></label>
                <div>@Html.DisplayFor(model => model.Course.CourseID)</div>
            </div>
            <div class="form-group">
                <label asp-for="Course.Title" class="control-label"></label>
                <input asp-for="Course.Title" class="form-control" />
                <span asp-validation-for="Course.Title" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Course.Credits" class="control-label"></label>
                <input asp-for="Course.Credits" class="form-control" />
                <span asp-validation-for="Course.Credits" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Course.Department" class="control-label"></label>
                <select asp-for="Course.DepartmentID" class="form-control" 
                        asp-items="@Model.DepartmentNameSL"></select>
                <span asp-validation-for="Course.DepartmentID" class="text-danger"></span>
            </div>
            <div class="form-group">
                <input type="submit" value="Save" class="btn btn-default" />
            </div>
        </form>
    </div>
</div>

<div>
    <a asp-page="./Index">Back to List</a>
</div>

@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

Předchozí kód provede následující změny:

  • Zobrazí ID kurzu. Obecně se nezobrazuje primární klíč (PK) entity. Sady PKS jsou obvykle pro uživatele nesmyslné. V tomto případě je PK číslo kurzu.
  • Změní titulek z DepartmentID na Department.
  • "ViewBag.DepartmentID" Nahradí za DepartmentNameSL (ze základní třídy).

Stránka obsahuje skryté pole (<input type="hidden">) pro číslo kurzu. <label> Přidání pomocné rutiny asp-for="Course.CourseID" značek neodstraní potřebu skrytého pole. <input type="hidden"> je vyžadováno, aby číslo kurzu bylo zahrnuto do publikovaných dat, když uživatel klikne na Uložit.

Otestujte aktualizovaný kód. Vytvoření, úprava a odstranění kurzu

Přidání AsNoTrackingu do modelů podrobností a odstranění stránek

AsNoTracking může zvýšit výkon, pokud sledování není vyžadováno. Přidejte AsNoTracking do modelu stránky Odstranit a Podrobnosti. Následující kód ukazuje aktualizovaný model stránky Odstranit:

public class DeleteModel : PageModel
{
    private readonly ContosoUniversity.Data.SchoolContext _context;

    public DeleteModel(ContosoUniversity.Data.SchoolContext context)
    {
        _context = context;
    }

    [BindProperty]
    public Course Course { get; set; }

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

        Course = await _context.Courses
            .AsNoTracking()
            .Include(c => c.Department)
            .FirstOrDefaultAsync(m => m.CourseID == id);

        if (Course == null)
        {
            return NotFound();
        }
        return Page();
    }

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

        Course = await _context.Courses
            .AsNoTracking()
            .FirstOrDefaultAsync(m => m.CourseID == id);

        if (Course != null)
        {
            _context.Courses.Remove(Course);
            await _context.SaveChangesAsync();
        }

        return RedirectToPage("./Index");
    }
}

Aktualizujte metodu OnGetAsync Pages/Courses/Details.cshtml.cs v souboru:

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

    Course = await _context.Courses
         .AsNoTracking()
         .Include(c => c.Department)
         .FirstOrDefaultAsync(m => m.CourseID == id);

    if (Course == null)
    {
        return NotFound();
    }
    return Page();
}

Úprava stránek Odstranit a Podrobnosti

Aktualizujte stránku Odstranit Razor následujícím kódem:

@page
@model ContosoUniversity.Pages.Courses.DeleteModel

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

<h2>Delete</h2>

<h3>Are you sure you want to delete this?</h3>
<div>
    <h4>Course</h4>
    <hr />
    <dl class="dl-horizontal">
        <dt>
            @Html.DisplayNameFor(model => model.Course.CourseID)
        </dt>
        <dd>
            @Html.DisplayFor(model => model.Course.CourseID)
        </dd>
        <dt>
            @Html.DisplayNameFor(model => model.Course.Title)
        </dt>
        <dd>
            @Html.DisplayFor(model => model.Course.Title)
        </dd>
        <dt>
            @Html.DisplayNameFor(model => model.Course.Credits)
        </dt>
        <dd>
            @Html.DisplayFor(model => model.Course.Credits)
        </dd>
        <dt>
            @Html.DisplayNameFor(model => model.Course.Department)
        </dt>
        <dd>
            @Html.DisplayFor(model => model.Course.Department.DepartmentID)
        </dd>
    </dl>
    
    <form method="post">
        <input type="hidden" asp-for="Course.CourseID" />
        <input type="submit" value="Delete" class="btn btn-default" /> |
        <a asp-page="./Index">Back to List</a>
    </form>
</div>

Proveďte stejné změny na stránce Podrobnosti.

Testování stránek kurzu

Otestujte vytváření, úpravy, podrobnosti a odstranění.

Aktualizace stránek instruktora

Následující části aktualizují stránky instruktora.

Přidání umístění kanceláře

Při úpravě záznamu instruktora můžete chtít aktualizovat zadání kanceláře instruktora. Entita Instructor má relaci 1:0 nebo 1 s entitou OfficeAssignment . Kód instruktora musí zpracovávat:

  • Pokud uživatel vymaže přiřazení kanceláře, odstraňte entitu OfficeAssignment .
  • Pokud uživatel zadá přiřazení kanceláře a bude prázdný, vytvořte novou OfficeAssignment entitu.
  • Pokud uživatel změní přiřazení kanceláře, aktualizujte entitu OfficeAssignment .

Aktualizujte model stránek pro úpravy instruktorů následujícím kódem:

public class EditModel : PageModel
{
    private readonly ContosoUniversity.Data.SchoolContext _context;

    public EditModel(ContosoUniversity.Data.SchoolContext context)
    {
        _context = context;
    }

    [BindProperty]
    public Instructor Instructor { get; set; }

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

        Instructor = await _context.Instructors
            .Include(i => i.OfficeAssignment)
            .AsNoTracking()
            .FirstOrDefaultAsync(m => m.ID == id);

        if (Instructor == null)
        {
            return NotFound();
        }
        return Page();
    }

    public async Task<IActionResult> OnPostAsync(int? id)
    {
        if (!ModelState.IsValid)
        {
            return Page();
        }

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

        if (await TryUpdateModelAsync<Instructor>(
            instructorToUpdate,
            "Instructor",
            i => i.FirstMidName, i => i.LastName, 
            i => i.HireDate, i => i.OfficeAssignment))
        {
            if (String.IsNullOrWhiteSpace(
                instructorToUpdate.OfficeAssignment?.Location))
            {
                instructorToUpdate.OfficeAssignment = null;
            }
            await _context.SaveChangesAsync();
        }
        return RedirectToPage("./Index");

    }
}

Předchozí kód:

  • Získá aktuální Instructor entitu z databáze pomocí dychtivého načítání pro OfficeAssignment navigační vlastnost.
  • Aktualizuje načtenou Instructor entitu hodnotami z pořadače modelu. TryUpdateModel zabraňuje nadměrnému umístění.
  • Pokud je umístění kanceláře prázdné, nastaví Instructor.OfficeAssignment se na hodnotu null. Pokud Instructor.OfficeAssignment je null, související řádek v OfficeAssignment tabulce se odstraní.

Aktualizace stránky pro úpravy instruktora

Aktualizujte Pages/Instructors/Edit.cshtml umístěním kanceláře:

@page
@model ContosoUniversity.Pages.Instructors.EditModel
@{
    ViewData["Title"] = "Edit";
}
<h2>Edit</h2>
<h4>Instructor</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form method="post">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <input type="hidden" asp-for="Instructor.ID" />
            <div class="form-group">
                <label asp-for="Instructor.LastName" class="control-label"></label>
                <input asp-for="Instructor.LastName" class="form-control" />
                <span asp-validation-for="Instructor.LastName" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Instructor.FirstMidName" class="control-label"></label>
                <input asp-for="Instructor.FirstMidName" class="form-control" />
                <span asp-validation-for="Instructor.FirstMidName" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Instructor.HireDate" class="control-label"></label>
                <input asp-for="Instructor.HireDate" class="form-control" />
                <span asp-validation-for="Instructor.HireDate" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Instructor.OfficeAssignment.Location" class="control-label"></label>
                <input asp-for="Instructor.OfficeAssignment.Location" class="form-control" />
                <span asp-validation-for="Instructor.OfficeAssignment.Location" class="text-danger" />
            </div>
            <div class="form-group">
                <input type="submit" value="Save" class="btn btn-default" />
            </div>
        </form>
    </div>
</div>

<div>
    <a asp-page="./Index">Back to List</a>
</div>

@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

Ověřte, že můžete změnit umístění kanceláře instruktora.

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

Instruktori mohou učit libovolný počet kurzů. V této části přidáte možnost změnit zadání kurzu. Na následujícím obrázku je aktualizovaná stránka pro úpravy instruktora:

Stránka Pro úpravy instruktora s kurzy

Course a Instructor má relaci M:N. Pokud chcete přidat a odebrat relace, přidáte a odeberete entity ze CourseAssignments sady entit join.

zaškrtávací políčka umožňují změny kurzů, ke kterým je instruktor přiřazený. Zaškrtávací políčko se zobrazí pro každý kurz v databázi. Kurzy, ke kterým je instruktor přiřazen, jsou kontrolovány. Uživatel může zaškrtnutím nebo zrušením zaškrtnutí políček změnit zadání kurzu. Pokud počet kurzů byl mnohem větší:

  • Pravděpodobně byste k zobrazení kurzů použili jiné uživatelské rozhraní.
  • Metoda manipulace s entitou spojení pro vytvoření nebo odstranění relací by se nezměnila.

Přidání předmětů pro podporu stránek pro vytváření a úpravy instruktorů

Vytvořte Models/SchoolViewModels/AssignedCourseData.cs pomocí následujícího kódu:

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

Třída AssignedCourseData obsahuje data pro vytvoření zaškrtávacích políček pro přiřazené kurzy instruktorem.

Pages/Instructors/InstructorCoursesPageModel.cshtml.cs Vytvořte základní třídu:

using ContosoUniversity.Data;
using ContosoUniversity.Models;
using ContosoUniversity.Models.SchoolViewModels;
using Microsoft.AspNetCore.Mvc.RazorPages;
using System.Collections.Generic;
using System.Linq;

namespace ContosoUniversity.Pages.Instructors
{
    public class InstructorCoursesPageModel : PageModel
    {

        public List<AssignedCourseData> AssignedCourseDataList;

        public void PopulateAssignedCourseData(SchoolContext context, 
                                               Instructor instructor)
        {
            var allCourses = context.Courses;
            var instructorCourses = new HashSet<int>(
                instructor.CourseAssignments.Select(c => c.CourseID));
            AssignedCourseDataList = new List<AssignedCourseData>();
            foreach (var course in allCourses)
            {
                AssignedCourseDataList.Add(new AssignedCourseData
                {
                    CourseID = course.CourseID,
                    Title = course.Title,
                    Assigned = instructorCourses.Contains(course.CourseID)
                });
            }
        }

        public void UpdateInstructorCourses(SchoolContext context, 
            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
                                .SingleOrDefault(i => i.CourseID == course.CourseID);
                        context.Remove(courseToRemove);
                    }
                }
            }
        }
    }
}

Jedná se InstructorCoursesPageModel o základní třídu, kterou použijete pro modely stránek Edit a Create. PopulateAssignedCourseData přečte všechny Course entity, které se mají naplnit AssignedCourseDataList. Pro každý kurz kód nastaví CourseID, titul a zda je instruktor přiřazen k kurzu. Sada hash se používá k vytváření efektivních vyhledávání.

Instruktori – Upravit model stránky

Aktualizujte model stránky pro úpravy instruktora následujícím kódem:

public class EditModel : InstructorCoursesPageModel
{
    private readonly ContosoUniversity.Data.SchoolContext _context;

    public EditModel(ContosoUniversity.Data.SchoolContext context)
    {
        _context = context;
    }

    [BindProperty]
    public Instructor Instructor { get; set; }

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

        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(_context, Instructor);
        return Page();
    }

    public async Task<IActionResult> OnPostAsync(int? id, string[] selectedCourses)
    {
        if (!ModelState.IsValid)
        {
            return Page();
        }

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

        if (await TryUpdateModelAsync<Instructor>(
            instructorToUpdate,
            "Instructor",
            i => i.FirstMidName, i => i.LastName,
            i => i.HireDate, i => i.OfficeAssignment))
        {
            if (String.IsNullOrWhiteSpace(
                instructorToUpdate.OfficeAssignment?.Location))
            {
                instructorToUpdate.OfficeAssignment = null;
            }
            UpdateInstructorCourses(_context, selectedCourses, instructorToUpdate);
            await _context.SaveChangesAsync();
            return RedirectToPage("./Index");
        }
        UpdateInstructorCourses(_context, selectedCourses, instructorToUpdate);
        PopulateAssignedCourseData(_context, instructorToUpdate);
        return Page();
    }
}

Předchozí kód zpracovává změny přiřazení office.

Aktualizujte zobrazení instruktora Razor :

@page
@model ContosoUniversity.Pages.Instructors.EditModel
@{
    ViewData["Title"] = "Edit";
}
<h2>Edit</h2>
<h4>Instructor</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form method="post">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <input type="hidden" asp-for="Instructor.ID" />
            <div class="form-group">
                <label asp-for="Instructor.LastName" class="control-label"></label>
                <input asp-for="Instructor.LastName" class="form-control" />
                <span asp-validation-for="Instructor.LastName" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Instructor.FirstMidName" class="control-label"></label>
                <input asp-for="Instructor.FirstMidName" class="form-control" />
                <span asp-validation-for="Instructor.FirstMidName" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Instructor.HireDate" class="control-label"></label>
                <input asp-for="Instructor.HireDate" class="form-control" />
                <span asp-validation-for="Instructor.HireDate" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Instructor.OfficeAssignment.Location" class="control-label"></label>
                <input asp-for="Instructor.OfficeAssignment.Location" class="form-control" />
                <span asp-validation-for="Instructor.OfficeAssignment.Location" class="text-danger" />
            </div>
            <div class="form-group">
                <div class="col-md-offset-2 col-md-10">
                    <table>
                        <tr>
                            @{
                                int cnt = 0;

                                foreach (var course in Model.AssignedCourseDataList)
                                {
                                    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>
            <div class="form-group">
                <input type="submit" value="Save" class="btn btn-default" />
            </div>
        </form>
    </div>
</div>

<div>
    <a asp-page="./Index">Back to List</a>
</div>

@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

Poznámka:

Když kód vložíte do sady Visual Studio, změní se konce řádků způsobem, který kód přeruší. Automatické formátování vrátíte jedním stisknutím kombinace kláves Ctrl+Z. Kombinace kláves Ctrl+Z opravuje konce řádků tak, aby vypadaly tak, jak vidíte tady. Odsazení nemusí být dokonalé, ale @:</tr><tr>@:<td>@:</td>čáry , a @:</tr> řádky musí být na jednom řádku, jak je znázorněno. 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. Hlasujte nebo zkontrolujte stav této chyby pomocí tohoto odkazu.

Předchozí kód vytvoří tabulku HTML se třemi sloupci. Každý sloupec má zaškrtávací políčko a titulek obsahující číslo a název kurzu. Všechna zaškrtávací políčka mají stejný název ("selectedCourses"). Použití stejného názvu informuje pořadač modelu, aby s nimi zacházal jako se skupinou. Atribut hodnoty každého zaškrtávacího políčka je nastaven na CourseIDhodnotu . Když se stránka publikuje, pořadač modelu předá pole, které se skládá z CourseID hodnot pouze pro zaškrtnutá políčka.

Po počátečním vykreslení zaškrtávacích políček jsou kurzy přiřazené instruktorovi zaškrtnuté atributy.

Spusťte aplikaci a otestujte aktualizovanou stránku pro úpravy instruktorů. Změňte některá zadání kurzu. 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 bylo vhodnější a efektivnější použít jiné uživatelské rozhraní a jinou metodu aktualizace.

Aktualizace stránky pro vytvoření instruktorů

Aktualizujte model stránky pro vytvoření instruktora následujícím kódem:

using ContosoUniversity.Models;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace ContosoUniversity.Pages.Instructors
{
    public class CreateModel : InstructorCoursesPageModel
    {
        private readonly ContosoUniversity.Data.SchoolContext _context;

        public CreateModel(ContosoUniversity.Data.SchoolContext context)
        {
            _context = context;
        }

        public IActionResult OnGet()
        {
            var instructor = new Instructor();
            instructor.CourseAssignments = new List<CourseAssignment>();

            // Provides an empty collection for the foreach loop
            // foreach (var course in Model.AssignedCourseDataList)
            // in the Create Razor page.
            PopulateAssignedCourseData(_context, instructor);
            return Page();
        }

        [BindProperty]
        public Instructor Instructor { get; set; }

        public async Task<IActionResult> OnPostAsync(string[] selectedCourses)
        {
            if (!ModelState.IsValid)
            {
                return Page();
            }

            var newInstructor = new Instructor();
            if (selectedCourses != null)
            {
                newInstructor.CourseAssignments = new List<CourseAssignment>();
                foreach (var course in selectedCourses)
                {
                    var courseToAdd = new CourseAssignment
                    {
                        CourseID = int.Parse(course)
                    };
                    newInstructor.CourseAssignments.Add(courseToAdd);
                }
            }

            if (await TryUpdateModelAsync<Instructor>(
                newInstructor,
                "Instructor",
                i => i.FirstMidName, i => i.LastName,
                i => i.HireDate, i => i.OfficeAssignment))
            {
                _context.Instructors.Add(newInstructor);                
                await _context.SaveChangesAsync();
                return RedirectToPage("./Index");
            }
            PopulateAssignedCourseData(_context, newInstructor);
            return Page();
        }
    }
}

Předchozí kód je podobný Pages/Instructors/Edit.cshtml.cs kódu.

Aktualizujte stránku pro vytvoření Razor instruktora následujícím kódem:

@page
@model ContosoUniversity.Pages.Instructors.CreateModel

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

<h2>Create</h2>

<h4>Instructor</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form method="post">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="Instructor.LastName" class="control-label"></label>
                <input asp-for="Instructor.LastName" class="form-control" />
                <span asp-validation-for="Instructor.LastName" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Instructor.FirstMidName" class="control-label"></label>
                <input asp-for="Instructor.FirstMidName" class="form-control" />
                <span asp-validation-for="Instructor.FirstMidName" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Instructor.HireDate" class="control-label"></label>
                <input asp-for="Instructor.HireDate" class="form-control" />
                <span asp-validation-for="Instructor.HireDate" class="text-danger"></span>
            </div>
            
            <div class="form-group">
                <label asp-for="Instructor.OfficeAssignment.Location" class="control-label"></label>
                <input asp-for="Instructor.OfficeAssignment.Location" class="form-control" />
                <span asp-validation-for="Instructor.OfficeAssignment.Location" class="text-danger" />
            </div>
            <div class="form-group">
                <div class="col-md-offset-2 col-md-10">
                    <table>
                        <tr>
                            @{
                                int cnt = 0;

                                foreach (var course in Model.AssignedCourseDataList)
                                {
                                    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>
            <div class="form-group">
                <input type="submit" value="Create" class="btn btn-default" />
            </div>
        </form>
    </div>
</div>

<div>
    <a asp-page="Index">Back to List</a>
</div>

@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

Otestujte stránku pro vytvoření instruktora.

Aktualizace stránky Odstranit

Aktualizujte model stránky Delete následujícím kódem:

using ContosoUniversity.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using System.Linq;
using System.Threading.Tasks;

namespace ContosoUniversity.Pages.Instructors
{
    public class DeleteModel : PageModel
    {
        private readonly ContosoUniversity.Data.SchoolContext _context;

        public DeleteModel(ContosoUniversity.Data.SchoolContext context)
        {
            _context = context;
        }

        [BindProperty]
        public Instructor Instructor { get; set; }

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

            Instructor = await _context.Instructors.SingleAsync(m => m.ID == id);

            if (Instructor == null)
            {
                return NotFound();
            }
            return Page();
        }

        public async Task<IActionResult> OnPostAsync(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 RedirectToPage("./Index");
        }
    }
}

Předchozí kód provede následující změny:

  • Používá dychtivé načítání pro CourseAssignments navigační vlastnost. CourseAssignments musí být zahrnuté nebo se při odstranění instruktora neodstraní. Abyste se vyhnuli nutnosti je číst, nakonfigurujte kaskádové odstranění v databázi.

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

Další materiály