Poznámka
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
Poznámka:
Toto není nejnovější verze tohoto článku. Aktuální verzi tohoto článku pro .NET 9 najdete zde.
Varování
Tato verze ASP.NET Core se už nepodporuje. Další informace najdete v zásadách podpory .NET a .NET Core. Aktuální vydání najdete v článku o 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í vydání tohoto článku najdete ve verzi .NET 9.
Tom Dykstra, Jeremy Likness a Jon P Smith
Webová aplikace Contoso University ukazuje, jak vytvářet Razor webové aplikace typu Pages pomocí EF Core a 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ůsobuje základní 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 jsou přihlášky přidány na stránku Details
.
Čtení přihlášek
Pokud chcete na stránce zobrazit data o zápisu studenta, musí se načíst data o registraci. Vygenerovaný kód v Pages/Students/Details.cshtml.cs
č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 IncludeThenInclude způsobují, že kontext má načíst Student.Enrollments
navigační vlastnost a v rámci každé registrace také 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 volat
Include
sFindAsync
. Takže pokud potřebujete související data,FirstOrDefaultAsync
je lepší volbou.
Data trasy 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
v 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 odeslaného formuláře z vlastnosti PageContext v PageModel objektu.
- 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.
Nadbytečné zveřejňování
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ž na stránce pro vytvoření nebo aktualizaci aplikace neexistuje pole Secret
, hacker může nastavit hodnotu Secret
pomocí nadměrného odesílání. 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 když návrhář aplikace nikdy nezamýšlel, aby byla vlastnost Secret
nastavena prostřednictvím stránky Vytvoření.
Zobrazit model
Modely zobrazení poskytují alternativní způsob, jak zabránit nadměrnému vkládání dat.
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 se použila StudentVM
na stránce Vytvořit místo Student
.
@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 Editace
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
má parametrid
. - 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ž se zavolá SaveChangesAsync
, 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 vytvoří se nová instance DbContext
. Přečtení entity znovu v novém kontextu simuluje zpracování na pracovní ploše.
Aktualizovat stránku Odstranit
Při selhání volání SaveChanges
je v této části implementována 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 neúspěšném pokusu o 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 OnPostAsync
volá OnGetAsync
kvůli selhání operace odstranění, parametr saveChangesError
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:
- Byla zachycena výjimka databáze.
- 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, odstraňte studenta a otestujte tak stránku Odstranit.
Další kroky
V tomto kurzu se reviduje a přizpůsobí výchozí 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 výuka byla zaměřena na EF Core, přidá se EF Core kód přímo do tříd modelu stránky.
Aktualizace stránky Podrobnosti
Strukturovaný kód pro stránky studentů neobsahuje data o zápisu. V této části se registrace přidávají na stránku Details
.
Čtení registrací
Pokud chcete na stránce zobrazit data o zápisu studenta, musí se načíst data o registraci. Kód vytvořený v rámci Pages/Students/Details.cshtml.cs
č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 a ThenInclude způsobí, že kontext načte Student.Enrollments
navigační vlastnost a v každém zápisu 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
Include
nelze volat sFindAsync
. 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.
Aktualizujte stránku 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
v 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 zadané ve formuláři z vlastnosti PageContext v 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.
Nadměrné zveřejňování
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 na stránce pro vytvoření nebo aktualizaci nemá pole Secret
, hacker může nastavovat hodnotu Secret
prostřednictvím nadměrného zadávání údajů. 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 když návrhář aplikace nikdy nezamýšlel, aby byla vlastnost Secret
nastavena s pomocí stránky 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 stránka Vytvořit použila StudentVM
místo Student
.
@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
má parametrid
. - Aktuální student se načte z databáze místo vytvoření prázdného studenta.
Spusťte aplikaci a otestujte ji tím, že vytvoříte a upravíte 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ž se volá SaveChangesAsync
, 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í na pracovní ploše.
Aktualizace stránky Odstranit
V této části je při selhání volání SaveChanges
implementována 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ž je stránka Smazat OnGetAsync
vyvolána z uživatelského rozhraní. Pokud je OnGetAsync
volána OnPostAsync
, protože odstranění selhalo, parametr saveChangesError
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 návodu se reviduje a upraví základní generovaný 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 tutoriál to nedělá. Aby se minimalizovala složitost a kurz se zaměřil na EF Core, je kód EF Core přidán přímo do tříd modelu stránky.
Aktualizace stránky Podrobnosti
Kód strukturovaný pro studentské stránky neobsahuje údaje o zápisu. 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, je třeba přečíst tato data. Vygenerovaný kód v Pages/Students/Details.cshtml.cs
čte pouze data studenta, a nikoliv data 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 IncludeThenInclude způsobují, že kontext načte Student.Enrollments
navigační vlastnost a v rámci každého zápisu 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
Include
volatFindAsync
. 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
v 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 odeslaného formuláře z vlastnosti PageContext 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.
Nadměrné zveřejňování
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ž na stránce pro vytvoření nebo aktualizaci nemá aplikace pole Secret
, hacker může hodnotu Secret
nastavit přenastavením. 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 přesto, že návrhář aplikace nikdy nezamýšlel, aby byla vlastnost nastavena 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, aby Create.cshtml byl aktualizován tak, aby používal StudentVM
místo Student
.
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 tím, že vytvoříte a upravíte 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ž se volá SaveChangesAsync
, 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 zároveň nová instance DbContext
. Opětovné přečtení entity v tomto novém kontextu simuluje desktopové zpracování.
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ěny (s výjimkou vyčištění výrazů using
).
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 neúspěšném pokusu o 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
má hodnotu false, když je stránka Delete OnGetAsync
volána z uživatelského rozhraní. Když OnGetAsync
je volán OnPostAsync
(protože operace odstranění selhala), parametr saveChangesError
je 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řidejte chybovou zprávu 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, abyste otestovali stránku Odstranit.