Obsługa współbieżności z programem Entity Framework 4.0 w aplikacji internetowej ASP.NET 4

Autor : Tom Dykstra

Ta seria samouczków jest oparta na aplikacji internetowej Contoso University utworzonej przez Wprowadzenie z serii samouczków platformy Entity Framework 4.0. Jeśli nie wykonasz wcześniejszych samouczków, jako punkt wyjścia dla tego samouczka możesz pobrać utworzoną aplikację . Możesz również pobrać aplikację utworzoną w ramach pełnej serii samouczków. Jeśli masz pytania dotyczące samouczków, możesz opublikować je na forum ASP.NET Entity Framework.

W poprzednim samouczku przedstawiono sposób sortowania i filtrowania danych przy użyciu kontrolki ObjectDataSource i programu Entity Framework. W tym samouczku przedstawiono opcje obsługi współbieżności w aplikacji internetowej ASP.NET korzystającej z programu Entity Framework. Zostanie utworzona nowa strona internetowa przeznaczona do aktualizowania zadań biura instruktora. Będziesz obsługiwać problemy ze współbieżnością na tej stronie i na stronie Działy, które zostały utworzone wcześniej.

Obraz06

Obraz01

Konflikty współbieżności

Konflikt współbieżności występuje, gdy jeden użytkownik edytuje rekord, a inny użytkownik edytuje ten sam rekord przed zapisaniem zmiany pierwszego użytkownika w bazie danych. Jeśli nie skonfigurujesz programu Entity Framework do wykrywania takich konfliktów, kto po raz ostatni aktualizuje bazę danych, zastępuje zmiany innego użytkownika. W wielu aplikacjach to ryzyko jest dopuszczalne i nie trzeba konfigurować aplikacji do obsługi możliwych konfliktów współbieżności. (Jeśli istnieje niewielu użytkowników lub kilka aktualizacji lub jeśli nie ma naprawdę krytycznego, jeśli niektóre zmiany zostaną zastąpione, koszt programowania na potrzeby współbieżności może przeważyć nad korzyścią). Jeśli nie musisz martwić się o konflikty współbieżności, możesz pominąć ten samouczek; Pozostałe dwa samouczki z tej serii nie zależą od niczego, co tworzysz w tej serii.

Pesymistyczne współbieżność (blokowanie)

Jeśli aplikacja musi zapobiec przypadkowej utracie danych w scenariuszach współbieżności, jednym ze sposobów jest użycie blokad bazy danych. Jest to nazywane pesymistyczną współbieżnością. Na przykład przed odczytaniem wiersza z bazy danych należy zażądać blokady tylko do odczytu lub dostępu do aktualizacji. Jeśli zablokujesz wiersz dostępu do aktualizacji, żaden inny użytkownik nie będzie mógł zablokować wiersza na potrzeby dostępu tylko do odczytu lub aktualizacji, ponieważ otrzymają kopię danych w procesie zmiany. Jeśli zablokujesz wiersz dostępu tylko do odczytu, inne osoby mogą również zablokować go na potrzeby dostępu tylko do odczytu, ale nie do aktualizacji.

Zarządzanie blokadami ma pewne wady. Może to być skomplikowane do programowania. Wymaga to znaczących zasobów zarządzania bazami danych i może powodować problemy z wydajnością w miarę wzrostu liczby użytkowników aplikacji (oznacza to, że nie jest dobrze skalowana). Z tych powodów nie wszystkie systemy zarządzania bazami danych obsługują pesymistyczną współbieżność. Program Entity Framework nie zapewnia wbudowanej obsługi, a w tym samouczku nie pokazano, jak go zaimplementować.

Optymistyczna współbieżność

Alternatywą dla pesymistycznej współbieżności jest optymistyczna współbieżność. Optymistyczna współbieżność oznacza umożliwienie wystąpienia konfliktów współbieżności, a następnie odpowiednie reagowanie, jeśli tak. Na przykład Jan uruchamia stronę Department.aspx , klika link Edytuj dla działu Historii i zmniejsza kwotę budżetu z 1000 000 000 USD do 125 000,000 USD. (John zarządza konkurencyjnym działem i chce uwolnić pieniądze dla własnego działu).

Obraz07

Przed kliknięciem przycisku Aktualizuj Jane uruchomi tę samą stronę, kliknij link Edytuj dla działu historii, a następnie zmieni pole Data rozpoczęcia z 10.01.2011 na 1.1.1.1.1999. (Jane zarządza działem historii i chce dać mu więcej stażu pracy.

Obraz08

Jan klika najpierw pozycję Aktualizuj , a następnie jane klika pozycję Aktualizuj. Przeglądarka Jane wyświetla teraz kwotę budżetu na 1000 000 000 USD, ale jest to niepoprawne, ponieważ kwota została zmieniona przez Johna na 125 000,000 USD.

Niektóre akcje, które można wykonać w tym scenariuszu, obejmują następujące elementy:

  • Możesz śledzić, która właściwość użytkownika zmodyfikowała i zaktualizować tylko odpowiednie kolumny w bazie danych. W przykładowym scenariuszu żadne dane nie zostaną utracone, ponieważ obaj użytkownicy zaktualizowali różne właściwości. Następnym razem, gdy ktoś przegląda dział Historii, zobaczy 1/1/1/1999 i $125,000.00.

    Jest to domyślne zachowanie w programie Entity Framework i może znacznie zmniejszyć liczbę konfliktów, które mogą spowodować utratę danych. Jednak to zachowanie nie pozwala uniknąć utraty danych, jeśli konkurencyjne zmiany są wprowadzane do tej samej właściwości jednostki. Ponadto takie zachowanie nie zawsze jest możliwe; podczas mapowania procedur składowanych na typ jednostki wszystkie właściwości jednostki są aktualizowane po wprowadzeniu zmian w jednostce w bazie danych.

  • Możesz pozwolić zmiany Jane zastąpić zmianę Johna. Po kliknięciu przycisku Aktualizuj kwota budżetu sięga 1 000 000 000 USD. Jest to nazywane klientem wins lub Last in Wins scenariusz. (Wartości klienta mają pierwszeństwo przed tym, co znajduje się w magazynie danych).

  • Możesz zapobiec aktualizowaniu zmiany Jane w bazie danych. Zazwyczaj jest wyświetlany komunikat o błędzie, pokazywanie bieżącego stanu danych i ponowne wprowadzenie zmian, jeśli nadal chce je wprowadzić. Możesz jeszcze bardziej zautomatyzować proces, zapisując jej dane wejściowe i dając jej możliwość ponownego zastosowania bez konieczności ponownego jego wprowadzania. Jest to nazywane scenariuszem Store Wins . (Wartości magazynu danych mają pierwszeństwo przed wartościami przesłanimi przez klienta).

Wykrywanie konfliktów współbieżności

W programie Entity Framework można rozwiązywać konflikty, obsługując OptimisticConcurrencyException wyjątki zgłaszane przez program Entity Framework. Aby wiedzieć, kiedy zgłaszać te wyjątki, program Entity Framework musi mieć możliwość wykrywania konfliktów. Dlatego należy odpowiednio skonfigurować bazę danych i model danych. Niektóre opcje włączania wykrywania konfliktów obejmują następujące opcje:

  • W bazie danych dołącz kolumnę tabeli, która może służyć do określenia, kiedy wiersz został zmieniony. Następnie można skonfigurować platformę Entity Framework tak, aby zawierała kolumnę Where w klauzuli SQL Update lub Delete poleceniach.

    Jest to cel kolumny Timestamp w OfficeAssignment tabeli.

    Obraz09

    Typ danych kolumny Timestamp nosi również nazwę Timestamp. Jednak kolumna nie zawiera wartości daty lub godziny. Zamiast tego wartość jest liczbą sekwencyjną, która jest zwiększana za każdym razem, gdy wiersz jest aktualizowany. W poleceniu lub klauzula Where zawiera oryginalną TimestampDelete wartość.Update Jeśli aktualizowany wiersz został zmieniony przez innego użytkownika, wartość w Timestamp pliku jest inna niż oryginalna wartość, więc klauzula Where nie zwraca wiersza do zaktualizowania. Gdy program Entity Framework wykryje, że żadne wiersze nie zostały zaktualizowane przez bieżące Update polecenie lub Delete (czyli gdy liczba wierszy, których dotyczy problem, wynosi zero), interpretuje to jako konflikt współbieżności.

  • Skonfiguruj platformę Entity Framework, aby uwzględnić oryginalne wartości każdej kolumny w tabeli w Where klauzuli Update i Delete poleceń.

    Podobnie jak w przypadku pierwszej opcji, jeśli coś w wierszu uległo zmianie od czasu pierwszego odczytania wiersza, Where klauzula nie zwróci wiersza do zaktualizowania, który program Entity Framework interpretuje jako konflikt współbieżności. Ta metoda jest tak skuteczna, jak używanie Timestamp pola, ale może być nieefektywna. W przypadku tabel bazy danych, które mają wiele kolumn, może to spowodować bardzo duże Where klauzule, a w aplikacji internetowej może wymagać utrzymania dużych ilości stanu. Utrzymywanie dużych ilości stanu może mieć wpływ na wydajność aplikacji, ponieważ wymaga zasobów serwera (na przykład stanu sesji) lub musi zostać uwzględniona na samej stronie sieci Web (na przykład stan widoku).

W tym samouczku dodasz obsługę błędów dla optymistycznych konfliktów współbieżności dla jednostki, która nie ma właściwości śledzenia ( Department jednostki) i dla jednostki, która ma właściwość śledzenia ( OfficeAssignment jednostkę).

Obsługa optymistycznej współbieżności bez właściwości śledzenia

Aby zaimplementować optymistyczną współbieżność dla Department jednostki, która nie ma właściwości śledzenia (Timestamp), należy wykonać następujące zadania:

  • Zmień model danych, aby włączyć śledzenie współbieżności dla Department jednostek.
  • W klasie obsługa SchoolRepository wyjątków współbieżności w metodzie SaveChanges .
  • Na stronie Departments.aspx obsłuż wyjątki współbieżności, wyświetlając użytkownikowi komunikat ostrzegający o niepomyślnym wprowadzeniu zmian. Użytkownik może następnie zobaczyć bieżące wartości i ponowić próbę wprowadzenia zmian, jeśli są one nadal potrzebne.

Włączanie śledzenia współbieżności w modelu danych

W programie Visual Studio otwórz aplikację internetową Contoso University, z którą pracujesz w poprzednim samouczku w tej serii.

Otwórz plik SchoolModel.edmx i w projektancie modelu danych kliknij prawym przyciskiem myszy Name właściwość w jednostce Department , a następnie kliknij polecenie Właściwości. W oknie Właściwości zmień ConcurrencyMode właściwość na Fixed.

Obraz16

Wykonaj te same czynności dla innych właściwości skalarnych innych niż klucz podstawowy (Budget, StartDatei Administrator.) (Nie można tego zrobić dla właściwości nawigacji). Określa to, że za każdym razem, gdy program Entity Framework wygeneruje Update polecenie lub Delete SQL w celu zaktualizowania Department jednostki w bazie danych, te kolumny (z oryginalnymi wartościami) muszą być uwzględnione w klauzuli Where . Jeśli podczas wykonywania polecenia lub Delete nie zostanie znaleziony Update żaden wiersz, program Entity Framework zgłosi wyjątek optymistycznie współbieżności.

Zapisz i zamknij model danych.

Obsługa wyjątków współbieżności w dal

Otwórz plik SchoolRepository.cs i dodaj następującą using instrukcję System.Data dla przestrzeni nazw:

using System.Data;

Dodaj następującą nową SaveChanges metodę, która obsługuje optymistyczne wyjątki współbieżności:

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

Jeśli podczas wywoływanej metody wystąpi błąd współbieżności, wartości właściwości jednostki w pamięci są zastępowane wartościami aktualnie w bazie danych. Wyjątek współbieżności jest ponownie zmieniany, aby strona internetowa mogła ją obsłużyć.

DeleteDepartment W metodach i UpdateDepartment zastąp istniejące wywołanie metody context.SaveChanges() wywołaniem SaveChanges() metody , aby wywołać nową metodę .

Obsługa wyjątków współbieżności w warstwie prezentacji

Otwórz plik Departments.aspx i dodaj OnDeleted="DepartmentsObjectDataSource_Deleted" atrybut do kontrolki DepartmentsObjectDataSource . Tag otwierający kontrolki będzie teraz podobny do poniższego przykładu.

<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" >

W kontrolce DepartmentsGridView określ wszystkie kolumny tabeli w atrybucie DataKeyNames , jak pokazano w poniższym przykładzie. Należy pamiętać, że spowoduje to utworzenie bardzo dużych pól stanu widoku, co jest jedną z przyczyn, dla których użycie pola śledzenia jest zazwyczaj preferowanym sposobem śledzenia konfliktów współbieżności.

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

Otwórz plik Departments.aspx.cs i dodaj następującą using instrukcję System.Data dla przestrzeni nazw:

using System.Data;

Dodaj następującą nową metodę, która zostanie wywołana z programów obsługi zdarzeń i Deleted procedur obsługi zdarzeń kontroli Updated źródła danych w celu obsługi wyjątków współbieżności:

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;
    }
}

Ten kod sprawdza typ wyjątku, a jeśli jest to wyjątek współbieżności, kod dynamicznie tworzy kontrolkę CustomValidator , która z kolei wyświetla komunikat w kontrolce ValidationSummary .

Wywołaj nową metodę z Updated programu obsługi zdarzeń, który został dodany wcześniej. Ponadto utwórz nową Deleted procedurę obsługi zdarzeń, która wywołuje tę samą metodę (ale nie wykonuje żadnych innych czynności):

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");
    }
}

Testowanie optymistycznej współbieżności na stronie działów

Uruchom stronę Departments.aspx .

Zrzut ekranu przedstawiający stronę Działy.

Kliknij pozycję Edytuj w wierszu i zmień wartość w kolumnie Budżet . Pamiętaj, że możesz edytować tylko rekordy utworzone na potrzeby tego samouczka, ponieważ istniejące School rekordy bazy danych zawierają nieprawidłowe dane. Rekord dla działu ekonomii jest bezpieczny do eksperymentowania z.)

Obraz18

Otwórz nowe okno przeglądarki i ponownie uruchom stronę (skopiuj adres URL z pola adresu pierwszego okna przeglądarki do drugiego okna przeglądarki).

Zrzut ekranu przedstawiający nowe okno przeglądarki gotowe do wprowadzenia danych wejściowych.

Kliknij pozycję Edytuj w tym samym wierszu, który został wcześniej edytowany, i zmień wartość budżetu na inną.

Obraz19

W drugim oknie przeglądarki kliknij pozycję Aktualizuj. Kwota budżetu została pomyślnie zmieniona na tę nową wartość.

Obraz20

W pierwszym oknie przeglądarki kliknij pozycję Aktualizuj. Aktualizacja kończy się niepowodzeniem. Kwota budżetu jest odtwarzana ponownie przy użyciu wartości ustawionej w drugim oknie przeglądarki i zostanie wyświetlony komunikat o błędzie.

Obraz 21

Obsługa optymistycznej współbieżności przy użyciu właściwości śledzenia

Aby obsłużyć optymistyczną współbieżność dla jednostki, która ma właściwość śledzenia, należy wykonać następujące zadania:

  • Dodaj procedury składowane do modelu danych, aby zarządzać jednostkami OfficeAssignment . (Śledzenie właściwości i procedur składowanych nie musi być używane razem; są one po prostu pogrupowane razem tutaj na potrzeby ilustracji).
  • Dodaj metody CRUD do dal i BLL dla OfficeAssignment jednostek, w tym kod do obsługi optymistycznych wyjątków współbieżności w dal.
  • Utwórz stronę internetową przypisań pakietu Office.
  • Przetestuj optymistyczną współbieżność na nowej stronie internetowej.

Dodawanie procedur składowanych officeAssignment do modelu danych

Otwórz plik SchoolModel.edmx w projektancie modelu, kliknij prawym przyciskiem myszy powierzchnię projektową, a następnie kliknij polecenie Aktualizuj model z bazy danych. Na karcie Dodaj w oknie dialogowym Wybieranie obiektów bazy danych rozwiń węzeł Procedury składowane i wybierz trzy OfficeAssignment procedury składowane (zobacz poniższy zrzut ekranu), a następnie kliknij przycisk Zakończ. (Te procedury składowane znajdowały się już w bazie danych podczas pobierania lub tworzenia ich przy użyciu skryptu).

Obraz02

Kliknij prawym przyciskiem myszy OfficeAssignment jednostkę i wybierz polecenie Mapowanie procedury składowanej.

Obraz03

Ustaw funkcje Insert, Update i Delete , aby używać odpowiednich procedur składowanych. OrigTimestamp Dla parametru Update funkcji ustaw właściwość na Timestamp i wybierz opcję Użyj oryginalnej wartości.

Obraz04

Gdy program Entity Framework wywołuje procedurę UpdateOfficeAssignment składowaną, przekaże oryginalną wartość Timestamp kolumny w parametrze OrigTimestamp . Procedura składowana używa tego parametru w klauzuli Where :

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

Procedura składowana wybiera również nową wartość Timestamp kolumny po aktualizacji, aby program Entity Framework mógł zachować jednostkę, która jest w pamięci zsynchronizowana OfficeAssignment z odpowiednim wierszem bazy danych.

(Należy pamiętać, że procedura składowana usuwania przypisania pakietu Office nie ma parametru OrigTimestamp . W związku z tym program Entity Framework nie może sprawdzić, czy jednostka nie zmieniła się przed usunięciem.

Zapisz i zamknij model danych.

Dodawanie metod officeAssignment do dal

Otwórz plik ISchoolRepository.cs i dodaj następujące metody CRUD dla OfficeAssignment zestawu jednostek:

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

Dodaj następujące nowe metody do pliku SchoolRepository.cs. W metodzie UpdateOfficeAssignment wywołasz metodę lokalną SaveChanges zamiast context.SaveChanges.

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

W projekcie testowym otwórz plik MockSchoolRepository.cs i dodaj do niego następującą OfficeAssignment kolekcję i metody CRUD. (Makiety repozytorium musi zaimplementować interfejs repozytorium lub rozwiązanie nie zostanie skompilowane).

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);
}

Dodawanie metod officeAssignment do BLL

W projekcie głównym otwórz plik SchoolBL.cs i dodaj następujące metody CRUD dla jednostki ustawionej OfficeAssignment na nią:

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;
    }
}

Tworzenie strony sieci Web OfficeAssignments

Utwórz nową stronę sieci Web, która używa strony wzorcowej Site.Master i nadaj jej nazwę OfficeAssignments.aspx. Dodaj następujące znaczniki do kontrolki Content o nazwie Content2:

<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>

Zwróć uwagę, że w atrybucie DataKeyNames znacznik określa Timestamp właściwość, a także klucz rekordu (InstructorID). Określenie właściwości w atrybucie DataKeyNames powoduje, że kontrolka zapisuje je w stanie kontrolnym (co jest podobne do stanu widoku), dzięki czemu oryginalne wartości są dostępne podczas przetwarzania zwrotnego.

Jeśli wartość nie zostanie zapisana Timestamp , program Entity Framework nie będzie miał go dla Where klauzuli polecenia SQL Update . W związku z tym nie znaleziono nic do zaktualizowania. W związku z tym platforma Entity Framework zgłaszałaby optymistyczny wyjątek współbieżności za każdym razem, gdy OfficeAssignment jednostka jest aktualizowana.

Otwórz plik OfficeAssignments.aspx.cs i dodaj następującą using instrukcję dla warstwy dostępu do danych:

using ContosoUniversity.DAL;

Dodaj następującą Page_Init metodę, która umożliwia korzystanie z funkcji danych dynamicznych. Dodaj również następującą procedurę obsługi dla ObjectDataSource zdarzenia kontrolki Updated , aby sprawdzić, czy występują błędy współbieżności:

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;
    }
}

Testowanie optymistycznej współbieżności na stronie OfficeAssignments

Uruchom stronę OfficeAssignments.aspx .

Zrzut ekranu przedstawiający stronę Przypisania pakietu Office.

Kliknij pozycję Edytuj w wierszu i zmień wartość w kolumnie Lokalizacja .

Obraz11

Otwórz nowe okno przeglądarki i ponownie uruchom stronę (skopiuj adres URL z pierwszego okna przeglądarki do drugiego okna przeglądarki).

Zrzut ekranu przedstawiający nowe okno przeglądarki.

Kliknij przycisk Edytuj w tym samym wierszu, który został wcześniej edytowany, i zmień wartość Location na coś innego.

Obraz12

W drugim oknie przeglądarki kliknij pozycję Aktualizuj.

Obraz13

Przejdź do pierwszego okna przeglądarki i kliknij przycisk Aktualizuj.

Obraz15

Zostanie wyświetlony komunikat o błędzie i wartość Lokalizacja została zaktualizowana, aby pokazać wartość zmienioną na w drugim oknie przeglądarki.

Obsługa współbieżności za pomocą kontrolki EntityDataSource

Kontrolka EntityDataSource zawiera wbudowaną logikę, która rozpoznaje ustawienia współbieżności w modelu danych i odpowiednio obsługuje operacje aktualizacji i usuwania. Jednak podobnie jak w przypadku wszystkich wyjątków, należy samodzielnie obsługiwać OptimisticConcurrencyException wyjątki, aby zapewnić przyjazny dla użytkownika komunikat o błędzie.

Następnie skonfigurujesz stronę Courses.aspx (która używa EntityDataSource kontrolki), aby zezwolić na operacje aktualizacji i usuwania oraz wyświetlić komunikat o błędzie, jeśli wystąpi konflikt współbieżności. Jednostka Course nie ma kolumny śledzenia współbieżności, dlatego użyjesz tej samej metody, która została użyta w jednostce Department : śledź wartości wszystkich właściwości innych niż klucz.

Otwórz plik SchoolModel.edmx . Dla właściwości Course innych niż klucz jednostki (Title, Creditsi DepartmentID) ustaw właściwość Tryb współbieżności na Fixedwartość . Następnie zapisz i zamknij model danych.

Otwórz stronę Courses.aspx i wprowadź następujące zmiany:

  • W kontrolce CoursesEntityDataSource dodaj EnableUpdate="true" atrybuty i EnableDelete="true" . Tag otwierający dla tej kontrolki jest teraz podobny do następującego przykładu:

    <asp:EntityDataSource ID="CoursesEntityDataSource" runat="server" 
            ContextTypeName="ContosoUniversity.DAL.SchoolEntities" EnableFlattening="false" 
            AutoGenerateWhereClause="True" EntitySetName="Courses"
            EnableUpdate="true" EnableDelete="true">
    
  • W kontrolce CoursesGridView zmień wartość atrybutu DataKeyNames na "CourseID,Title,Credits,DepartmentID". Następnie dodaj CommandField element do Columns elementu, który zawiera przyciski Edytuj i Usuń (<asp:CommandField ShowEditButton="True" ShowDeleteButton="True" />). Kontrolka GridView jest teraz podobna do następującego przykładu:

    <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>
    

Uruchom stronę i utwórz sytuację powodującą konflikt, tak jak wcześniej na stronie Działy. Uruchom stronę w dwóch oknach przeglądarki, kliknij przycisk Edytuj w tym samym wierszu w każdym oknie i wprowadź inną zmianę w każdym z nich. Kliknij przycisk Aktualizuj w jednym oknie, a następnie kliknij przycisk Aktualizuj w drugim oknie. Po kliknięciu przycisku Aktualizuj po raz drugi zostanie wyświetlona strona błędu, która wynika z nieobsługiwanego wyjątku współbieżności.

Obraz22

Ten błąd jest bardzo podobny do sposobu obsługi tej kontrolki ObjectDataSource . Otwórz stronę Courses.aspx i w kontrolce CoursesEntityDataSource określ programy obsługi dla zdarzeń Deleted i Updated . Tag otwierający kontrolki przypomina teraz następujący przykład:

<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">

Przed kontrolką dodaj następującą ValidationSummary kontrolkęCoursesGridView:

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

W pliku Courses.aspx.cs dodaj instrukcję dla System.Data przestrzeni nazw, dodaj metodę using sprawdzającą wyjątki współbieżności i dodając programy obsługi dla EntityDataSource kontrolek Updated i Deleted procedur obsługi. Kod będzie wyglądać następująco:

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;
    }
}

Jedyną różnicą między tym kodem a tym, co zrobiliśmy dla ObjectDataSource kontrolki, jest to, że w tym przypadku wyjątek współbieżności znajduje się we Exception właściwości obiektu argumentów zdarzeń, a nie we właściwości tego wyjątku InnerException .

Uruchom stronę i ponownie utwórz konflikt współbieżności. Tym razem zostanie wyświetlony komunikat o błędzie:

Obraz23

Spowoduje to ukończenie wprowadzenia do obsługi konfliktów współbieżności. W następnym samouczku przedstawiono wskazówki dotyczące zwiększania wydajności w aplikacji internetowej korzystającej z programu Entity Framework.