Část 2, Razor Stránky s EF Core ASP.NET Core – CRUD
Poznámka:
Toto není nejnovější verze tohoto článku. Aktuální verzi najdete v tomto článku ve verzi .NET 9.
Upozorňující
Tato verze ASP.NET Core se už nepodporuje. Další informace najdete v zásadách podpory .NET a .NET Core. Aktuální verzi najdete v tomto článku ve verzi .NET 9.
Důležité
Tyto informace se týkají předběžného vydání produktu, který může být podstatně změněn před komerčním vydáním. Microsoft neposkytuje žádné záruky, výslovné ani předpokládané, týkající se zde uváděných informací.
Aktuální verzi najdete v tomto článku ve verzi .NET 9.
Tom Dykstra, Jeremy Likness a Jon P Smith
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 reviduje a přizpůsobí kód CRUD (vytvoření, čtení, aktualizace, odstranění).
Žádné úložiště
Někteří vývojáři používají model vrstvy služby nebo úložiště k vytvoření abstraktní vrstvy mezi uživatelským rozhraním (Razor stránkami) a vrstvou přístupu k datům. Tento kurz to nedělá. Aby se minimalizovala složitost a kurz se zaměřil na EF Core, EF Core přidá se kód přímo do tříd modelu stránky.
Aktualizace stránky Podrobnosti
Vygenerovaný kód pro stránky Studentů neobsahuje data registrace. V této části se registrace přidají na Details
stránku.
Čtení registrací
Pokud chcete na stránce zobrazit data o zápisu studenta, musí se načíst data o registraci. Vygenerovaný kód vygenerovaný v Pages/Students/Details.cshtml.cs
datech čte pouze Student
data bez Enrollment
dat:
public async Task<IActionResult> OnGetAsync(int? id)
{
if (id == null)
{
return NotFound();
}
Student = await _context.Students.FirstOrDefaultAsync(m => m.ID == id);
if (Student == null)
{
return NotFound();
}
return Page();
}
Nahraďte metodu OnGetAsync
následujícím kódem pro čtení dat zápisu pro vybraného studenta. Změny jsou zvýrazněné.
public async Task<IActionResult> OnGetAsync(int? id)
{
if (id == null)
{
return NotFound();
}
Student = await _context.Students
.Include(s => s.Enrollments)
.ThenInclude(e => e.Course)
.AsNoTracking()
.FirstOrDefaultAsync(m => m.ID == id);
if (Student == null)
{
return NotFound();
}
return Page();
}
Metody Include ThenInclude způsobují, že kontext načte Student.Enrollments
navigační vlastnost a v rámci každé registrace Enrollment.Course
navigační vlastnost. Tyto metody jsou podrobně prozkoumány v kurzu čtení souvisejících dat .
Metoda AsNoTracking zlepšuje výkon ve scénářích, kdy se vrácené entity neaktualizují v aktuálním kontextu. AsNoTracking
je popsáno dále v tomto kurzu.
Zobrazení registrací
Nahraďte kód Pages/Students/Details.cshtml
následujícím kódem, aby se zobrazil seznam registrací. Změny jsou zvýrazněné.
@page
@model ContosoUniversity.Pages.Students.DetailsModel
@{
ViewData["Title"] = "Details";
}
<h1>Details</h1>
<div>
<h4>Student</h4>
<hr />
<dl class="row">
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.Student.LastName)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.Student.LastName)
</dd>
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.Student.FirstMidName)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.Student.FirstMidName)
</dd>
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.Student.EnrollmentDate)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.Student.EnrollmentDate)
</dd>
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.Student.Enrollments)
</dt>
<dd class="col-sm-10">
<table class="table">
<tr>
<th>Course Title</th>
<th>Grade</th>
</tr>
@foreach (var item in Model.Student.Enrollments)
{
<tr>
<td>
@Html.DisplayFor(modelItem => item.Course.Title)
</td>
<td>
@Html.DisplayFor(modelItem => item.Grade)
</td>
</tr>
}
</table>
</dd>
</dl>
</div>
<div>
<a asp-page="./Edit" asp-route-id="@Model.Student.ID">Edit</a> |
<a asp-page="./Index">Back to List</a>
</div>
Předchozí smyčka kódu prochází entitami v Enrollments
navigační vlastnosti. Pro každou registraci se zobrazí název kurzu a známka. Název kurzu se načte z Course
entity, která je uložená v Course
navigační vlastnosti entity Enrollments.
Spusťte aplikaci, vyberte kartu Studenti a klikněte na odkaz Podrobnosti pro studenta. Zobrazí se seznam kurzů a známek pro vybraného studenta.
Způsoby čtení jedné entity
Vygenerovaný kód používá metodu FirstOrDefaultAsync ke čtení jedné entity. Tato metoda vrátí hodnotu null, pokud se nic nenajde; v opačném případě vrátí první řádek, který splňuje kritéria filtru dotazu. FirstOrDefaultAsync
je obecně lepší volbou než následující alternativy:
- SingleOrDefaultAsync – vyvolá výjimku, pokud existuje více než jedna entita, která splňuje filtr dotazu. Chcete-li zjistit, zda dotaz může vrátit více než jeden řádek,
SingleOrDefaultAsync
pokusí se načíst více řádků. Tato nadbytečná práce není nutná, pokud dotaz může vrátit pouze jednu entitu, jako když hledá jedinečný klíč. - FindAsync – Najde entitu s primárním klíčem (PK). Pokud je entita s PK sledována kontextem, vrátí se bez požadavku na databázi. Tato metoda je optimalizovaná pro vyhledání jedné entity, ale nemůžete ji
FindAsync
volatInclude
. Takže pokud potřebujete související data,FirstOrDefaultAsync
je lepší volbou.
Směrování dat vs. řetězec dotazu
Adresa URL stránky Podrobnosti je https://localhost:<port>/Students/Details?id=1
. Hodnota primárního klíče entity je v řetězci dotazu. Někteří vývojáři raději předávají hodnotu klíče ve směrovacích datech: https://localhost:<port>/Students/Details/1
. Další informace najdete v tématu Aktualizace vygenerovaného kódu.
Aktualizace stránky Vytvořit
Vygenerovaný OnPostAsync
kód pro stránku Vytvořit je zranitelný vůči nadměrnému umístění. Nahraďte metodu OnPostAsync
Pages/Students/Create.cshtml.cs
následujícím kódem.
public async Task<IActionResult> OnPostAsync()
{
var emptyStudent = new Student();
if (await TryUpdateModelAsync<Student>(
emptyStudent,
"student", // Prefix for form value.
s => s.FirstMidName, s => s.LastName, s => s.EnrollmentDate))
{
_context.Students.Add(emptyStudent);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
return Page();
}
TryUpdateModelAsync
Předchozí kód vytvoří objekt Student a pak použije pole publikovaného formuláře k aktualizaci vlastností objektu Student. Metoda TryUpdateModelAsync :
- Používá hodnoty publikovaného formuláře z PageContext vlastnosti v objektu PageModel.
- Aktualizuje pouze uvedené vlastnosti (
s => s.FirstMidName, s => s.LastName, s => s.EnrollmentDate
). - Vyhledá pole formuláře s předponou student. Například
Student.FirstMidName
. Nerozlišuje se malá a velká písmena. - Používá systém vazeb modelu k převodu hodnot formulářů z řetězců na typy v
Student
modelu. NapříkladEnrollmentDate
je převeden naDateTime
.
Spusťte aplikaci a vytvořte entitu studenta, která otestuje stránku Vytvořit.
Přesunutí
Použití TryUpdateModel
k aktualizaci polí s publikovanými hodnotami je osvědčeným postupem zabezpečení, protože brání nadměrnému publikování. Předpokládejme například, že entita Student obsahuje Secret
vlastnost, kterou by tato webová stránka neměla aktualizovat nebo přidat:
public class Student
{
public int ID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
public DateTime EnrollmentDate { get; set; }
public string Secret { get; set; }
}
I když aplikace nemá Secret
na stránce pro vytvoření nebo aktualizaci Razor pole, hacker může hodnotu nastavit Secret
tak, že ji přeloží. Hacker může použít nástroj, jako je Fiddler nebo napsat nějaký JavaScript, k publikování Secret
hodnoty formuláře. Původní kód neomezuje pole, která binder modelu používá při vytváření instance Student.
V databázi se aktualizuje jakákoli hodnota, kterou hacker zadal pro Secret
pole formuláře. Následující obrázek ukazuje nástroj Fiddler, který přidává Secret
pole s hodnotou OverPost k publikovaným hodnotám formuláře.
Hodnota OverPost je úspěšně přidána do Secret
vlastnosti vloženého řádku. K tomu dochází i v případě, že návrhář aplikace nikdy nezamýšlel Secret
, aby byla vlastnost nastavená na stránce Vytvořit.
Zobrazit model
Modely zobrazení poskytují alternativní způsob, jak zabránit nadměrnému umístění.
Aplikační model se často nazývá doménový model. Doménový model obvykle obsahuje všechny vlastnosti vyžadované odpovídající entitou v databázi. Model zobrazení obsahuje pouze vlastnosti potřebné pro stránku uživatelského rozhraní, například vytvořit stránku.
Kromě modelu zobrazení používají některé aplikace k předávání dat mezi Razor třídou modelu stránky Stránky a prohlížečem model vazby nebo vstupní model.
Představte si následující StudentVM
model zobrazení:
public class StudentVM
{
public int ID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
public DateTime EnrollmentDate { get; set; }
}
Následující kód používá StudentVM
model zobrazení k vytvoření nového studenta:
[BindProperty]
public StudentVM StudentVM { get; set; }
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
var entry = _context.Add(new Student());
entry.CurrentValues.SetValues(StudentVM);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
SetValues metoda nastaví hodnoty tohoto objektu čtením hodnot z jiného PropertyValues objektu. SetValues
používá porovnávání názvů vlastností. Typ modelu zobrazení:
- Nemusí souviset s typem modelu.
- Musí mít vlastnosti, které odpovídají.
Použití StudentVM
vyžaduje, aby Student
stránka Vytvořit místoStudentVM
:
@page
@model CreateVMModel
@{
ViewData["Title"] = "Create";
}
<h1>Create</h1>
<h4>Student</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="StudentVM.LastName" class="control-label"></label>
<input asp-for="StudentVM.LastName" class="form-control" />
<span asp-validation-for="StudentVM.LastName" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="StudentVM.FirstMidName" class="control-label"></label>
<input asp-for="StudentVM.FirstMidName" class="form-control" />
<span asp-validation-for="StudentVM.FirstMidName" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="StudentVM.EnrollmentDate" class="control-label"></label>
<input asp-for="StudentVM.EnrollmentDate" class="form-control" />
<span asp-validation-for="StudentVM.EnrollmentDate" class="text-danger"></span>
</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 Upravit
V Pages/Students/Edit.cshtml.cs
příkazu nahraďte OnGetAsync
a OnPostAsync
metody následujícím kódem.
public async Task<IActionResult> OnGetAsync(int? id)
{
if (id == null)
{
return NotFound();
}
Student = await _context.Students.FindAsync(id);
if (Student == null)
{
return NotFound();
}
return Page();
}
public async Task<IActionResult> OnPostAsync(int id)
{
var studentToUpdate = await _context.Students.FindAsync(id);
if (studentToUpdate == null)
{
return NotFound();
}
if (await TryUpdateModelAsync<Student>(
studentToUpdate,
"student",
s => s.FirstMidName, s => s.LastName, s => s.EnrollmentDate))
{
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
return Page();
}
Změny kódu se podobají stránce Vytvořit s několika výjimkami:
FirstOrDefaultAsync
byla nahrazena znakem FindAsync. Pokud nepotřebujete zahrnout související data,FindAsync
je efektivnější.OnPostAsync
id
má parametr.- Aktuální student se načte z databáze místo vytvoření prázdného studenta.
Spusťte aplikaci a otestujte ji vytvořením a úpravou studenta.
Stavy entit
Kontext databáze sleduje, jestli se entity v paměti synchronizují s odpovídajícími řádky v databázi. Tyto informace o sledování určují, co se stane při volání SaveChangesAsync . Pokud je například do metody předána AddAsync nová entita, stav dané entity je nastaven na Added. Když SaveChangesAsync
se volá, kontext databáze vydá příkaz SQL INSERT
.
Entita může být v jednom z následujících stavů:
Added
: Entita ještě v databázi neexistuje. MetodaSaveChanges
vydáINSERT
příkaz.Unchanged
: U této entity není nutné ukládat žádné změny. Entita má tento stav při čtení z databáze.Modified
: Některé nebo všechny hodnoty vlastností entity byly změněny. MetodaSaveChanges
vydáUPDATE
příkaz.Deleted
: Entita byla označena k odstranění. MetodaSaveChanges
vydáDELETE
příkaz.Detached
: Entita není sledována kontextem databáze.
V desktopové aplikaci se změny stavu obvykle nastaví automaticky. Entita je přečtená, provede se změny a stav entity se automaticky změní na Modified
. Volání SaveChanges
generuje příkaz SQL UPDATE
, který aktualizuje pouze změněné vlastnosti.
Ve webové aplikaci, DbContext
která čte entitu a zobrazí data, se odstraní po vykreslení stránky. Při zavolání metody stránky OnPostAsync
se vytvoří nový webový požadavek a s novou instancí DbContext
. Opětovné přečtení entity v tomto novém kontextu simuluje zpracování plochy.
Aktualizace stránky Odstranit
V této části se při selhání volání SaveChanges
implementuje vlastní chybová zpráva.
Nahraďte kód v souboru Pages/Students/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 Microsoft.Extensions.Logging;
using System;
using System.Threading.Tasks;
namespace ContosoUniversity.Pages.Students
{
public class DeleteModel : PageModel
{
private readonly ContosoUniversity.Data.SchoolContext _context;
private readonly ILogger<DeleteModel> _logger;
public DeleteModel(ContosoUniversity.Data.SchoolContext context,
ILogger<DeleteModel> logger)
{
_context = context;
_logger = logger;
}
[BindProperty]
public Student Student { get; set; }
public string ErrorMessage { get; set; }
public async Task<IActionResult> OnGetAsync(int? id, bool? saveChangesError = false)
{
if (id == null)
{
return NotFound();
}
Student = await _context.Students
.AsNoTracking()
.FirstOrDefaultAsync(m => m.ID == id);
if (Student == null)
{
return NotFound();
}
if (saveChangesError.GetValueOrDefault())
{
ErrorMessage = String.Format("Delete {ID} failed. Try again", id);
}
return Page();
}
public async Task<IActionResult> OnPostAsync(int? id)
{
if (id == null)
{
return NotFound();
}
var student = await _context.Students.FindAsync(id);
if (student == null)
{
return NotFound();
}
try
{
_context.Students.Remove(student);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
catch (DbUpdateException ex)
{
_logger.LogError(ex, ErrorMessage);
return RedirectToAction("./Delete",
new { id, saveChangesError = true });
}
}
}
}
Předchozí kód:
- Přidá protokolování.
- Přidá volitelný parametr
saveChangesError
doOnGetAsync
podpisu metody.saveChangesError
označuje, zda byla metoda volána po selhání odstranění objektu studenta.
Operace odstranění může selhat kvůli přechodným problémům se sítí. Přechodné chyby sítě jsou pravděpodobnější, když je databáze v cloudu. Parametr saveChangesError
je false
, když se stránka OnGetAsync
Delete volá z uživatelského rozhraní. Pokud OnGetAsync
je volána OnPostAsync
, protože operace odstranění selhala, saveChangesError
parametr je true
.
Metoda OnPostAsync
načte vybranou entitu a potom zavolá metodu Remove , která nastaví stav entity na Deleted
. Při SaveChanges
volání se vygeneruje příkaz SQL DELETE
. Pokud Remove
selže:
- Výjimka databáze se zachytí.
- Metoda Delete pages
OnGetAsync
je volána ssaveChangesError=true
.
Přidejte chybovou zprávu do Pages/Students/Delete.cshtml
:
@page
@model ContosoUniversity.Pages.Students.DeleteModel
@{
ViewData["Title"] = "Delete";
}
<h1>Delete</h1>
<p class="text-danger">@Model.ErrorMessage</p>
<h3>Are you sure you want to delete this?</h3>
<div>
<h4>Student</h4>
<hr />
<dl class="row">
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.Student.LastName)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.Student.LastName)
</dd>
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.Student.FirstMidName)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.Student.FirstMidName)
</dd>
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.Student.EnrollmentDate)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.Student.EnrollmentDate)
</dd>
</dl>
<form method="post">
<input type="hidden" asp-for="Student.ID" />
<input type="submit" value="Delete" class="btn btn-danger" /> |
<a asp-page="./Index">Back to List</a>
</form>
</div>
Spusťte aplikaci a odstraňte studenta a otestujte stránku Odstranit.
Další kroky
V tomto kurzu se reviduje a přizpůsobí kód CRUD (vytvoření, čtení, aktualizace, odstranění).
Žádné úložiště
Někteří vývojáři používají model vrstvy služby nebo úložiště k vytvoření abstraktní vrstvy mezi uživatelským rozhraním (Razor stránkami) a vrstvou přístupu k datům. Tento kurz to nedělá. Aby se minimalizovala složitost a kurz se zaměřil na EF Core, EF Core přidá se kód přímo do tříd modelu stránky.
Aktualizace stránky Podrobnosti
Vygenerovaný kód pro stránky Studentů neobsahuje data registrace. V této části se registrace přidají na Details
stránku.
Čtení registrací
Pokud chcete na stránce zobrazit data o zápisu studenta, musí se načíst data o registraci. Vygenerovaný kód vygenerovaný v Pages/Students/Details.cshtml.cs
datech čte pouze Student
data bez Enrollment
dat:
public async Task<IActionResult> OnGetAsync(int? id)
{
if (id == null)
{
return NotFound();
}
Student = await _context.Students.FirstOrDefaultAsync(m => m.ID == id);
if (Student == null)
{
return NotFound();
}
return Page();
}
Nahraďte metodu OnGetAsync
následujícím kódem pro čtení dat zápisu pro vybraného studenta. Změny jsou zvýrazněné.
public async Task<IActionResult> OnGetAsync(int? id)
{
if (id == null)
{
return NotFound();
}
Student = await _context.Students
.Include(s => s.Enrollments)
.ThenInclude(e => e.Course)
.AsNoTracking()
.FirstOrDefaultAsync(m => m.ID == id);
if (Student == null)
{
return NotFound();
}
return Page();
}
Metody Include ThenInclude způsobují, že kontext načte Student.Enrollments
navigační vlastnost a v rámci každé registrace Enrollment.Course
navigační vlastnost. Tyto metody jsou podrobně prozkoumány v kurzu čtení souvisejících dat .
Metoda AsNoTracking zlepšuje výkon ve scénářích, kdy se vrácené entity neaktualizují v aktuálním kontextu. AsNoTracking
je popsáno dále v tomto kurzu.
Zobrazení registrací
Nahraďte kód Pages/Students/Details.cshtml
následujícím kódem, aby se zobrazil seznam registrací. Změny jsou zvýrazněné.
@page
@model ContosoUniversity.Pages.Students.DetailsModel
@{
ViewData["Title"] = "Details";
}
<h1>Details</h1>
<div>
<h4>Student</h4>
<hr />
<dl class="row">
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.Student.LastName)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.Student.LastName)
</dd>
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.Student.FirstMidName)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.Student.FirstMidName)
</dd>
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.Student.EnrollmentDate)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.Student.EnrollmentDate)
</dd>
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.Student.Enrollments)
</dt>
<dd class="col-sm-10">
<table class="table">
<tr>
<th>Course Title</th>
<th>Grade</th>
</tr>
@foreach (var item in Model.Student.Enrollments)
{
<tr>
<td>
@Html.DisplayFor(modelItem => item.Course.Title)
</td>
<td>
@Html.DisplayFor(modelItem => item.Grade)
</td>
</tr>
}
</table>
</dd>
</dl>
</div>
<div>
<a asp-page="./Edit" asp-route-id="@Model.Student.ID">Edit</a> |
<a asp-page="./Index">Back to List</a>
</div>
Předchozí smyčka kódu prochází entitami v Enrollments
navigační vlastnosti. Pro každou registraci se zobrazí název kurzu a známka. Název kurzu se načte z Course
entity, která je uložená v Course
navigační vlastnosti entity Enrollments.
Spusťte aplikaci, vyberte kartu Studenti a klikněte na odkaz Podrobnosti pro studenta. Zobrazí se seznam kurzů a známek pro vybraného studenta.
Způsoby čtení jedné entity
Vygenerovaný kód používá metodu FirstOrDefaultAsync ke čtení jedné entity. Tato metoda vrátí hodnotu null, pokud se nic nenajde; v opačném případě vrátí první řádek, který splňuje kritéria filtru dotazu. FirstOrDefaultAsync
je obecně lepší volbou než následující alternativy:
- SingleOrDefaultAsync – vyvolá výjimku, pokud existuje více než jedna entita, která splňuje filtr dotazu. Chcete-li zjistit, zda dotaz může vrátit více než jeden řádek,
SingleOrDefaultAsync
pokusí se načíst více řádků. Tato nadbytečná práce není nutná, pokud dotaz může vrátit pouze jednu entitu, jako když hledá jedinečný klíč. - FindAsync – Najde entitu s primárním klíčem (PK). Pokud je entita s PK sledována kontextem, vrátí se bez požadavku na databázi. Tato metoda je optimalizovaná pro vyhledání jedné entity, ale nemůžete ji
FindAsync
volatInclude
. Takže pokud potřebujete související data,FirstOrDefaultAsync
je lepší volbou.
Směrování dat vs. řetězec dotazu
Adresa URL stránky Podrobnosti je https://localhost:<port>/Students/Details?id=1
. Hodnota primárního klíče entity je v řetězci dotazu. Někteří vývojáři raději předávají hodnotu klíče ve směrovacích datech: https://localhost:<port>/Students/Details/1
. Další informace najdete v tématu Aktualizace vygenerovaného kódu.
Aktualizace stránky Vytvořit
Vygenerovaný OnPostAsync
kód pro stránku Vytvořit je zranitelný vůči nadměrnému umístění. Nahraďte metodu OnPostAsync
Pages/Students/Create.cshtml.cs
následujícím kódem.
public async Task<IActionResult> OnPostAsync()
{
var emptyStudent = new Student();
if (await TryUpdateModelAsync<Student>(
emptyStudent,
"student", // Prefix for form value.
s => s.FirstMidName, s => s.LastName, s => s.EnrollmentDate))
{
_context.Students.Add(emptyStudent);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
return Page();
}
TryUpdateModelAsync
Předchozí kód vytvoří objekt Student a pak použije pole publikovaného formuláře k aktualizaci vlastností objektu Student. Metoda TryUpdateModelAsync :
- Používá hodnoty publikovaného formuláře z PageContext vlastnosti v objektu PageModel.
- Aktualizuje pouze uvedené vlastnosti (
s => s.FirstMidName, s => s.LastName, s => s.EnrollmentDate
). - Vyhledá pole formuláře s předponou student. Například
Student.FirstMidName
. Nerozlišuje se malá a velká písmena. - Používá systém vazeb modelu k převodu hodnot formulářů z řetězců na typy v
Student
modelu. NapříkladEnrollmentDate
je převeden naDateTime
.
Spusťte aplikaci a vytvořte entitu studenta, která otestuje stránku Vytvořit.
Přesunutí
Použití TryUpdateModel
k aktualizaci polí s publikovanými hodnotami je osvědčeným postupem zabezpečení, protože brání nadměrnému publikování. Předpokládejme například, že entita Student obsahuje Secret
vlastnost, kterou by tato webová stránka neměla aktualizovat nebo přidat:
public class Student
{
public int ID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
public DateTime EnrollmentDate { get; set; }
public string Secret { get; set; }
}
I když aplikace nemá Secret
na stránce pro vytvoření nebo aktualizaci Razor pole, hacker může hodnotu nastavit Secret
tak, že ji přeloží. Hacker může použít nástroj, jako je Fiddler nebo napsat nějaký JavaScript, k publikování Secret
hodnoty formuláře. Původní kód neomezuje pole, která binder modelu používá při vytváření instance Student.
V databázi se aktualizuje jakákoli hodnota, kterou hacker zadal pro Secret
pole formuláře. Následující obrázek ukazuje nástroj Fiddler, který přidává Secret
pole s hodnotou OverPost k publikovaným hodnotám formuláře.
Hodnota OverPost je úspěšně přidána do Secret
vlastnosti vloženého řádku. K tomu dochází i v případě, že návrhář aplikace nikdy nezamýšlel Secret
, aby byla vlastnost nastavená na stránce Vytvořit.
Zobrazit model
Modely zobrazení poskytují alternativní způsob, jak zabránit nadměrnému umístění.
Aplikační model se často nazývá doménový model. Doménový model obvykle obsahuje všechny vlastnosti vyžadované odpovídající entitou v databázi. Model zobrazení obsahuje pouze vlastnosti potřebné pro stránku uživatelského rozhraní, například vytvořit stránku.
Kromě modelu zobrazení používají některé aplikace k předávání dat mezi Razor třídou modelu stránky Stránky a prohlížečem model vazby nebo vstupní model.
Představte si následující StudentVM
model zobrazení:
public class StudentVM
{
public int ID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
public DateTime EnrollmentDate { get; set; }
}
Následující kód používá StudentVM
model zobrazení k vytvoření nového studenta:
[BindProperty]
public StudentVM StudentVM { get; set; }
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
var entry = _context.Add(new Student());
entry.CurrentValues.SetValues(StudentVM);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
SetValues metoda nastaví hodnoty tohoto objektu čtením hodnot z jiného PropertyValues objektu. SetValues
používá porovnávání názvů vlastností. Typ modelu zobrazení:
- Nemusí souviset s typem modelu.
- Musí mít vlastnosti, které odpovídají.
Použití StudentVM
vyžaduje, aby Student
stránka Vytvořit místoStudentVM
:
@page
@model CreateVMModel
@{
ViewData["Title"] = "Create";
}
<h1>Create</h1>
<h4>Student</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="StudentVM.LastName" class="control-label"></label>
<input asp-for="StudentVM.LastName" class="form-control" />
<span asp-validation-for="StudentVM.LastName" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="StudentVM.FirstMidName" class="control-label"></label>
<input asp-for="StudentVM.FirstMidName" class="form-control" />
<span asp-validation-for="StudentVM.FirstMidName" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="StudentVM.EnrollmentDate" class="control-label"></label>
<input asp-for="StudentVM.EnrollmentDate" class="form-control" />
<span asp-validation-for="StudentVM.EnrollmentDate" class="text-danger"></span>
</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 Upravit
V Pages/Students/Edit.cshtml.cs
příkazu nahraďte OnGetAsync
a OnPostAsync
metody následujícím kódem.
public async Task<IActionResult> OnGetAsync(int? id)
{
if (id == null)
{
return NotFound();
}
Student = await _context.Students.FindAsync(id);
if (Student == null)
{
return NotFound();
}
return Page();
}
public async Task<IActionResult> OnPostAsync(int id)
{
var studentToUpdate = await _context.Students.FindAsync(id);
if (studentToUpdate == null)
{
return NotFound();
}
if (await TryUpdateModelAsync<Student>(
studentToUpdate,
"student",
s => s.FirstMidName, s => s.LastName, s => s.EnrollmentDate))
{
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
return Page();
}
Změny kódu se podobají stránce Vytvořit s několika výjimkami:
FirstOrDefaultAsync
byla nahrazena znakem FindAsync. Pokud nepotřebujete zahrnout související data,FindAsync
je efektivnější.OnPostAsync
id
má parametr.- Aktuální student se načte z databáze místo vytvoření prázdného studenta.
Spusťte aplikaci a otestujte ji vytvořením a úpravou studenta.
Stavy entit
Kontext databáze sleduje, jestli se entity v paměti synchronizují s odpovídajícími řádky v databázi. Tyto informace o sledování určují, co se stane při volání SaveChangesAsync . Pokud je například do metody předána AddAsync nová entita, stav dané entity je nastaven na Added. Když SaveChangesAsync
se volá, kontext databáze vydá příkaz SQL INSERT
.
Entita může být v jednom z následujících stavů:
Added
: Entita ještě v databázi neexistuje. MetodaSaveChanges
vydáINSERT
příkaz.Unchanged
: U této entity není nutné ukládat žádné změny. Entita má tento stav při čtení z databáze.Modified
: Některé nebo všechny hodnoty vlastností entity byly změněny. MetodaSaveChanges
vydáUPDATE
příkaz.Deleted
: Entita byla označena k odstranění. MetodaSaveChanges
vydáDELETE
příkaz.Detached
: Entita není sledována kontextem databáze.
V desktopové aplikaci se změny stavu obvykle nastaví automaticky. Entita je přečtená, provede se změny a stav entity se automaticky změní na Modified
. Volání SaveChanges
generuje příkaz SQL UPDATE
, který aktualizuje pouze změněné vlastnosti.
Ve webové aplikaci, DbContext
která čte entitu a zobrazí data, se odstraní po vykreslení stránky. Při zavolání metody stránky OnPostAsync
se vytvoří nový webový požadavek a s novou instancí DbContext
. Opětovné přečtení entity v tomto novém kontextu simuluje zpracování plochy.
Aktualizace stránky Odstranit
V této části se při selhání volání SaveChanges
implementuje vlastní chybová zpráva.
Nahraďte kód v souboru Pages/Students/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 Microsoft.Extensions.Logging;
using System;
using System.Threading.Tasks;
namespace ContosoUniversity.Pages.Students
{
public class DeleteModel : PageModel
{
private readonly ContosoUniversity.Data.SchoolContext _context;
private readonly ILogger<DeleteModel> _logger;
public DeleteModel(ContosoUniversity.Data.SchoolContext context,
ILogger<DeleteModel> logger)
{
_context = context;
_logger = logger;
}
[BindProperty]
public Student Student { get; set; }
public string ErrorMessage { get; set; }
public async Task<IActionResult> OnGetAsync(int? id, bool? saveChangesError = false)
{
if (id == null)
{
return NotFound();
}
Student = await _context.Students
.AsNoTracking()
.FirstOrDefaultAsync(m => m.ID == id);
if (Student == null)
{
return NotFound();
}
if (saveChangesError.GetValueOrDefault())
{
ErrorMessage = String.Format("Delete {ID} failed. Try again", id);
}
return Page();
}
public async Task<IActionResult> OnPostAsync(int? id)
{
if (id == null)
{
return NotFound();
}
var student = await _context.Students.FindAsync(id);
if (student == null)
{
return NotFound();
}
try
{
_context.Students.Remove(student);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
catch (DbUpdateException ex)
{
_logger.LogError(ex, ErrorMessage);
return RedirectToAction("./Delete",
new { id, saveChangesError = true });
}
}
}
}
Předchozí kód:
- Přidá protokolování.
- Přidá volitelný parametr
saveChangesError
doOnGetAsync
podpisu metody.saveChangesError
označuje, zda byla metoda volána po selhání odstranění objektu studenta.
Operace odstranění může selhat kvůli přechodným problémům se sítí. Přechodné chyby sítě jsou pravděpodobnější, když je databáze v cloudu. Parametr saveChangesError
je false
, když se stránka OnGetAsync
Delete volá z uživatelského rozhraní. Pokud OnGetAsync
je volána OnPostAsync
, protože operace odstranění selhala, saveChangesError
parametr je true
.
Metoda OnPostAsync
načte vybranou entitu a potom zavolá metodu Remove , která nastaví stav entity na Deleted
. Při SaveChanges
volání se vygeneruje příkaz SQL DELETE
. Pokud Remove
selže:
- Výjimka databáze se zachytí.
- Metoda Delete pages
OnGetAsync
je volána ssaveChangesError=true
.
Přidejte chybovou zprávu do Pages/Students/Delete.cshtml
:
@page
@model ContosoUniversity.Pages.Students.DeleteModel
@{
ViewData["Title"] = "Delete";
}
<h1>Delete</h1>
<p class="text-danger">@Model.ErrorMessage</p>
<h3>Are you sure you want to delete this?</h3>
<div>
<h4>Student</h4>
<hr />
<dl class="row">
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.Student.LastName)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.Student.LastName)
</dd>
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.Student.FirstMidName)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.Student.FirstMidName)
</dd>
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.Student.EnrollmentDate)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.Student.EnrollmentDate)
</dd>
</dl>
<form method="post">
<input type="hidden" asp-for="Student.ID" />
<input type="submit" value="Delete" class="btn btn-danger" /> |
<a asp-page="./Index">Back to List</a>
</form>
</div>
Spusťte aplikaci a odstraňte studenta a otestujte stránku Odstranit.
Další kroky
V tomto kurzu se reviduje a přizpůsobí kód CRUD (vytvoření, čtení, aktualizace, odstranění).
Žádné úložiště
Někteří vývojáři používají model vrstvy služby nebo úložiště k vytvoření abstraktní vrstvy mezi uživatelským rozhraním (Razor stránkami) a vrstvou přístupu k datům. Tento kurz to nedělá. Aby se minimalizovala složitost a kurz se zaměřil na EF Core, EF Core přidá se kód přímo do tříd modelu stránky.
Aktualizace stránky Podrobnosti
Vygenerovaný kód pro stránky Studentů neobsahuje data registrace. V této části se registrace přidají na stránku Podrobnosti.
Čtení registrací
Pokud chcete na stránce zobrazit data o zápisu studenta, musí být data o registraci čtená. Vygenerovaný kód vygenerovaný v Pages/Students/Details.cshtml.cs
datech studenta čte pouze data studenta bez dat o registraci:
public async Task<IActionResult> OnGetAsync(int? id)
{
if (id == null)
{
return NotFound();
}
Student = await _context.Students.FirstOrDefaultAsync(m => m.ID == id);
if (Student == null)
{
return NotFound();
}
return Page();
}
Nahraďte metodu OnGetAsync
následujícím kódem pro čtení dat zápisu pro vybraného studenta. Změny jsou zvýrazněné.
public async Task<IActionResult> OnGetAsync(int? id)
{
if (id == null)
{
return NotFound();
}
Student = await _context.Students
.Include(s => s.Enrollments)
.ThenInclude(e => e.Course)
.AsNoTracking()
.FirstOrDefaultAsync(m => m.ID == id);
if (Student == null)
{
return NotFound();
}
return Page();
}
Metody Include ThenInclude způsobují, že kontext načte Student.Enrollments
navigační vlastnost a v rámci každé registrace Enrollment.Course
navigační vlastnost. Tyto metody jsou podrobně prozkoumány v kurzu čtení souvisejících dat .
Metoda AsNoTracking zlepšuje výkon ve scénářích, kdy se vrácené entity neaktualizují v aktuálním kontextu. AsNoTracking
je popsáno dále v tomto kurzu.
Zobrazení registrací
Nahraďte kód Pages/Students/Details.cshtml
následujícím kódem, aby se zobrazil seznam registrací. Změny jsou zvýrazněné.
@page
@model ContosoUniversity.Pages.Students.DetailsModel
@{
ViewData["Title"] = "Details";
}
<h1>Details</h1>
<div>
<h4>Student</h4>
<hr />
<dl class="row">
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.Student.LastName)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.Student.LastName)
</dd>
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.Student.FirstMidName)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.Student.FirstMidName)
</dd>
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.Student.EnrollmentDate)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.Student.EnrollmentDate)
</dd>
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.Student.Enrollments)
</dt>
<dd class="col-sm-10">
<table class="table">
<tr>
<th>Course Title</th>
<th>Grade</th>
</tr>
@foreach (var item in Model.Student.Enrollments)
{
<tr>
<td>
@Html.DisplayFor(modelItem => item.Course.Title)
</td>
<td>
@Html.DisplayFor(modelItem => item.Grade)
</td>
</tr>
}
</table>
</dd>
</dl>
</div>
<div>
<a asp-page="./Edit" asp-route-id="@Model.Student.ID">Edit</a> |
<a asp-page="./Index">Back to List</a>
</div>
Předchozí smyčka kódu prochází entitami v Enrollments
navigační vlastnosti. Pro každou registraci se zobrazí název kurzu a známka. Název kurzu se načte z entity kurzu, která je uložená v Course
navigační vlastnosti entity Registrace.
Spusťte aplikaci, vyberte kartu Studenti a klikněte na odkaz Podrobnosti pro studenta. Zobrazí se seznam kurzů a známek pro vybraného studenta.
Způsoby čtení jedné entity
Vygenerovaný kód používá metodu FirstOrDefaultAsync ke čtení jedné entity. Tato metoda vrátí hodnotu null, pokud se nic nenajde; v opačném případě vrátí první řádek, který splňuje kritéria filtru dotazu. FirstOrDefaultAsync
je obecně lepší volbou než následující alternativy:
- SingleOrDefaultAsync – vyvolá výjimku, pokud existuje více než jedna entita, která splňuje filtr dotazu. Chcete-li zjistit, zda dotaz může vrátit více než jeden řádek,
SingleOrDefaultAsync
pokusí se načíst více řádků. Tato nadbytečná práce není nutná, pokud dotaz může vrátit pouze jednu entitu, jako když hledá jedinečný klíč. - FindAsync – Najde entitu s primárním klíčem (PK). Pokud je entita s PK sledována kontextem, vrátí se bez požadavku na databázi. Tato metoda je optimalizovaná pro vyhledání jedné entity, ale nemůžete ji
FindAsync
volatInclude
. Takže pokud potřebujete související data,FirstOrDefaultAsync
je lepší volbou.
Směrování dat vs. řetězec dotazu
Adresa URL stránky Podrobnosti je https://localhost:<port>/Students/Details?id=1
. Hodnota primárního klíče entity je v řetězci dotazu. Někteří vývojáři raději předávají hodnotu klíče ve směrovacích datech: https://localhost:<port>/Students/Details/1
. Další informace najdete v tématu Aktualizace vygenerovaného kódu.
Aktualizace stránky Vytvořit
Vygenerovaný OnPostAsync
kód pro stránku Vytvořit je zranitelný vůči nadměrnému umístění. Nahraďte metodu OnPostAsync
Pages/Students/Create.cshtml.cs
následujícím kódem.
public async Task<IActionResult> OnPostAsync()
{
var emptyStudent = new Student();
if (await TryUpdateModelAsync<Student>(
emptyStudent,
"student", // Prefix for form value.
s => s.FirstMidName, s => s.LastName, s => s.EnrollmentDate))
{
_context.Students.Add(emptyStudent);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
return Page();
}
TryUpdateModelAsync
Předchozí kód vytvoří objekt Student a pak použije pole publikovaného formuláře k aktualizaci vlastností objektu Student. Metoda TryUpdateModelAsync :
- Používá hodnoty publikovaného formuláře z PageContext vlastnosti v objektu PageModel.
- Aktualizuje pouze uvedené vlastnosti (
s => s.FirstMidName, s => s.LastName, s => s.EnrollmentDate
). - Vyhledá pole formuláře s předponou student. Například
Student.FirstMidName
. Nerozlišuje se malá a velká písmena. - Používá systém vazeb modelu k převodu hodnot formulářů z řetězců na typy v
Student
modelu. NapříkladEnrollmentDate
je třeba převést na DateTime.
Spusťte aplikaci a vytvořte entitu studenta, která otestuje stránku Vytvořit.
Přesunutí
Použití TryUpdateModel
k aktualizaci polí s publikovanými hodnotami je osvědčeným postupem zabezpečení, protože brání nadměrnému publikování. Předpokládejme například, že entita Student obsahuje Secret
vlastnost, kterou by tato webová stránka neměla aktualizovat nebo přidat:
public class Student
{
public int ID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
public DateTime EnrollmentDate { get; set; }
public string Secret { get; set; }
}
I když aplikace nemá Secret
na stránce pro vytvoření nebo aktualizaci Razor pole, hacker může hodnotu nastavit Secret
tak, že ji přeloží. Hacker může použít nástroj, jako je Fiddler nebo napsat nějaký JavaScript, k publikování Secret
hodnoty formuláře. Původní kód neomezuje pole, která binder modelu používá při vytváření instance Student.
V databázi se aktualizuje jakákoli hodnota, kterou hacker zadal pro Secret
pole formuláře. Následující obrázek ukazuje nástroj Fiddler, který přidává Secret
pole (s hodnotou OverPost) do odeslaných hodnot formuláře.
Hodnota OverPost je úspěšně přidána do Secret
vlastnosti vloženého řádku. K tomu dochází i v případě, že návrhář aplikace nikdy nezamýšlel Secret
, aby byla vlastnost nastavená na stránce Vytvořit.
Zobrazit model
Modely zobrazení poskytují alternativní způsob, jak zabránit nadměrnému umístění.
Aplikační model se často nazývá doménový model. Doménový model obvykle obsahuje všechny vlastnosti vyžadované odpovídající entitou v databázi. Model zobrazení obsahuje pouze vlastnosti potřebné pro uživatelské rozhraní, pro které se používá (například stránka Vytvořit).
Kromě modelu zobrazení používají některé aplikace k předávání dat mezi Razor třídou modelu stránky Stránky a prohlížečem model vazby nebo vstupní model.
Představte si následující Student
model zobrazení:
using System;
namespace ContosoUniversity.Models
{
public class StudentVM
{
public int ID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
public DateTime EnrollmentDate { get; set; }
}
}
Následující kód používá StudentVM
model zobrazení k vytvoření nového studenta:
[BindProperty]
public StudentVM StudentVM { get; set; }
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
var entry = _context.Add(new Student());
entry.CurrentValues.SetValues(StudentVM);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
SetValues metoda nastaví hodnoty tohoto objektu čtením hodnot z jiného PropertyValues objektu. SetValues
používá porovnávání názvů vlastností. Typ modelu zobrazení nemusí souviset s typem modelu, ale musí mít jenom vlastnosti, které odpovídají.
Použití StudentVM
vyžaduje aktualizaci Create.cshtml, Student
aby místo StudentVM
.
Aktualizace stránky Upravit
V Pages/Students/Edit.cshtml.cs
příkazu nahraďte OnGetAsync
a OnPostAsync
metody následujícím kódem.
public async Task<IActionResult> OnGetAsync(int? id)
{
if (id == null)
{
return NotFound();
}
Student = await _context.Students.FindAsync(id);
if (Student == null)
{
return NotFound();
}
return Page();
}
public async Task<IActionResult> OnPostAsync(int id)
{
var studentToUpdate = await _context.Students.FindAsync(id);
if (studentToUpdate == null)
{
return NotFound();
}
if (await TryUpdateModelAsync<Student>(
studentToUpdate,
"student",
s => s.FirstMidName, s => s.LastName, s => s.EnrollmentDate))
{
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
return Page();
}
Změny kódu se podobají stránce Vytvořit s několika výjimkami:
FirstOrDefaultAsync
byla nahrazena znakem FindAsync. Pokud nejsou zahrnutá související data potřeba,FindAsync
je efektivnější.OnPostAsync
id
má parametr.- Aktuální student se načte z databáze místo vytvoření prázdného studenta.
Spusťte aplikaci a otestujte ji vytvořením a úpravou studenta.
Stavy entit
Kontext databáze sleduje, jestli se entity v paměti synchronizují s odpovídajícími řádky v databázi. Tyto informace o sledování určují, co se stane při volání SaveChangesAsync . Pokud je například do metody předána AddAsync nová entita, stav dané entity je nastaven na Added. Když SaveChangesAsync
se volá, kontext databáze vydá příkaz SQL INSERT.
Entita může být v jednom z následujících stavů:
Added
: Entita ještě v databázi neexistuje. MetodaSaveChanges
vydá příkaz INSERT.Unchanged
: U této entity není nutné ukládat žádné změny. Entita má tento stav při čtení z databáze.Modified
: Některé nebo všechny hodnoty vlastností entity byly změněny. MetodaSaveChanges
vydává příkaz UPDATE.Deleted
: Entita byla označena k odstranění. MetodaSaveChanges
vydá příkaz DELETE.Detached
: Entita není sledována kontextem databáze.
V desktopové aplikaci se změny stavu obvykle nastaví automaticky. Entita je přečtená, provede se změny a stav entity se automaticky změní na Modified
. Volání SaveChanges
generuje příkaz SQL UPDATE, který aktualizuje pouze změněné vlastnosti.
Ve webové aplikaci, DbContext
která čte entitu a zobrazí data, se odstraní po vykreslení stránky. Při zavolání metody stránky OnPostAsync
se vytvoří nový webový požadavek a s novou instancí DbContext
. Opětovné přečtení entity v tomto novém kontextu simuluje zpracování plochy.
Aktualizace stránky Odstranit
V této části implementujete vlastní chybovou zprávu, když volání SaveChanges
selže.
Nahraďte kód v Pages/Students/Delete.cshtml.cs
následujícím kódem. Změny jsou zvýrazněné (kromě vyčištění using
příkazů).
using ContosoUniversity.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using System.Threading.Tasks;
namespace ContosoUniversity.Pages.Students
{
public class DeleteModel : PageModel
{
private readonly ContosoUniversity.Data.SchoolContext _context;
public DeleteModel(ContosoUniversity.Data.SchoolContext context)
{
_context = context;
}
[BindProperty]
public Student Student { get; set; }
public string ErrorMessage { get; set; }
public async Task<IActionResult> OnGetAsync(int? id, bool? saveChangesError = false)
{
if (id == null)
{
return NotFound();
}
Student = await _context.Students
.AsNoTracking()
.FirstOrDefaultAsync(m => m.ID == id);
if (Student == null)
{
return NotFound();
}
if (saveChangesError.GetValueOrDefault())
{
ErrorMessage = "Delete failed. Try again";
}
return Page();
}
public async Task<IActionResult> OnPostAsync(int? id)
{
if (id == null)
{
return NotFound();
}
var student = await _context.Students.FindAsync(id);
if (student == null)
{
return NotFound();
}
try
{
_context.Students.Remove(student);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
catch (DbUpdateException /* ex */)
{
//Log the error (uncomment ex variable name and write a log.)
return RedirectToAction("./Delete",
new { id, saveChangesError = true });
}
}
}
}
Předchozí kód přidá volitelný parametr saveChangesError
do OnGetAsync
podpisu metody. saveChangesError
označuje, zda byla metoda volána po selhání odstranění objektu studenta. Operace odstranění může selhat kvůli přechodným problémům se sítí. Přechodné chyby sítě jsou pravděpodobnější, když je databáze v cloudu. Parametr saveChangesError
je false, když se stránka OnGetAsync
Delete volá z uživatelského rozhraní. Když OnGetAsync
je volána ( OnPostAsync
protože operace odstranění selhala), saveChangesError
je parametr pravdivý.
Metoda OnPostAsync
načte vybranou entitu a potom zavolá metodu Remove , která nastaví stav entity na Deleted
. Při SaveChanges
volání se vygeneruje příkaz SQL DELETE. Pokud Remove
selže:
- Výjimka databáze se zachytí.
- Metoda Delete stránky
OnGetAsync
je volána ssaveChangesError=true
.
Přidání chybové zprávy na stránku Odstranit Razor (Pages/Students/Delete.cshtml
):
@page
@model ContosoUniversity.Pages.Students.DeleteModel
@{
ViewData["Title"] = "Delete";
}
<h1>Delete</h1>
<p class="text-danger">@Model.ErrorMessage</p>
<h3>Are you sure you want to delete this?</h3>
<div>
<h4>Student</h4>
<hr />
<dl class="row">
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.Student.LastName)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.Student.LastName)
</dd>
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.Student.FirstMidName)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.Student.FirstMidName)
</dd>
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.Student.EnrollmentDate)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.Student.EnrollmentDate)
</dd>
</dl>
<form method="post">
<input type="hidden" asp-for="Student.ID" />
<input type="submit" value="Delete" class="btn btn-danger" /> |
<a asp-page="./Index">Back to List</a>
</form>
</div>
Spusťte aplikaci a odstraňte studenta a otestujte stránku Odstranit.