Sdílet prostřednictvím


Zpracování souběžnosti s Rozhraním Entity Framework 4.0 ve webové aplikaci ASP.NET 4

Tom Dykstra

Tato série kurzů vychází z webové aplikace Contoso University vytvořené Začínáme s řadou kurzů Entity Framework 4.0. Pokud jste nedokončí předchozí kurzy, můžete si jako výchozí bod pro tento kurz stáhnout aplikaci , kterou jste vytvořili. Můžete si také stáhnout aplikaci vytvořenou kompletní řadou kurzů. Pokud máte dotazy k kurzům, můžete je publikovat na fóru ASP.NET Entity Framework.

V předchozím kurzu jste zjistili, jak řadit a filtrovat data pomocí ObjectDataSource ovládacího prvku a Entity Frameworku. Tento kurz ukazuje možnosti pro zpracování souběžnosti ve ASP.NET webové aplikaci, která používá Entity Framework. Vytvoříte novou webovou stránku, která je vyhrazená k aktualizaci přiřazení v kanceláři instruktora. Problémy se souběžností budete řešit na této stránce a na stránce Oddělení, kterou jste vytvořili dříve.

Obrázek06

Obrázek01

Konflikty souběžnosti

Ke konfliktu souběžnosti dochází, když jeden uživatel upraví záznam a jiný upraví stejný záznam před zápisem změny prvního uživatele do databáze. Pokud entity Framework nenastavíte tak, aby takové konflikty detekoval, ten, kdo naposledy aktualizuje databázi, přepíše změny jiného uživatele. V mnoha aplikacích je toto riziko přijatelné a nemusíte aplikaci konfigurovat tak, aby zvládala možné konflikty souběžnosti. (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í za souběžnost můžou převážit výhody.) Pokud se nemusíte starat o konflikty souběžnosti, můžete tento kurz přeskočit. zbývající dva kurzy v této sérii nezávisí na ničem, co vytvoříte v tomto.

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á určité 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 Department.aspx , klikne na odkaz Upravit pro oddělení Historie a sníží částku rozpočtu z 1 000 000 USD na 125 000,00 USD. (John spravuje konkurenční oddělení a chce uvolnit peníze pro své vlastní oddělení.)

Obrázek07

Než Jan klikne na Aktualizovat, Jana spustí stejnou stránku, klikne na odkaz Upravit pro oddělení Historie a potom změní pole Počáteční datum z 10. 1. 2011 na 1. 1. 1999. (Jane spravuje oddělení historie a chce mu dát vyšší úroveň.)

Obrázek08

Jan nejdřív klikne na Aktualizovat a pak na Aktualizovat. Jane v prohlížeči teď uvádí částku rozpočtu jako 1 000 000,00 USD, ale není to správné, protože jan změnil částku na 125 000,00 USD.

Mezi akce, které můžete v tomto scénáři provést, 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ž někdo příště projde oddělení historie, uvidí 1. 1. 1999 a 125 000,00 USD.

    Toto je výchozí chování v Entity Frameworku a může výrazně snížit počet konfliktů, které by mohly vést ke ztrátě dat. Toto chování ale nevyhne ztrátě dat, pokud se u stejné vlastnosti entity provedou konkurenční změny. Kromě toho, toto chování není vždy možné; při mapování uložených procedur na typ entity se při provedení jakýchkoli změn entity v databázi aktualizují všechny vlastnosti entity.

  • Můžete nechat Janovu změnu přepsat. Jakmile Jane klikne na Aktualizovat, částka rozpočtu se vrátí na 1 000 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.)

  • 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í znovu zadat změny, pokud je bude chtít provést. Proces můžete dále automatizovat tak, že uložíte její vstup a poskytnete jí možnost ho znovu použít, aniž byste ho museli znovu zadávat. Tento scénář se nazývá Store Wins . (Hodnoty úložiště dat mají přednost před hodnotami odeslanými klientem.)

Zjišťování konfliktů souběžnosti

V Entity Frameworku můžete konflikty vyřešit zpracováním OptimisticConcurrencyException výjimek, které Entity Framework vyvolá. 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 databázi zahrňte sloupec tabulky, 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ů.

    To je účel Timestamp sloupce v tabulce OfficeAssignment .

    Obrázek09

    Datový typ sloupce se Timestamp také nazývá Timestamp. Sloupec ale ve skutečnosti neobsahuje hodnotu data nebo času. Místo toho je hodnota pořadové číslo, které se při každé aktualizaci řádku zvýší. Update V příkazu nebo Delete klauzule Where obsahuje původní Timestamp hodnotu. Pokud aktualizovaný řádek změnil jiný uživatel, hodnota v Timestamp souboru se liší od původní hodnoty, takže Where klauzule nevrátí žádný řádek, který by se měl aktualizovat. Když Entity Framework zjistí, že aktuální Update nebo Delete příkaz neaktualizoval žá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. Tato metoda je stejně efektivní jako použití Timestamp pole, ale může být neefektivní. U databázových tabulek, které mají mnoho sloupců, to může vést k velmi velkým Where klauzulemi a ve webové aplikaci může vyžadovat udržování velkých objemů stavu. Udržování velkých objemů stavu může ovlivnit výkon aplikace, protože buď vyžaduje prostředky serveru (například stav relace), nebo musí být součástí samotné webové stránky (například stav zobrazení).

V tomto kurzu přidáte zpracování chyb pro konflikty optimistické souběžnosti pro entitu, která nemá vlastnost sledování (entitu Department ), a pro entitu, která má vlastnost sledování (entitu OfficeAssignment ).

Zpracování optimistické souběžnosti bez vlastnosti sledování

Pokud chcete implementovat optimistickou souběžnost pro entitu Department , která nemá vlastnost sledování (Timestamp), provedete následující úlohy:

  • Změňte datový model tak, aby umožňoval sledování souběžnosti pro Department entity.
  • SchoolRepository Ve třídě zpracovávat výjimky souběžnosti v SaveChanges metodě.
  • Na stránce Departments.aspx můžete zpracovávat výjimky souběžnosti zobrazením zprávy uživateli s upozorněním, že pokusy o změny byly neúspěšné. Uživatel pak může zobrazit aktuální hodnoty a zkusit změny zopakovat, pokud jsou ještě potřeba.

Povolení sledování souběžnosti v datovém modelu

V sadě Visual Studio otevřete webovou aplikaci Contoso University, se kterou jste pracovali v předchozím kurzu v této sérii.

Otevřete SchoolModel.edmx a v návrháři datového modelu klikněte pravým tlačítkem na Name vlastnost v entitě Department a potom klikněte na Vlastnosti. V okně Vlastnosti změňte vlastnost na ConcurrencyModeFixed.

Obrázek 16

To samé udělejte pro ostatní skalární vlastnosti bez primárního klíče (Budget, StartDatea Administrator.) (U navigačních vlastností to nejde udělat.) To určuje, že pokaždé, když Entity Framework vygeneruje Update příkaz nebo Delete SQL pro aktualizaci Department entity v databázi, musí být tyto sloupce (s původními hodnotami) zahrnuty do klauzule Where . Pokud se při Update spuštění příkazu nebo Delete nenajde žádný řádek, Entity Framework vyvolá výjimku optimistické souběžnosti.

Uložte a zavřete datový model.

Zpracování výjimek souběžnosti v dal

Otevřete Soubor SchoolRepository.cs a přidejte následující using příkaz pro System.Data obor názvů:

using System.Data;

Přidejte následující novou SaveChanges metodu, která zpracovává výjimky optimistické souběžnosti:

public void SaveChanges()
{
    try
    {
        context.SaveChanges();
    }
    catch (OptimisticConcurrencyException ocex)
    {
        context.Refresh(RefreshMode.StoreWins, ocex.StateEntries[0].Entity);
        throw ocex;
    }
}

Pokud při zavolání této metody dojde k chybě souběžnosti, nahradí se hodnoty vlastností entity v paměti hodnotami, které jsou aktuálně v databázi. Výjimka souběžnosti se opakuje, aby ji webová stránka zvládla.

DeleteDepartment V metodách a UpdateDepartment nahraďte stávající volání context.SaveChanges() volánímSaveChanges(), aby bylo možné vyvolat novou metodu.

Zpracování výjimek souběžnosti v prezentační vrstvě

Otevřete Departments.aspx a přidejte OnDeleted="DepartmentsObjectDataSource_Deleted" do DepartmentsObjectDataSource ovládacího prvku atribut. Značka otevření ovládacího prvku teď bude vypadat podobně jako v následujícím příkladu.

<asp:ObjectDataSource ID="DepartmentsObjectDataSource" runat="server" 
        TypeName="ContosoUniversity.BLL.SchoolBL" DataObjectTypeName="ContosoUniversity.DAL.Department" 
        SelectMethod="GetDepartmentsByName" DeleteMethod="DeleteDepartment" UpdateMethod="UpdateDepartment"
        ConflictDetection="CompareAllValues" OldValuesParameterFormatString="orig{0}" 
        OnUpdated="DepartmentsObjectDataSource_Updated" SortParameterName="sortExpression" 
        OnDeleted="DepartmentsObjectDataSource_Deleted" >

V ovládacím DepartmentsGridView prvku zadejte všechny sloupce tabulky v atributu DataKeyNames , jak je znázorněno v následujícím příkladu. Všimněte si, že se tím vytvoří velmi velká pole stavu zobrazení, což je jeden z důvodů, proč je obecně upřednostňovaným způsobem sledování konfliktů souběžnosti použití pole sledování.

<asp:GridView ID="DepartmentsGridView" runat="server" AutoGenerateColumns="False"
        DataSourceID="DepartmentsObjectDataSource" 
        DataKeyNames="DepartmentID,Name,Budget,StartDate,Administrator" 
        OnRowUpdating="DepartmentsGridView_RowUpdating"
        OnRowDataBound="DepartmentsGridView_RowDataBound"
        AllowSorting="True" >

Otevřete Departments.aspx.cs a přidejte následující using příkaz pro System.Data obor názvů:

using System.Data;

Přidejte následující novou metodu, kterou budete volat z obslužných rutin ovládacího prvku Updated zdroje dat a Deleted obslužných rutin událostí pro zpracování výjimek souběžnosti:

private void CheckForOptimisticConcurrencyException(ObjectDataSourceStatusEventArgs e, string function)
{
    if (e.Exception.InnerException is OptimisticConcurrencyException)
    {
        var concurrencyExceptionValidator = new CustomValidator();
        concurrencyExceptionValidator.IsValid = false;
        concurrencyExceptionValidator.ErrorMessage = 
            "The record you attempted to edit or delete was modified by another " +
            "user after you got the original value. The edit or delete operation was canceled " +
            "and the other user's values have been displayed so you can " +
            "determine whether you still want to edit or delete this record.";
        Page.Validators.Add(concurrencyExceptionValidator);
        e.ExceptionHandled = true;
    }
}

Tento kód kontroluje typ výjimky, a pokud se jedná o výjimku souběžnosti, kód dynamicky vytvoří CustomValidator ovládací prvek, který následně zobrazí zprávu v ovládacím ValidationSummary prvku.

Volejte novou metodu z obslužné rutiny Updated události, kterou jste přidali dříve. Kromě toho vytvořte novou Deleted obslužnou rutinu události, která volá stejnou metodu (ale nedělá nic jiného):

protected void DepartmentsObjectDataSource_Updated(object sender, ObjectDataSourceStatusEventArgs e)
{
    if (e.Exception != null)
    {
        CheckForOptimisticConcurrencyException(e, "update");
        // ...
    }
}

protected void DepartmentsObjectDataSource_Deleted(object sender, ObjectDataSourceStatusEventArgs e)
{
    if (e.Exception != null)
    {
        CheckForOptimisticConcurrencyException(e, "delete");
    }
}

Testování optimistické souběžnosti na stránce Oddělení

Spusťte stránku Departments.aspx .

Snímek obrazovky se stránkou Oddělení

Klikněte na Upravit na řádku a změňte hodnotu ve sloupci Rozpočet . (Nezapomeňte, že můžete upravovat jenom záznamy, které jste vytvořili pro účely tohoto kurzu, protože existující School databázové záznamy obsahují některá neplatná data. Záznam ekonomického oddělení je bezpečným záznamem pro experimentování.)

Obrázek 18

Otevřete nové okno prohlížeče a spusťte stránku znovu (zkopírujte adresu URL z pole adresy prvního okna prohlížeče do druhého okna prohlížeče).

Snímek obrazovky s novým oknem prohlížeče, které je připravené pro vstup

Klikněte na Upravit na stejném řádku, který jste upravili dříve, a změňte hodnotu Rozpočet na něco jiného.

Obrázek 19

V druhém okně prohlížeče klikněte na Aktualizovat. Částka rozpočtu se úspěšně změnila na tuto novou hodnotu.

Obrázek 20

V prvním okně prohlížeče klikněte na Aktualizovat. Aktualizace se nezdaří. Částka rozpočtu se znovu zobrazí pomocí hodnoty, kterou jste nastavili v druhém okně prohlížeče, a zobrazí se chybová zpráva.

Obrázek 21

Zpracování optimistické souběžnosti pomocí vlastnosti sledování

Pokud chcete zpracovat optimistickou souběžnost entity s vlastností sledování, provedete následující úlohy:

  • Přidejte do datového modelu uložené procedury pro správu OfficeAssignment entit. (Vlastnosti sledování a uložené procedury se nemusí používat společně, jsou tady jenom seskupené pro ilustraci.)
  • Přidejte metody CRUD do DAL a BLL pro OfficeAssignment entity, včetně kódu pro zpracování výjimek optimistické souběžnosti v DAL.
  • Vytvořte webovou stránku přiřazení office.
  • Otestujte optimistickou souběžnost na nové webové stránce.

Přidání uložených procedur OfficeAssignment do datového modelu

Otevřete soubor SchoolModel.edmx v návrháři modelu, klikněte pravým tlačítkem na návrhovou plochu a klikněte na Aktualizovat model z databáze. Na kartě Přidat v dialogovém okně Zvolte databázové objekty rozbalte uložené procedury , vyberte tři OfficeAssignment uložené procedury (viz následující snímek obrazovky) a potom klikněte na Dokončit. (Tyto uložené procedury již byly v databázi, když jste ji stáhli nebo vytvořili pomocí skriptu.)

Obrázek02

Klikněte pravým tlačítkem na entitu OfficeAssignment a vyberte Mapování uložené procedury.

Obrázek03

Nastavte funkce Insert, Update a Delete tak, aby používaly odpovídající uložené procedury. OrigTimestamp Jako parametr Update funkce nastavte Vlastnost na Timestamp a vyberte možnost Použít původní hodnotu.

Obrázek04

Když Entity Framework zavolá uloženou proceduru UpdateOfficeAssignment , předá původní hodnotu Timestamp sloupce v parametru OrigTimestamp . Uložená procedura používá ve své Where klauzuli tento parametr:

ALTER PROCEDURE [dbo].[UpdateOfficeAssignment]
    @InstructorID int,
    @Location nvarchar(50),
    @OrigTimestamp timestamp
    AS
    UPDATE OfficeAssignment SET Location=@Location 
    WHERE InstructorID=@InstructorID AND [Timestamp]=@OrigTimestamp;
    IF @@ROWCOUNT > 0
    BEGIN
        SELECT [Timestamp] FROM OfficeAssignment 
            WHERE InstructorID=@InstructorID;
    END

Uložená procedura také vybere novou hodnotu Timestamp sloupce po aktualizaci, aby Entity Framework mohl udržovat entitu OfficeAssignment , která je v paměti, synchronizovaná s odpovídajícím řádkem databáze.

(Všimněte si, že uložená procedura pro odstranění přiřazení office nemá OrigTimestamp parametr. Z tohoto důvodu entity Framework nemůže před odstraněním ověřit, jestli se entita nezměnila.)

Uložte a zavřete datový model.

Přidání metod OfficeAssignment do dal

Otevřete soubor ISchoolRepository.cs a přidejte následující metody CRUD pro OfficeAssignment sadu entit:

IEnumerable<OfficeAssignment> GetOfficeAssignments(string sortExpression);
void InsertOfficeAssignment(OfficeAssignment OfficeAssignment);
void DeleteOfficeAssignment(OfficeAssignment OfficeAssignment);
void UpdateOfficeAssignment(OfficeAssignment OfficeAssignment, OfficeAssignment origOfficeAssignment);

Do souboru SchoolRepository.cs přidejte následující nové metody. UpdateOfficeAssignment V metodě voláte místní SaveChanges metodu místo context.SaveChangesmetody .

public IEnumerable<OfficeAssignment> GetOfficeAssignments(string sortExpression)
{
    return new ObjectQuery<OfficeAssignment>("SELECT VALUE o FROM OfficeAssignments AS o", context).Include("Person").OrderBy("it." + sortExpression).ToList();
}

public void InsertOfficeAssignment(OfficeAssignment officeAssignment)
{
    context.OfficeAssignments.AddObject(officeAssignment);
    context.SaveChanges();
}

public void DeleteOfficeAssignment(OfficeAssignment officeAssignment)
{
    context.OfficeAssignments.Attach(officeAssignment);
    context.OfficeAssignments.DeleteObject(officeAssignment);
    context.SaveChanges();
}

public void UpdateOfficeAssignment(OfficeAssignment officeAssignment, OfficeAssignment origOfficeAssignment)
{
    context.OfficeAssignments.Attach(origOfficeAssignment);
    context.ApplyCurrentValues("OfficeAssignments", officeAssignment);
    SaveChanges();
}

V testovacím projektu otevřete Soubor MockSchoolRepository.cs a přidejte do něj následující OfficeAssignment kolekci a metody CRUD. (Napodobené úložiště musí implementovat rozhraní úložiště, jinak se řešení nezkompiluje.)

List<OfficeAssignment> officeAssignments = new List<OfficeAssignment>();
        
public IEnumerable<OfficeAssignment> GetOfficeAssignments(string sortExpression)
{
    return officeAssignments;
}

public void InsertOfficeAssignment(OfficeAssignment officeAssignment)
{
    officeAssignments.Add(officeAssignment);
}

public void DeleteOfficeAssignment(OfficeAssignment officeAssignment)
{
    officeAssignments.Remove(officeAssignment);
}

public void UpdateOfficeAssignment(OfficeAssignment officeAssignment, OfficeAssignment origOfficeAssignment)
{
    officeAssignments.Remove(origOfficeAssignment);
    officeAssignments.Add(officeAssignment);
}

Přidání metod přiřazení OfficeAs do BLL

V hlavním projektu otevřete Soubor SchoolBL.cs a přidejte následující metody CRUD pro entitu nastavenou OfficeAssignment do něj:

public IEnumerable<OfficeAssignment> GetOfficeAssignments(string sortExpression)
{
    if (string.IsNullOrEmpty(sortExpression)) sortExpression = "Person.LastName";
    return schoolRepository.GetOfficeAssignments(sortExpression);
}

public void InsertOfficeAssignment(OfficeAssignment officeAssignment)
{
    try
    {
        schoolRepository.InsertOfficeAssignment(officeAssignment);
    }
    catch (Exception ex)
    {
        //Include catch blocks for specific exceptions first,
        //and handle or log the error as appropriate in each.
        //Include a generic catch block like this one last.
        throw ex;
    }
}

public void DeleteOfficeAssignment(OfficeAssignment officeAssignment)
{
    try
    {
        schoolRepository.DeleteOfficeAssignment(officeAssignment);
    }
    catch (Exception ex)
    {
        //Include catch blocks for specific exceptions first,
        //and handle or log the error as appropriate in each.
        //Include a generic catch block like this one last.
        throw ex;
    }
}

public void UpdateOfficeAssignment(OfficeAssignment officeAssignment, OfficeAssignment origOfficeAssignment)
{
    try
    {
        schoolRepository.UpdateOfficeAssignment(officeAssignment, origOfficeAssignment);
    }
    catch (Exception ex)
    {
        //Include catch blocks for specific exceptions first,
        //and handle or log the error as appropriate in each.
        //Include a generic catch block like this one last.
        throw ex;
    }
}

Vytvoření webové stránky OfficeAssignments

Vytvořte novou webovou stránku, která používá stránku předlohy Site.Master a pojmenujte ji OfficeAssignments.aspx. Do ovládacího prvku s Content názvem Content2přidejte následující kód :

<h2>Office Assignments</h2>
    <asp:ObjectDataSource ID="OfficeAssignmentsObjectDataSource" runat="server" TypeName="ContosoUniversity.BLL.SchoolBL"
        DataObjectTypeName="ContosoUniversity.DAL.OfficeAssignment" SelectMethod="GetOfficeAssignments"
        DeleteMethod="DeleteOfficeAssignment" UpdateMethod="UpdateOfficeAssignment" ConflictDetection="CompareAllValues"
        OldValuesParameterFormatString="orig{0}"
        SortParameterName="sortExpression"  OnUpdated="OfficeAssignmentsObjectDataSource_Updated">
    </asp:ObjectDataSource>
    <asp:ValidationSummary ID="OfficeAssignmentsValidationSummary" runat="server" ShowSummary="true"
        DisplayMode="BulletList" Style="color: Red; width: 40em;" />
    <asp:GridView ID="OfficeAssignmentsGridView" runat="server" AutoGenerateColumns="False"
        DataSourceID="OfficeAssignmentsObjectDataSource" DataKeyNames="InstructorID,Timestamp"
        AllowSorting="True">
        <Columns>
            <asp:CommandField ShowEditButton="True" ShowDeleteButton="True" ItemStyle-VerticalAlign="Top">
                <ItemStyle VerticalAlign="Top"></ItemStyle>
            </asp:CommandField>
            <asp:TemplateField HeaderText="Instructor" SortExpression="Person.LastName">
                <ItemTemplate>
                    <asp:Label ID="InstructorLastNameLabel" runat="server" Text='<%# Eval("Person.LastName") %>'></asp:Label>,
                    <asp:Label ID="InstructorFirstNameLabel" runat="server" Text='<%# Eval("Person.FirstMidName") %>'></asp:Label>
                </ItemTemplate>
            </asp:TemplateField>
            <asp:DynamicField DataField="Location" HeaderText="Location" SortExpression="Location"/>
        </Columns>
        <SelectedRowStyle BackColor="LightGray"></SelectedRowStyle>
    </asp:GridView>

Všimněte si, že v atributu DataKeyNames kód určuje Timestamp vlastnost a klíč záznamu (InstructorID). Zadání vlastností v atributu DataKeyNames způsobí, že ovládací prvek je uloží ve stavu ovládacího prvku (který je podobný stavu zobrazení), aby byly původní hodnoty k dispozici během zpracování zpětného zpracování.

Pokud jste hodnotu neuložili Timestamp , Entity Framework ji nebude mít pro Where klauzuli příkazu SQL Update . V důsledku toho by se nenašlo nic, co by bylo možné aktualizovat. V důsledku toho entity Framework vyvolá při každé OfficeAssignment aktualizaci entity výjimku optimistické souběžnosti.

Otevřete OfficeAssignments.aspx.cs a přidejte následující using příkaz pro vrstvu přístupu k datům:

using ContosoUniversity.DAL;

Přidejte následující Page_Init metodu, která povolí funkci dynamických dat. Přidejte také následující obslužnou rutinu ObjectDataSource pro událost ovládacího prvku Updated , abyste mohli zkontrolovat chyby souběžnosti:

protected void Page_Init(object sender, EventArgs e)
{
    OfficeAssignmentsGridView.EnableDynamicData(typeof(OfficeAssignment));
}

protected void OfficeAssignmentsObjectDataSource_Updated(object sender, ObjectDataSourceStatusEventArgs e)
{
    if (e.Exception != null)
    {
        var concurrencyExceptionValidator = new CustomValidator();
        concurrencyExceptionValidator.IsValid = false;
        concurrencyExceptionValidator.ErrorMessage = "The record you attempted to " +
            "update has been modified by another user since you last visited this page. " +
            "Your update was canceled to allow you to review the other user's " +
            "changes and determine if you still want to update this record.";
        Page.Validators.Add(concurrencyExceptionValidator);
        e.ExceptionHandled = true;
    }
}

Testování optimistické souběžnosti na stránce OfficeAssignments

Spusťte stránku OfficeAssignments.aspx .

Snímek obrazovky se stránkou Přiřazení Office

Klikněte na Upravit v řádku a změňte hodnotu ve sloupci Umístění .

Obrázek 11

Otevřete nové okno prohlížeče a spusťte stránku znovu (zkopírujte adresu URL z prvního okna prohlížeče do druhého okna prohlížeče).

Snímek obrazovky s novým oknem prohlížeče

Klikněte na Upravit na stejném řádku, který jste upravili dříve, a změňte hodnotu Umístění na něco jiného.

Obrázek 12

V druhém okně prohlížeče klikněte na Aktualizovat.

Obrázek 13

Přepněte do prvního okna prohlížeče a klikněte na Aktualizovat.

Obrázek 15

Zobrazí se chybová zpráva a hodnota Umístění byla aktualizována tak, aby zobrazovala hodnotu, na kterou jste ji změnili v druhém okně prohlížeče.

Zpracování souběžnosti pomocí ovládacího prvku EntityDataSource

Ovládací EntityDataSource prvek obsahuje integrovanou logiku, která rozpoznává nastavení souběžnosti v datovém modelu a odpovídajícím způsobem zpracovává operace aktualizace a odstranění. Stejně jako u všech výjimek je však nutné zpracovávat OptimisticConcurrencyException výjimky sami, abyste mohli poskytnout uživatelsky přívětivou chybovou zprávu.

Dále nakonfigurujete stránku Courses.aspx (která používá EntityDataSource ovládací prvek) tak, aby umožňovala operace aktualizace a odstranění a zobrazila chybovou zprávu, pokud dojde ke konfliktu souběžnosti. Entita Course nemá sloupec sledování souběžnosti, takže použijete stejnou metodu, jakou jste použili s entitou Department : sledujte hodnoty všech vlastností, které nejsou klíčové.

Otevřete soubor SchoolModel.edmx . Pro jiné než klíčové vlastnosti Course entity (Title, Creditsa DepartmentID) nastavte vlastnost Režim souběžnosti na Fixedhodnotu . Pak datový model uložte a zavřete.

Otevřete stránku Courses.aspx a proveďte následující změny:

  • V ovládacím CoursesEntityDataSource prvku přidejte EnableUpdate="true" atributy a EnableDelete="true" . Počáteční značka pro tento ovládací prvek se teď podobá následujícímu příkladu:

    <asp:EntityDataSource ID="CoursesEntityDataSource" runat="server" 
            ContextTypeName="ContosoUniversity.DAL.SchoolEntities" EnableFlattening="false" 
            AutoGenerateWhereClause="True" EntitySetName="Courses"
            EnableUpdate="true" EnableDelete="true">
    
  • V ovládacím CoursesGridView prvku změňte hodnotu atributu DataKeyNames na "CourseID,Title,Credits,DepartmentID". Pak do elementu CommandFieldColumns přidejte prvek, který zobrazuje tlačítka Upravit a Odstranit (<asp:CommandField ShowEditButton="True" ShowDeleteButton="True" />). Ovládací GridView prvek se teď podobá následujícímu příkladu:

    <asp:GridView ID="CoursesGridView" runat="server" AutoGenerateColumns="False" 
            DataKeyNames="CourseID,Title,Credits,DepartmentID"
            DataSourceID="CoursesEntityDataSource" >
            <Columns>
                <asp:CommandField ShowEditButton="True" ShowDeleteButton="True" />
                <asp:BoundField DataField="CourseID" HeaderText="CourseID" ReadOnly="True" SortExpression="CourseID" />
                <asp:BoundField DataField="Title" HeaderText="Title" SortExpression="Title" />
                <asp:BoundField DataField="Credits" HeaderText="Credits" SortExpression="Credits" />
            </Columns>
        </asp:GridView>
    

Spusťte stránku a vytvořte konfliktní situaci jako předtím na stránce Oddělení. Spusťte stránku ve dvou oknech prohlížeče, klikněte na Upravit na stejném řádku v každém okně a proveďte v každém z nich jinou změnu. V jednom okně klikněte na Aktualizovat a v druhém okně klikněte na Aktualizovat . Když kliknete na Aktualizovat podruhé, zobrazí se chybová stránka, která je výsledkem neošetřené výjimky souběžnosti.

Obrázek 22

Tuto chybu zpracujete velmi podobným způsobem, jakým jste ji zpracovali u ObjectDataSource ovládacího prvku. Otevřete stránku Courses.aspx a v ovládacím CoursesEntityDataSource prvku zadejte obslužné rutiny pro Deleted události a Updated . Počáteční značka ovládacího prvku teď vypadá podobně jako v následujícím příkladu:

<asp:EntityDataSource ID="CoursesEntityDataSource" runat="server" 
        ContextTypeName="ContosoUniversity.DAL.SchoolEntities" EnableFlattening="false"
        AutoGenerateWhereClause="true" EntitySetName="Courses" 
        EnableUpdate="true" EnableDelete="true" 
        OnDeleted="CoursesEntityDataSource_Deleted" 
        OnUpdated="CoursesEntityDataSource_Updated">

Před ovládací CoursesGridView prvek přidejte následující ValidationSummary ovládací prvek:

<asp:ValidationSummary ID="CoursesValidationSummary" runat="server" 
        ShowSummary="true" DisplayMode="BulletList"  />

V Courses.aspx.cs přidejte using příkaz pro System.Data obor názvů, přidejte metodu, která kontroluje výjimky souběžnosti, a přidejte obslužné rutiny pro EntityDataSource ovládací prvek Updated a Deleted obslužné rutiny. Kód bude vypadat takto:

using System.Data;
protected void CoursesEntityDataSource_Updated(object sender, EntityDataSourceChangedEventArgs e)
{
    CheckForOptimisticConcurrencyException(e, "update");
}

protected void CoursesEntityDataSource_Deleted(object sender, EntityDataSourceChangedEventArgs e)
{
    CheckForOptimisticConcurrencyException(e, "delete");
}

private void CheckForOptimisticConcurrencyException(EntityDataSourceChangedEventArgs e, string function)
{
    if (e.Exception != null && e.Exception is OptimisticConcurrencyException)
    {
        var concurrencyExceptionValidator = new CustomValidator();
        concurrencyExceptionValidator.IsValid = false;
        concurrencyExceptionValidator.ErrorMessage = 
            "The record you attempted to edit or delete was modified by another " +
            "user after you got the original value. The edit or delete operation was canceled " +
            "and the other user's values have been displayed so you can " +
            "determine whether you still want to edit or delete this record.";
        Page.Validators.Add(concurrencyExceptionValidator);
        e.ExceptionHandled = true;
    }
}

Jediným rozdílem mezi tímto kódem a tím, co jste udělali pro ObjectDataSource ovládací prvek, je, že v tomto případě je výjimka souběžnosti ve Exception vlastnosti objektu argumentů události, nikoli ve vlastnosti této výjimky InnerException .

Spusťte stránku a znovu vytvořte konflikt souběžnosti. Tentokrát se zobrazí chybová zpráva:

Obrázek 23

Tím se dokončí úvod do zpracování konfliktů souběžnosti. Další kurz poskytne pokyny ke zlepšení výkonu ve webové aplikaci, která používá Entity Framework.