Sdílet prostřednictvím


Kurz: Zpracování souběžnosti pomocí EF v aplikaci ASP.NET MVC 5

V předchozích kurzech jste se naučili aktualizovat data. V tomto kurzu se dozvíte, jak pomocí optimistické souběžnosti zpracovat konflikty, když více uživatelů aktualizuje stejnou entitu současně. Webové stránky, které pracují s entitou Department , změníte tak, aby zpracovávaly chyby souběžnosti. Následující ilustrace znázorňují stránky Upravit a odstranit, včetně některých zpráv, které se zobrazí, pokud dojde ke konfliktu souběžnosti.

Snímek obrazovky se stránkou Upravit s hodnotami pro 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 se naučíte:

  • Další informace o konfliktech souběžnosti
  • Přidání optimistické souběžnosti
  • Úprava kontroleru 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 je 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 nepovolíte detekci takových konfliktů, každý, kdo aktualizuje databázi, naposledy přepíše změny jiného uživatele. V mnoha aplikacích je toto riziko přijatelné: pokud existuje několik uživatelů nebo několik aktualizací, nebo pokud není ve skutečnosti důležité, pokud jsou některé změny přepsány, náklady na programování pro souběžnost můžou výhodu převažovat. V takovém případě nemusíte aplikaci konfigurovat tak, aby zpracovávala konflikty souběžnosti.

Pesimistické souběžnosti (uzamyká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 aktualizaci. Pokud uzamknete řádek pro přístup k aktualizacím, nebudou moct ostatní uživatelé zamknout řádek pro přístup jen pro čtení nebo pro aktualizaci, protože by získali kopii dat, která se právě mění. Pokud uzamknete řádek 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. Program může být složitý. Vyžaduje významné prostředky správy databází a může způsobit problémy s výkonem při nárůstu počtu uživatelů aplikace. Z těchto důvodů ne všechny systémy pro správu databází podporují pesimistické souběžnosti. Entity Framework neposkytuje žádnou integrovanou podporu a tento kurz vám neukazuje, jak ji implementovat.

Optimistická metoda souběžného zpracování

Alternativou k pesimistické souběžnosti je optimistická souběžnost. Optimistická souběžnost znamená, že umožňuje, aby došlo ke konfliktům souběžnosti, a pak odpovídajícím způsobem reagovat, pokud ano. Jan například spustí stránku Úpravy oddělení, změní částku rozpočtu pro anglické oddělení z 350 000,00 USD na 0,00 USD.

Než Jan klikne na Uložit, jane spustí stejnou stránku a změní pole Počáteční datum od 1. 9. 2007 do 8. 8. 2013.

Jan nejprve klikne na Uložit a uvidí změnu, když se prohlížeč vrátí na stránku Index a pak Jane klikne na Uložit. Co se stane dál, určuje způsob zpracování konfliktů souběžnosti. Mezi tyto možnosti patří:

  • Můžete sledovat, kterou vlastnost uživatel upravil, a aktualizovat pouze odpovídající sloupce v databázi. V ukázkovém scénáři by se neztratila žádná data, protože dva uživatelé aktualizovali různé vlastnosti. Když někdo příště přejde do anglického oddělení, uvidí změny Johna i Janeho – počáteční datum 8. 8. 2013 a rozpočet nula dolarů.

    Tato metoda aktualizace může snížit počet konfliktů, které můžou vést ke ztrátě dat, ale nemůže se vyhnout ztrátě dat, pokud jsou u stejné vlastnosti entity provedeny konkurenční změny. To, jestli Entity Framework funguje tímto způsobem, závisí na tom, jak implementujete aktualizační kód. Často to není praktické ve webové aplikaci, protože může vyžadovat, abyste zachovali velké množství stavu, abyste mohli sledovat všechny původní hodnoty vlastností pro entitu i nové hodnoty. Udržování velkého množství stavu může ovlivnit výkon aplikace, protože 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.

  • Janovu změnu můžete nechat přepsat. Když někdo příště přejde do anglického oddělení, uvidí 8.8.2013 a obnovenou hodnotu 350 000,00 USD. To 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 do této části, pokud neproděláte žádné kódování pro zpracování souběžnosti, dojde k tomu automaticky.

  • V databázi můžete zabránit aktualizaci janovy změny. Obvykle se zobrazí chybová zpráva, zobrazí se její aktuální stav dat a umožníte jí znovu použít její změny, pokud je stále chce provést. Tomu se říká scénář wins ve Storu. (Hodnoty úložiště dat mají přednost před hodnotami odeslanými klientem.) V tomto kurzu implementujete scénář Wins pro Store. Tato metoda zajišťuje, aby se nepřepsaly žádné změny, aniž by uživatel upozorňoval 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 Entity Framework schopen detekovat konflikty. Proto je nutné správně nakonfigurovat databázi a datový model. Mezi možnosti povolení detekce konfliktů patří:

  • V tabulce databáze zahrňte sledovací sloupec, který lze použít k určení, kdy byl řádek změněn. Potom můžete rozhraní Entity Framework nakonfigurovat tak, aby zahrnovalo tento sloupec v Where klauzuli SQL Update nebo Delete příkazů.

    Datový typ sloupce sledování je obvykle rowversion. Hodnota rowversion je pořadové číslo, které se při každé aktualizaci řádku zvýší. Update V klauzuli nebo Delete příkaz Where obsahuje původní hodnotu sloupce sledování (původní verze řádku). Pokud byl řádek aktualizovaný jiným uživatelem změněn, hodnota ve rowversion sloupci se liší od původní hodnoty, takže příkaz nemůže Delete najít řádek, který Update se má aktualizovat kvůli Where klauzuli. Když Entity Framework zjistí, že žádné řádky nebyly aktualizovány příkazem nebo Delete příkazem Update (to znamená, že počet ovlivněných řádků je nula), interpretuje to jako konflikt souběžnosti.

  • Nakonfigurujte Entity Framework tak, aby zahrnovala původní hodnoty každého sloupce v tabulce v Where klauzuli Update a Delete příkazy.

    Stejně jako v první možnosti, pokud se od prvního čtení řádku něco v řádku změnilo, Where klauzule nevrátí řádek pro aktualizaci, který Entity Framework interpretuje jako konflikt souběžnosti. U databázových tabulek s mnoha sloupci může tento přístup vést k velmi velkým Where klauzulemi a může vyžadovat, abyste zachovali velké množství stavu. Jak jsme uvedli dříve, udržování velkého objemu 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 tento přístup implementovat ke souběžnosti, je nutné označit všechny vlastnosti jiného než primárního klíče v entitě, pro kterou chcete sledovat souběžnost, přidáním atributu ConcurrencyCheck do nich. Tato změna umožňuje rozhraní Entity Framework zahrnout všechny sloupce do klauzule 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, jestli všechno funguje správně.

Přidání optimistické souběžnosti

Do pole 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ů odesílaných do databáze. Atribut se nazývá Časové razítko, protože předchozí verze SQL Serveru používaly datový typ časového razítka SQL před nahrazením verze řádku SQL. Typ .Net pro rowversion je bajtové pole.

Pokud raději používáte rozhraní FLUENT API, můžete pomocí metody IsConcurrencyToken určit vlastnost sledování, 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, kterou jste změnili model databáze, takže je potřeba provést další migraci. V konzole Správce balíčků (PMC) zadejte následující příkazy:

Add-Migration RowVersion
Update-Database

Úprava kontroleru oddělení

Do pole 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", aby rozevírací seznamy správců oddělení obsahovaly celé jméno instruktora, a ne jenom příjmení.

ViewBag.InstructorID = new SelectList(db.Instructors, "ID", "FullName");

Nahraďte stávají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. Jako alternativu byste nemuseli entitu oddělení znovu vytvořit, pokud se zobrazí jenom chybová zpráva bez opětovného zobrazení polí oddělení.

Zobrazení uloží původní RowVersion hodnotu do skrytého pole a metoda ji obdrží v parametru rowVersion . Než zavoláte SaveChanges, musíte tuto původní RowVersion hodnotu vlastnosti vložit do OriginalValues kolekce pro entitu. Když pak Entity Framework vytvoří příkaz SQL UPDATE , bude tento příkaz obsahovat WHERE klauzuli, která hledá řádek s původní RowVersion hodnotou.

Pokud příkaz UPDATE neovlivní žá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; jinak je nutné 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í, bude mít hodnotu null pouze v případě, databaseEntry že oddělení bylo odstraněno po FindAsync provedení a před SaveChanges provedení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 s hodnotami 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 Pro úpravy a při příštím kliknutí uživatele na Uložit dojde pouze k chybám souběžnosti, ke kterým dochází, protože se při přehrání stránky Pro úpravy zachytí.

V Views\Department\Edit.cshtml přidejte skryté pole pro uložení RowVersion hodnoty vlastnosti bezprostředně 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 myši na hypertextový odkaz Pro anglické oddělení a vyberte Otevřít v nové kartě a potom klikněte na upravit hypertextový odkaz pro anglické oddělení. Dvě karty 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 ukazuje stránku Upravit se zprávou, která vysvětluje, že operace je zrušena, protože hodnota byla změněna jiným uživatelem.

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

U stránky Odstranit služba Entity Framework detekuje konflikty souběžnosti způsobené jiným uživatelem, který upravuje oddělení podobným způsobem. Když metoda HttpGet Delete zobrazí potvrzovací zobrazení, zobrazení obsahuje původní RowVersion hodnotu ve skrytém poli. Tato hodnota je pak k dispozici metodě 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 nulový dopad na řádky (to znamená, že se řádek po zobrazení stránky potvrzení odstranění změnil), vyvolá se výjimka souběžnosti a HttpGet Delete volá se metoda s příznakem chyby nastaveným na true opětovné zobrazení potvrzovací stránky s chybovou zprávou. Je také možné, že byly ovlivněny nulové řádky, protože řádek odstranil jiný uživatel, takže v takovém případě se zobrazí jiná chybová zpráva.

V 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, zda se stránka znovu zobrazuje po chybě souběžnosti. Pokud je truetento příznak , chybová zpráva se odešle do zobrazení pomocí ViewBag vlastnosti.

Nahraďte kód v HttpPost Delete metodě (pojmenovaný 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 vygenerovaný kód, 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 pojmenovaný metodou HttpPost Delete DeleteConfirmed HttpPost , který metodě poskytne jedinečný podpis. ( CLR vyžaduje přetížené metody, aby měly různé parametry metody.) Teď, když jsou podpisy jedinečné, můžete držet konvence MVC a použít stejný název pro HttpPost metody a HttpGet metody delete.

Pokud dojde k chybě souběžnosti, kód znovu zobrazí potvrzovací stránku odstranění a zobrazí příznak, který označuje, že by se měla zobrazit chybová zpráva souběžnosti.

V Zobrazení\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 chybovou h2 h3 zprávu:

<p class="error">@ViewBag.ConcurrencyErrorMessage</p>

LastName Nahradí ho FullName Administrator v poli:

<dt>
  Administrator
</dt>
<dd>
  @Html.DisplayFor(model => model.Administrator.FullName)
</dd>

Nakonec za příkaz přidá skrytá pole a DepartmentID RowVersion vlastnosti Html.BeginForm :

@Html.HiddenFor(model => model.DepartmentID)
@Html.HiddenFor(model => model.RowVersion)

Spusťte stránku Index oddělení. Klikněte pravým tlačítkem myši na odstranit hypertextový odkaz pro anglické oddělení a vyberte Otevřít v nové kartě a potom 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 potvrdí změnu.

Na druhé kartě klikněte na Odstranit.

Zobrazí se chybová zpráva o souběžnosti a hodnoty oddělení se aktualizují s informacemi, které jsou 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 Framework najdete v ASP.NET přístupu k datům – doporučené zdroje informací.

Informace o dalších způsobech zpracování různých scénářů souběžnosti naleznete v tématu Optimistic Concurrency Patterns and Working with Property Values na MSDN. V dalším kurzu se dozvíte, jak implementovat dědičnost tabulek podle hierarchie pro entity Instructor a Student entity.

Další kroky

V tomto kurzu se naučíte:

  • Dozvěděli jste se o konfliktech souběžnosti
  • Přidání optimistické souběžnosti
  • Upravený kontroler oddělení
  • Testování 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.