Sdílet prostřednictvím


Zpracování souběžnosti s rozhraním Entity Framework v aplikaci ASP.NET MVC (7 z 10)

Tom Dykstra

Ukázková webová aplikace Contoso University ukazuje, jak vytvářet aplikace ASP.NET MVC 4 pomocí entity Framework 5 Code First a sady Visual Studio 2012. Informace o sérii kurzů najdete v prvním kurzu v této sérii.

Poznámka

Pokud narazíte na problém, který nemůžete vyřešit, stáhněte si dokončenou kapitolu a zkuste problém reprodukovat. Obecně můžete najít řešení problému porovnáním kódu s dokončeným kódem. Informace o některých běžných chybách a jejich řešení najdete v tématu Chyby a alternativní řešení.

V předchozích dvou kurzech jste pracovali se souvisejícími daty. Tento kurz ukazuje, jak zpracovávat souběžnost. Vytvoříte webové stránky, které s entitou Department pracují, a stránky, které upravují a odstraňují Department entity, budou zpracovávat chyby souběžnosti. Následující ilustrace znázorňují stránky Index a Delete, včetně některých zpráv, které se zobrazí v případě konfliktu souběžnosti.

Snímek obrazovky znázorňující stránku Oddělení univerzit contoso před úpravami

Snímek obrazovky se stránkou Univerzita se zprávou, že operace se zrušila, protože hodnotu změnil jiný uživatel.

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 (to znamená, že se dobře škáluje). 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.

Changing_English_dept_budget_to_100000

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.

Changing_English_dept_start_date_to_1999

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 ovlivnit výkon aplikace, protože vyžaduje prostředky serveru nebo musí být zahrnuta do samotné webové stránky (například do skrytých polí).

  • 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 . (Hodnoty 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ých objemů stavu může ovlivnit výkon aplikace, protože buď vyžaduje prostředky serveru, nebo musí být součástí samotné webové stránky. 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í vlastnosti optimistické souběžnosti do entity oddělení

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)]
    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();

Projděte si problém s githubem Replace IsConcurrencyToken by IsRowVersion.

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

Vytvoření kontroleru oddělení

Pomocí Department následujícího nastavení vytvořte kontroler a zobrazení stejným způsobem jako u ostatních kontrolerů:

Add_Controller_dialog_box_for_Department_controller

V souboru Controllers\DepartmentController.cs přidejte using příkaz:

using System.Data.Entity.Infrastructure;

Změňte "Příjmení" na "FullName" všude v tomto souboru (čtyři výskyty), aby rozevírací seznamy správce oddělení obsahovaly celé jméno instruktora, a ne jenom příjmení.

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

Nahraďte existující kód pro metodu HttpPostEdit následujícím kódem:

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(
   [Bind(Include = "DepartmentID, Name, Budget, StartDate, RowVersion, InstructorID")]
    Department department)
{
   try
   {
      if (ModelState.IsValid)
      {
         db.Entry(department).State = EntityState.Modified;
         db.SaveChanges();
         return RedirectToAction("Index");
      }
   }
   catch (DbUpdateConcurrencyException ex)
   {
      var entry = ex.Entries.Single();
      var clientValues = (Department)entry.Entity;
      var databaseValues = (Department)entry.GetDatabaseValues().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.");
      department.RowVersion = databaseValues.RowVersion;
   }
   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 save changes. Try again, and if the problem persists contact your system administrator.");
   }

   ViewBag.InstructorID = new SelectList(db.Instructors, "InstructorID", "FullName", department.InstructorID);
   return View(department);
}

Zobrazení uloží původní RowVersion hodnotu do skrytého pole. Když pořadač modelu vytvoří department instanci, bude mít tento objekt původní RowVersion hodnotu vlastnosti a nové hodnoty pro ostatní vlastnosti, jak uživatel zadal na stránce Upravit. 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. Tato entita má hodnoty načtené z databáze i nové hodnoty zadané uživatelem:

var entry = ex.Entries.Single();
var clientValues = (Department)entry.Entity;
var databaseValues = (Department)entry.GetDatabaseValues().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 bude uložena ve skrytém poli při opětovném zobrazení stránky Pro úpravy a při dalším kliknutí uživatele na Uložit budou zachyceny pouze chyby souběžnosti, ke kterým dochází, protože se znovu zobrazí stránka Upravit.

Do pole 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()
    @Html.ValidationSummary(true)

    <fieldset>
        <legend>Department</legend>

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

        <div class="editor-label">
            @Html.LabelFor(model => model.Name)
        </div>

V souboru Views\Department\Index.cshtml nahraďte stávající kód následujícím kódem, kterým přesunete odkazy řádků doleva a změníte název stránky a záhlaví sloupců tak, aby se zobrazovaly FullName místo LastName ve sloupci Administrator :

@model IEnumerable<ContosoUniversity.Models.Department>

@{
    ViewBag.Title = "Departments";
}

<h2>Departments</h2>

<p>
    @Html.ActionLink("Create New", "Create")
</p>
<table>
    <tr>
        <th></th>
        <th>Name</th>
        <th>Budget</th>
        <th>Start Date</th>
        <th>Administrator</th>
    </tr>

@foreach (var item in Model) {
    <tr>
        <td>
            @Html.ActionLink("Edit", "Edit", new { id=item.DepartmentID }) |
            @Html.ActionLink("Details", "Details", new { id=item.DepartmentID }) |
            @Html.ActionLink("Delete", "Delete", new { id=item.DepartmentID })
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.Name)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.Budget)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.StartDate)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.Administrator.FullName)
        </td>
    </tr>
}

</table>

Testování zpracování optimistické souběžnosti

Spusťte web a klikněte na Oddělení:

Snímek obrazovky se stránkou Contoso University Departments

Klikněte pravým tlačítkem na hypertextový odkaz Upravit pro Kim Abercrombie, vyberte Otevřít na nové kartě a pak klikněte na upravit hypertextový odkaz pro Kim Abercrombie. Obě okna zobrazují stejné informace.

Department_Edit_page_before_changes

Změňte pole v prvním okně prohlížeče a klikněte na Uložit.

Department_Edit_page_1_after_change

Prohlížeč zobrazí stránku Index se změněnou hodnotou.

Departments_Index_page_after_first_budget_edit

Změňte libovolné pole v druhém okně prohlížeče a klikněte na Uložit.

Department_Edit_page_2_after_change

V druhém okně prohlížeče klikněte na Uložit . Zobrazí se chybová zpráva:

Snímek obrazovky se stránkou University s chybovou zprávou připravenou k tomu, aby uživatel znovu vybral Uložit.

Znovu klikněte na Uložit . Hodnota, kterou jste zadali v druhém prohlížeči, se uloží spolu s původní hodnotou dat, která jste změnili v prvním prohlížeči. Když se zobrazí stránka Index, zobrazí se uložené hodnoty.

Department_Index_page_with_change_from_second_browser

Aktualizace stránky Pro odstranění

Na stránce Odstranit Zjistí Entity Framework konflikty souběžnosti způsobené tím, že někdo jiný upravuje oddělení podobným způsobem. HttpGetDelete Když metoda zobrazí potvrzovací zobrazení, bude zobrazení obsahovat 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 nulový počet řádků (to znamená, že řádek byl změněn po zobrazení potvrzovací stránky Odstranit), vyvolá se výjimka souběžnosti a HttpGet Delete zavolá 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 nulový počet řádků byl ovlivněn, 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 ActionResult Delete(int id, bool? concurrencyError)
{
    Department department = db.Departments.Find(id);

    if (concurrencyError.GetValueOrDefault())
    {
        if (department == null)
        {
            ViewBag.ConcurrencyErrorMessage = "The record you attempted to delete "
                + "was deleted by another user after you got the original values. "
                + "Click the Back to List hyperlink.";
        }
        else
        {
            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 je stránka po chybě souběžnosti znovu zobrazena. Pokud je truetento příznak , odešle se do zobrazení 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 ActionResult Delete(Department department)
{
    try
    {
        db.Entry(department).State = EntityState.Deleted;
        db.SaveChanges();
        return RedirectToAction("Index");
    }
    catch (DbUpdateConcurrencyException)
    {
        return RedirectToAction("Delete", new { concurrencyError=true } );
    }
    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 ActionResult DeleteConfirmed(int id)

Tento parametr jste změnili na Department instanci entity vytvořenou pořadačem modelu. Získáte tak přístup k hodnotě RowVersion vlastnosti kromě klíče záznamu.

public ActionResult Delete(Department department)

Také jste změnili 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 zůstat u konvence MVC a použít stejný název pro HttpPost metody a HttpGet delete.

Pokud dojde k chybě souběžnosti, kód znovu zobrazí potvrzovací stránku Odstranit a poskytne příznak, který označuje, ž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ý provede některé změny formátování a přidá pole s chybovou zprávou. 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>
<fieldset>
    <legend>Department</legend>

    <div class="display-label">
         @Html.DisplayNameFor(model => model.Name)
    </div>
    <div class="display-field">
        @Html.DisplayFor(model => model.Name)
    </div>

    <div class="display-label">
         @Html.DisplayNameFor(model => model.Budget)
    </div>
    <div class="display-field">
        @Html.DisplayFor(model => model.Budget)
    </div>

    <div class="display-label">
         @Html.DisplayNameFor(model => model.StartDate)
    </div>
    <div class="display-field">
        @Html.DisplayFor(model => model.StartDate)
    </div>

    <div class="display-label">
         @Html.DisplayNameFor(model => model.Administrator.FullName)
    </div>
    <div class="display-field">
        @Html.DisplayFor(model => model.Administrator.FullName)
    </div>
</fieldset>
@using (Html.BeginForm()) {
    @Html.AntiForgeryToken()
   @Html.HiddenFor(model => model.DepartmentID)
    @Html.HiddenFor(model => model.RowVersion)
    <p>
        <input type="submit" value="Delete" /> |
        @Html.ActionLink("Back to List", "Index")
    </p>
}

Tento kód přidá mezi nadpisy a h3 chybovou h2 zprávu:

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

V poli se nahradí LastName za FullNameAdministrator :

<div class="display-label">
    @Html.LabelFor(model => model.InstructorID)
</div>
<div class="display-field">
    @Html.DisplayFor(model => model.Administrator.FullName)
</div>

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

@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 hypertextový odkaz Odstranit pro anglické oddělení a vyberte Otevřít v novém okně a pak v prvním okně 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 :

Department_Edit_page_after_change_before_delete

Stránka Index změnu potvrdí.

Departments_Index_page_after_budget_edit_before_delete

V druhém okně klikněte na Odstranit.

Department_Delete_confirmation_page_before_concurrency_error

Zobrazí se chybová zpráva o souběžnosti a hodnoty Oddělení se aktualizují s 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 Rejstřík, která ukazuje, že oddělení bylo odstraněno.

Souhrn

Tím se dokončí úvod do zpracování konfliktů souběžnosti. Informace o dalších způsobech zpracování různých scénářů souběžnosti najdete v tématech Vzorce optimistické souběžnosti a Práce s hodnotami vlastností na blogu týmu Entity Framework. V dalším kurzu se dozvíte, jak implementovat dědičnost tabulek v hierarchii pro Instructor entity a Student .

Odkazy na další prostředky Entity Framework najdete v mapě obsahu ASP.NET Data Access.