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.
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).
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.
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 SQLUpdate
lubDelete
poleceniach.Jest to cel kolumny
Timestamp
wOfficeAssignment
tabeli.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 klauzulaWhere
zawiera oryginalnąTimestamp
Delete
wartość.Update
Jeśli aktualizowany wiersz został zmieniony przez innego użytkownika, wartość wTimestamp
pliku jest inna niż oryginalna wartość, więc klauzulaWhere
nie zwraca wiersza do zaktualizowania. Gdy program Entity Framework wykryje, że żadne wiersze nie zostały zaktualizowane przez bieżąceUpdate
polecenie lubDelete
(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
klauzuliUpdate
iDelete
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żywanieTimestamp
pola, ale może być nieefektywna. W przypadku tabel bazy danych, które mają wiele kolumn, może to spowodować bardzo dużeWhere
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 metodzieSaveChanges
. - 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
.
Wykonaj te same czynności dla innych właściwości skalarnych innych niż klucz podstawowy (Budget
, StartDate
i 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 .
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.)
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).
Kliknij pozycję Edytuj w tym samym wierszu, który został wcześniej edytowany, i zmień wartość budżetu na inną.
W drugim oknie przeglądarki kliknij pozycję Aktualizuj. Kwota budżetu została pomyślnie zmieniona na tę nową wartość.
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.
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).
Kliknij prawym przyciskiem myszy OfficeAssignment
jednostkę i wybierz polecenie Mapowanie procedury składowanej.
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.
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 .
Kliknij pozycję Edytuj w wierszu i zmień wartość w kolumnie Lokalizacja .
Otwórz nowe okno przeglądarki i ponownie uruchom stronę (skopiuj adres URL z pierwszego okna przeglądarki do drugiego okna przeglądarki).
Kliknij przycisk Edytuj w tym samym wierszu, który został wcześniej edytowany, i zmień wartość Location na coś innego.
W drugim oknie przeglądarki kliknij pozycję Aktualizuj.
Przejdź do pierwszego okna przeglądarki i kliknij przycisk Aktualizuj.
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
, Credits
i DepartmentID
) ustaw właściwość Tryb współbieżności na Fixed
wartość . Następnie zapisz i zamknij model danych.
Otwórz stronę Courses.aspx i wprowadź następujące zmiany:
W kontrolce
CoursesEntityDataSource
dodajEnableUpdate="true"
atrybuty iEnableDelete="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ść atrybutuDataKeyNames
na"CourseID,Title,Credits,DepartmentID"
. Następnie dodajCommandField
element doColumns
elementu, który zawiera przyciski Edytuj i Usuń (<asp:CommandField ShowEditButton="True" ShowDeleteButton="True" />
). KontrolkaGridView
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.
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:
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.