Kurz: Zpracování souběžnosti s EF v aplikaci ASP.NET MVC 5
V předchozích kurzech jste se naučili, jak aktualizovat data. V tomto kurzu se dozvíte, jak pomocí optimistické souběžnosti řešit konflikty, když více uživatelů aktualizuje stejnou entitu najednou. Webové stránky, které pracují s entitou Department
, změníte tak, aby zpracovávaly chyby souběžnosti. Následující obrázky znázorňují stránky Upravit a Odstranit, včetně některých zpráv, které se zobrazí v případě konfliktu souběžnosti.
V tomto kurzu jste:
- Informace o konfliktech souběžnosti
- Přidání optimistické souběžnosti
- Upravit kontroler oddělení
- Zpracování souběžnosti testů
- Aktualizace stránky Odstranit
Požadavky
Konflikty souběžnosti
Ke konfliktu souběžnosti dojde, když jeden uživatel zobrazí data entity, aby ji mohl upravit, a pak jiný uživatel aktualizuje data stejné entity před zápisem změny prvního uživatele do databáze. Pokud zjišťování takových konfliktů nepovolíte, ten, kdo naposledy aktualizuje databázi, přepíše změny druhého uživatele. V mnoha aplikacích je toto riziko přijatelné: pokud existuje jen málo uživatelů nebo málo aktualizací nebo pokud nejsou skutečně kritické, pokud jsou některé změny přepsány, náklady na programování pro souběžnost mohou převážit výhody. V takovém případě nemusíte aplikaci konfigurovat tak, aby zpracovávala konflikty souběžnosti.
Pesimistická souběžnost (zamykání)
Pokud vaše aplikace potřebuje zabránit náhodné ztrátě dat ve scénářích souběžnosti, jedním ze způsobů, jak to udělat, je použít zámky databáze. Tomu se říká pesimistická souběžnost. Například před čtením řádku z databáze si vyžádáte zámek jen pro čtení nebo pro přístup k aktualizacím. Pokud uzamknete řádek pro přístup k aktualizacím, žádní jiní uživatelé nebudou moct zamknout řádek jen pro čtení nebo pro přístup k aktualizacím, protože by získali kopii dat, která se právě mění. Pokud řádek uzamknete pro přístup jen pro čtení, ostatní ho můžou také uzamknout pro přístup jen pro čtení, ale ne pro aktualizaci.
Správa zámků má nevýhody. Může být složité programovat. Vyžaduje významné prostředky pro správu databází a může způsobovat problémy s výkonem, když se zvýší počet uživatelů aplikace. Z těchto důvodů pesimistická souběžnost nepodporují všechny systémy pro správu databází. Entity Framework neposkytuje žádnou integrovanou podporu a tento kurz neukazuje, jak ho implementovat.
Optimistická metoda souběžného zpracování
Alternativou k pesimistické souběžnosti je optimistická souběžnost. Optimistická souběžnost znamená, že povolíte, aby ke konfliktům souběžnosti docházelo, a pokud ano, pak odpovídajícím způsobem reagovat. Jan například spustí stránku Úpravy oddělení a změní částku rozpočtu pro anglické oddělení z 350 000,00 USD na 0,00 USD.
Než Jan klikne na Uložit, Jana spustí stejnou stránku a změní pole Počáteční datum z 1. 9. 2007 na 8. 8. 2013.
Jan nejprve klikne na Uložit a uvidí svou změnu, když se prohlížeč vrátí na stránku Index, a pak jan klikne na Uložit. To, co se stane dál, závisí na způsobu zpracování konfliktů souběžnosti. Mezi tyto možnosti patří:
Můžete sledovat, kterou vlastnost uživatel změnil, a aktualizovat pouze odpovídající sloupce v databázi. V ukázkovém scénáři by nedošlo ke ztrátě dat, protože dva uživatelé aktualizovali různé vlastnosti. Až si někdo příště projde anglické oddělení, uvidí změny Johna i Jane – počáteční datum 8. 8. 2013 a rozpočet nula dolarů.
Tato metoda aktualizace může snížit počet konfliktů, které by mohly vést ke ztrátě dat, ale nemůže se vyhnout ztrátě dat, pokud jsou provedeny konkurenční změny stejné vlastnosti entity. To, jestli Entity Framework funguje tímto způsobem, závisí na způsobu implementace aktualizačního kódu. Ve webové aplikaci to často není praktické, protože může vyžadovat udržování velkého množství stavu, aby bylo možné sledovat všechny původní hodnoty vlastností pro entitu i nové hodnoty. Udržování velkého množství stavu může mít vliv na výkon aplikace, protože buď vyžaduje prostředky serveru, nebo musí být součástí samotné webové stránky (například ve skrytých polích) nebo v souboru cookie.
Můžete nechat Janovu změnu přepsat. Až si někdo příště projde anglické oddělení, uvidí 8. 8. 2013 a obnovenou hodnotu 350 000,00 USD. Tento scénář se nazývá Klient wins nebo Last ve scénáři Wins . (Všechny hodnoty z klienta mají přednost před tím, co je v úložišti dat.) Jak je uvedeno v úvodu k této části, pokud nezakódujete pro zpracování souběžnosti, dojde k tomu automaticky.
V databázi můžete zabránit, aby se změny Jane aktualizovaly. Obvykle byste zobrazili chybovou zprávu, zobrazili byste jí aktuální stav dat a povolili jí, aby změny znovu použila, pokud je stále chce provést. Tento scénář se nazývá Store Wins . (Hodnoty úložiště dat mají přednost před hodnotami odeslanými klientem.) V tomto kurzu implementujete scénář služby Store Wins. Tato metoda zajišťuje, že se nepřepíšou žádné změny, aniž by uživatel byl upozorněn na to, co se děje.
Zjišťování konfliktů souběžnosti
Konflikty můžete vyřešit zpracováním výjimek OptimisticConcurrencyException , které entity Framework vyvolává. Aby bylo možné zjistit, kdy tyto výjimky vyvolat, musí být Rozhraní Entity Framework schopné detekovat konflikty. Proto musíte odpovídajícím způsobem nakonfigurovat databázi a datový model. Mezi možnosti povolení detekce konfliktů patří:
V tabulce databáze zahrňte sledovací sloupec, pomocí kterého můžete určit, kdy byl řádek změněn. Pak můžete nakonfigurovat Entity Framework tak, aby zahrnoval tento sloupec do klauzule
Where
SQLUpdate
neboDelete
příkazů.Datový typ sledovacího sloupce je obvykle rowversion. Hodnota rowversion je pořadové číslo, které se při každé aktualizaci řádku zvýší.
Update
V příkazu neboDelete
klauzuleWhere
obsahuje původní hodnotu sledovacího sloupce (původní verzi řádku). Pokud aktualizovaný řádek změnil jiný uživatel, hodnota verowversion
sloupci se liší od původní hodnoty, takžeUpdate
příkaz orDelete
nemůže kvůli klauzuli najít řádek, který se má aktualizovatWhere
. Když Entity Framework zjistí, že příkazem neboDelete
nebyly aktualizoványUpdate
žádné řádky (to znamená, že počet ovlivněných řádků je nulový), interpretuje to jako konflikt souběžnosti.Nakonfigurujte Entity Framework tak, aby zahrnoval původní hodnoty všech sloupců v tabulce v klauzuli
Where
Update
aDelete
příkazů.Stejně jako u první možnosti platí, že pokud se od prvního přečtení řádku něco na řádku změnilo,
Where
klauzule nevrátí řádek, který se má aktualizovat, což Entity Framework interpretuje jako konflikt souběžnosti. U databázových tabulek, které mají mnoho sloupců, může mít tento přístup za následek velmi velkéWhere
klauzule a může vyžadovat udržování velkých objemů stavu. Jak jsme si poznamenali dříve, udržování velkého množství stavu může ovlivnit výkon aplikace. Proto se tento přístup obecně nedoporučuje a není to metoda použitá v tomto kurzu.Pokud chcete implementovat tento přístup ke souběžnosti, musíte v entitě, pro kterou chcete souběžnost sledovat, označit všechny vlastnosti, které nejsou primárním klíčem, přidáním atributu ConcurrencyCheck k nim. Tato změna umožňuje Entity Frameworku zahrnout všechny sloupce v klauzuli
UPDATE
SQLWHERE
příkazů.
Ve zbývající části tohoto kurzu přidáte do Department
entity vlastnost sledování rowversion, vytvoříte kontroler a zobrazení a otestujete, abyste ověřili, že vše funguje správně.
Přidání optimistické souběžnosti
V souboru Models\Department.cs přidejte vlastnost sledování s názvem RowVersion
:
public class Department
{
public int DepartmentID { get; set; }
[StringLength(50, MinimumLength = 3)]
public string Name { get; set; }
[DataType(DataType.Currency)]
[Column(TypeName = "money")]
public decimal Budget { get; set; }
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
[Display(Name = "Start Date")]
public DateTime StartDate { get; set; }
[Display(Name = "Administrator")]
public int? InstructorID { get; set; }
[Timestamp]
public byte[] RowVersion { get; set; }
public virtual Instructor Administrator { get; set; }
public virtual ICollection<Course> Courses { get; set; }
}
Atribut časového razítka určuje, že tento sloupec bude zahrnut do Where
klauzule Update
a Delete
příkazů odeslaných do databáze. Atribut se nazývá Časové razítko, protože předchozí verze SQL Server používaly datový typ časového razítka SQL předtím, než ho nahradila verze řádků SQL. Typ .Net pro rowversion je bajtové pole.
Pokud dáváte přednost použití rozhraní FLUENT API, můžete pomocí metody IsConcurrencyToken určit vlastnost tracking, jak je znázorněno v následujícím příkladu:
modelBuilder.Entity<Department>()
.Property(p => p.RowVersion).IsConcurrencyToken();
Přidáním vlastnosti jste změnili model databáze, takže musíte provést další migraci. V konzole Správce balíčků (PMC) zadejte následující příkazy:
Add-Migration RowVersion
Update-Database
Upravit kontroler oddělení
V souboru Controllers\DepartmentController.cs přidejte using
příkaz:
using System.Data.Entity.Infrastructure;
V souboru DepartmentController.cs změňte všechny čtyři výskyty "Příjmení" na "FullName" tak, aby rozevírací seznamy správce oddělení obsahovaly celé jméno instruktora místo příjmení.
ViewBag.InstructorID = new SelectList(db.Instructors, "ID", "FullName");
Nahraďte existující kód pro metodu HttpPost
Edit
následujícím kódem:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Edit(int? id, byte[] rowVersion)
{
string[] fieldsToBind = new string[] { "Name", "Budget", "StartDate", "InstructorID", "RowVersion" };
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
var departmentToUpdate = await db.Departments.FindAsync(id);
if (departmentToUpdate == null)
{
Department deletedDepartment = new Department();
TryUpdateModel(deletedDepartment, fieldsToBind);
ModelState.AddModelError(string.Empty,
"Unable to save changes. The department was deleted by another user.");
ViewBag.InstructorID = new SelectList(db.Instructors, "ID", "FullName", deletedDepartment.InstructorID);
return View(deletedDepartment);
}
if (TryUpdateModel(departmentToUpdate, fieldsToBind))
{
try
{
db.Entry(departmentToUpdate).OriginalValues["RowVersion"] = rowVersion;
await db.SaveChangesAsync();
return RedirectToAction("Index");
}
catch (DbUpdateConcurrencyException ex)
{
var entry = ex.Entries.Single();
var clientValues = (Department)entry.Entity;
var databaseEntry = entry.GetDatabaseValues();
if (databaseEntry == null)
{
ModelState.AddModelError(string.Empty,
"Unable to save changes. The department was deleted by another user.");
}
else
{
var databaseValues = (Department)databaseEntry.ToObject();
if (databaseValues.Name != clientValues.Name)
ModelState.AddModelError("Name", "Current value: "
+ databaseValues.Name);
if (databaseValues.Budget != clientValues.Budget)
ModelState.AddModelError("Budget", "Current value: "
+ String.Format("{0:c}", databaseValues.Budget));
if (databaseValues.StartDate != clientValues.StartDate)
ModelState.AddModelError("StartDate", "Current value: "
+ String.Format("{0:d}", databaseValues.StartDate));
if (databaseValues.InstructorID != clientValues.InstructorID)
ModelState.AddModelError("InstructorID", "Current value: "
+ db.Instructors.Find(databaseValues.InstructorID).FullName);
ModelState.AddModelError(string.Empty, "The record you attempted to edit "
+ "was modified by another user after you got the original value. The "
+ "edit operation was canceled and the current values in the database "
+ "have been displayed. If you still want to edit this record, click "
+ "the Save button again. Otherwise click the Back to List hyperlink.");
departmentToUpdate.RowVersion = databaseValues.RowVersion;
}
}
catch (RetryLimitExceededException /* dex */)
{
//Log the error (uncomment dex variable name and add a line here to write a log.)
ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists, see your system administrator.");
}
}
ViewBag.InstructorID = new SelectList(db.Instructors, "ID", "FullName", departmentToUpdate.InstructorID);
return View(departmentToUpdate);
}
FindAsync
Pokud metoda vrátí hodnotu null, oddělení bylo odstraněno jiným uživatelem. Zobrazený kód používá hodnoty publikovaného formuláře k vytvoření entity oddělení, aby se stránka Upravit znovu zobrazila s chybovou zprávou. Alternativně byste nemuseli znovu vytvářet entitu oddělení, pokud zobrazíte pouze chybovou zprávu bez opětovného zobrazení polí oddělení.
Zobrazení uloží původní RowVersion
hodnotu do skrytého pole a metoda ji přijme v parametru rowVersion
. Před voláním SaveChanges
je nutné vložit původní RowVersion
hodnotu vlastnosti do OriginalValues
kolekce pro entitu. Když pak Entity Framework vytvoří příkaz SQL UPDATE
, bude tento příkaz obsahovat WHERE
klauzuli, která hledá řádek, který má původní RowVersion
hodnotu.
Pokud příkaz neovlivní UPDATE
žádné řádky (žádné řádky nemají původní RowVersion
hodnotu), Entity Framework vyvolá DbUpdateConcurrencyException
výjimku a kód v catch
bloku získá ovlivněnou Department
entitu z objektu výjimky.
var entry = ex.Entries.Single();
Tento objekt má nové hodnoty zadané uživatelem ve své Entity
vlastnosti a můžete získat hodnoty načtené z databáze voláním GetDatabaseValues
metody .
var clientValues = (Department)entry.Entity;
var databaseEntry = entry.GetDatabaseValues();
Metoda GetDatabaseValues
vrátí hodnotu null, pokud někdo odstranil řádek z databáze. V opačném případě musíte přetypovat vrácený objekt do Department
třídy, aby bylo možné získat přístup k vlastnostem Department
. (Vzhledem k tomu, že jste již zkontrolovali odstranění, databaseEntry
bude mít hodnotu null pouze v případě, že oddělení bylo odstraněno po FindAsync
spuštění a před SaveChanges
spuštěním.)
if (databaseEntry == null)
{
ModelState.AddModelError(string.Empty,
"Unable to save changes. The department was deleted by another user.");
}
else
{
var databaseValues = (Department)databaseEntry.ToObject();
Dále kód přidá vlastní chybovou zprávu pro každý sloupec, který obsahuje hodnoty databáze, které se liší od toho, co uživatel zadal na stránce Upravit:
if (databaseValues.Name != currentValues.Name)
ModelState.AddModelError("Name", "Current value: " + databaseValues.Name);
// ...
Delší chybová zpráva vysvětluje, co se stalo a co s tím dělat:
ModelState.AddModelError(string.Empty, "The record you attempted to edit "
+ "was modified by another user after you got the original value. The"
+ "edit operation was canceled and the current values in the database "
+ "have been displayed. If you still want to edit this record, click "
+ "the Save button again. Otherwise click the Back to List hyperlink.");
Nakonec kód nastaví RowVersion
hodnotu objektu Department
na novou hodnotu načtenou z databáze. Tato nová RowVersion
hodnota se uloží do skrytého pole při opětovném zobrazení stránky Upravit a při příštím kliknutí uživatele na Uložit budou zachyceny pouze chyby souběžnosti, ke kterým dochází od opětovného zobrazení stránky Upravit.
V souboru Views\Department\Edit.cshtml přidejte skryté pole pro uložení RowVersion
hodnoty vlastnosti hned za skryté pole vlastnosti DepartmentID
:
@model ContosoUniversity.Models.Department
@{
ViewBag.Title = "Edit";
}
<h2>Edit</h2>
@using (Html.BeginForm())
{
@Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Department</h4>
<hr />
@Html.ValidationSummary(true)
@Html.HiddenFor(model => model.DepartmentID)
@Html.HiddenFor(model => model.RowVersion)
Zpracování souběžnosti testů
Spusťte web a klikněte na Oddělení.
Klikněte pravým tlačítkem na hypertextový odkaz Upravit pro anglické oddělení, vyberte kartu Otevřít v nové a pak klikněte na hypertextový odkaz Upravit pro anglické oddělení. Na dvou kartách se zobrazují stejné informace.
Změňte pole na první kartě prohlížeče a klikněte na Uložit.
V prohlížeči se zobrazí stránka Index se změněnou hodnotou.
Změňte pole na druhé kartě prohlížeče a klikněte na Uložit. Zobrazí se chybová zpráva:
Znovu klikněte na Uložit . Hodnota, kterou jste zadali na druhé kartě prohlížeče, se uloží spolu s původní hodnotou dat, která jste změnili v prvním prohlížeči. Uložené hodnoty se zobrazí, když se zobrazí stránka Index.
Aktualizace stránky Odstranit
Na stránce Odstranit entity Framework detekuje konflikty souběžnosti způsobené tím, že někdo jiný upravuje oddělení podobným způsobem. HttpGet
Delete
Když metoda zobrazí zobrazení potvrzení, toto zobrazení obsahuje původní RowVersion
hodnotu ve skrytém poli. Tato hodnota je pak k dispozici pro metodu HttpPost
Delete
, která se volá, když uživatel potvrdí odstranění. Když Entity Framework vytvoří příkaz SQL DELETE
, obsahuje WHERE
klauzuli s původní RowVersion
hodnotou. Pokud má příkaz za následek nulovou změnu řádků (to znamená, že řádek byl změněn po zobrazení stránky potvrzení odstranění), vyvolá se výjimka souběžnosti a HttpGet Delete
volá se metoda s příznakem chyby nastaveným na true
hodnotu, aby se znovu zobrazila potvrzovací stránka s chybovou zprávou. Je také možné, že nula řádků byla ovlivněna, protože řádek byl odstraněn jiným uživatelem, takže v takovém případě se zobrazí jiná chybová zpráva.
V souboru DepartmentController.cs nahraďte metodu HttpGet
Delete
následujícím kódem:
public async Task<ActionResult> Delete(int? id, bool? concurrencyError)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Department department = await db.Departments.FindAsync(id);
if (department == null)
{
if (concurrencyError.GetValueOrDefault())
{
return RedirectToAction("Index");
}
return HttpNotFound();
}
if (concurrencyError.GetValueOrDefault())
{
ViewBag.ConcurrencyErrorMessage = "The record you attempted to delete "
+ "was modified by another user after you got the original values. "
+ "The delete operation was canceled and the current values in the "
+ "database have been displayed. If you still want to delete this "
+ "record, click the Delete button again. Otherwise "
+ "click the Back to List hyperlink.";
}
return View(department);
}
Metoda přijímá volitelný parametr, který označuje, jestli se stránka znovu zobrazuje po chybě souběžnosti. Pokud je true
tento příznak , je do zobrazení odeslána chybová zpráva pomocí ViewBag
vlastnosti.
Nahraďte kód v HttpPost
Delete
metodě (s názvem DeleteConfirmed
) následujícím kódem:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Delete(Department department)
{
try
{
db.Entry(department).State = EntityState.Deleted;
await db.SaveChangesAsync();
return RedirectToAction("Index");
}
catch (DbUpdateConcurrencyException)
{
return RedirectToAction("Delete", new { concurrencyError = true, id=department.DepartmentID });
}
catch (DataException /* dex */)
{
//Log the error (uncomment dex variable name after DataException and add a line here to write a log.
ModelState.AddModelError(string.Empty, "Unable to delete. Try again, and if the problem persists contact your system administrator.");
return View(department);
}
}
V kódu, který jste právě nahradili, tato metoda přijala pouze ID záznamu:
public async Task<ActionResult> DeleteConfirmed(int id)
Tento parametr jste změnili na Department
instanci entity vytvořenou pořadačem modelu. Tím získáte přístup k hodnotě RowVersion
vlastnosti kromě klíče záznamu.
public async Task<ActionResult> Delete(Department department)
Změnili jste také název metody akce z DeleteConfirmed
na Delete
. Vygenerovaný kód s názvem HttpPost
Delete
metoda DeleteConfirmed
dává HttpPost
metodě jedinečný podpis. ( ClR vyžaduje, aby přetížené metody měly různé parametry metody.) Teď, když jsou podpisy jedinečné, můžete se držet konvence MVC a použít stejný název pro HttpPost
metody a HttpGet
delete.
Pokud dojde k chybě souběžnosti, kód znovu zobrazí stránku potvrzení odstranění a zobrazí příznak, který indikuje, že by se měla zobrazit chybová zpráva souběžnosti.
V souboru Views\Department\Delete.cshtml nahraďte vygenerovaný kód následujícím kódem, který přidá pole chybové zprávy a skrytá pole pro vlastnosti DepartmentID a RowVersion. Změny jsou zvýrazněné.
@model ContosoUniversity.Models.Department
@{
ViewBag.Title = "Delete";
}
<h2>Delete</h2>
<p class="error">@ViewBag.ConcurrencyErrorMessage</p>
<h3>Are you sure you want to delete this?</h3>
<div>
<h4>Department</h4>
<hr />
<dl class="dl-horizontal">
<dt>
Administrator
</dt>
<dd>
@Html.DisplayFor(model => model.Administrator.FullName)
</dd>
<dt>
@Html.DisplayNameFor(model => model.Name)
</dt>
<dd>
@Html.DisplayFor(model => model.Name)
</dd>
<dt>
@Html.DisplayNameFor(model => model.Budget)
</dt>
<dd>
@Html.DisplayFor(model => model.Budget)
</dd>
<dt>
@Html.DisplayNameFor(model => model.StartDate)
</dt>
<dd>
@Html.DisplayFor(model => model.StartDate)
</dd>
</dl>
@using (Html.BeginForm()) {
@Html.AntiForgeryToken()
@Html.HiddenFor(model => model.DepartmentID)
@Html.HiddenFor(model => model.RowVersion)
<div class="form-actions no-color">
<input type="submit" value="Delete" class="btn btn-default" /> |
@Html.ActionLink("Back to List", "Index")
</div>
}
</div>
Tento kód přidá mezi nadpisy a h3
chybovou h2
zprávu:
<p class="error">@ViewBag.ConcurrencyErrorMessage</p>
LastName
Nahradí v Administrator
poli zaFullName
:
<dt>
Administrator
</dt>
<dd>
@Html.DisplayFor(model => model.Administrator.FullName)
</dd>
Nakonec přidá za příkaz skrytá pole pro DepartmentID
vlastnosti Html.BeginForm
aRowVersion
:
@Html.HiddenFor(model => model.DepartmentID)
@Html.HiddenFor(model => model.RowVersion)
Spusťte stránku Index oddělení. Klikněte pravým tlačítkem na hypertextový odkaz Odstranit pro anglické oddělení a vyberte Otevřít na nové kartě a pak na první kartě klikněte na hypertextový odkaz Upravit pro anglické oddělení.
V prvním okně změňte jednu z hodnot a klikněte na Uložit.
Stránka Index změnu potvrdí.
Na druhé kartě klikněte na Odstranit.
Zobrazí se chybová zpráva souběžnosti a hodnoty Oddělení se aktualizují tím, co je aktuálně v databázi.
Pokud znovu kliknete na Odstranit , budete přesměrováni na stránku Index, která ukazuje, že oddělení bylo odstraněno.
Získání kódu
Další materiály
Odkazy na další prostředky Entity Frameworku najdete v tématu ASP.NET Přístup k datům – doporučené zdroje.
Informace o dalších způsobech zpracování různých scénářů souběžnosti najdete v tématech Optimistic Concurrency Patterns a Práce s hodnotami vlastností na webu MSDN. V dalším kurzu se dozvíte, jak implementovat dědičnost tabulek podle hierarchie pro Instructor
entity a Student
.
Další kroky
V tomto kurzu jste:
- Informace o konfliktech souběžnosti
- Přidání optimistické souběžnosti
- Upravený kontroler oddělení
- Otestované zpracování souběžnosti
- Aktualizace stránky Odstranit
V dalším článku se dozvíte, jak implementovat dědičnost v datovém modelu.
Váš názor
https://aka.ms/ContentUserFeedback.
Připravujeme: V průběhu roku 2024 budeme postupně vyřazovat problémy z GitHub coby mechanismus zpětné vazby pro obsah a nahrazovat ho novým systémem zpětné vazby. Další informace naleznete v tématu:Odeslat a zobrazit názory pro