Sdílet prostřednictvím


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.

Snímek obrazovky se stránkou Upravit s hodnotami Název oddělení, Rozpočet, Počáteční datum a Správce se zvýrazněnými aktuálními hodnotami

Snímek obrazovky se stránkou Odstranit záznam se zprávou o operaci odstranění a tlačítkem Odstranit

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 SQL Update nebo Delete 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 nebo Delete klauzule Where obsahuje původní hodnotu sledovacího sloupce (původní verzi řádku). Pokud aktualizovaný řádek změnil jiný uživatel, hodnota ve rowversion sloupci se liší od původní hodnoty, takže Update příkaz or Delete nemůže kvůli klauzuli najít řádek, který se má aktualizovat Where . Když Entity Framework zjistí, že příkazem nebo Delete nebyly aktualizovány Update žá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 WhereUpdate a Delete 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 SQL WHERE 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 HttpPostEdit 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 SaveChangesje 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:

Snímek obrazovky se stránkou Upravit se zprávou, která vysvětluje, že operace byla zrušena, protože hodnotu změnil jiný uživatel.

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. HttpGetDelete Když metoda zobrazí zobrazení potvrzení, toto zobrazení obsahuje původní RowVersion hodnotu ve skrytém poli. Tato hodnota je pak k dispozici pro metodu HttpPostDelete , 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 HttpGetDelete 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 truetento příznak , je do zobrazení odeslána chybová zpráva pomocí ViewBag vlastnosti.

Nahraďte kód v HttpPostDelete 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 HttpPostDelete 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.

Department_Delete_confirmation_page_with_concurrency_error

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

Stáhnout dokončený projekt

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.