Notatka
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Optymistyczna współbieżność polega na optymistycznej próbie zapisania encji w bazie danych w nadziei, że dane tam nie uległy zmianie od czasu załadowania encji. Jeśli okaże się, że dane uległy zmianie, zostanie zgłoszony wyjątek i musisz rozwiązać konflikt przed podjęciem próby zapisania ponownie. W tym temacie opisano sposób obsługi takich wyjątków w programie Entity Framework. Techniki przedstawione w tym temacie mają zastosowanie w równym stopniu do modeli utworzonych przy użyciu funkcji Code First i projektanta EF.
Ten wpis nie jest odpowiednim miejscem na pełną dyskusję na temat optymistycznej współbieżności. W poniższych sekcjach zakłada się pewną wiedzę na temat rozwiązywania współbieżności i przedstawia wzorce typowych zadań.
Wiele z tych wzorców korzysta z tematów omówionych w temacie Praca z wartościami właściwości.
Rozwiązywanie problemów ze współbieżnością podczas korzystania z niezależnych skojarzeń (gdzie klucz obcy nie jest mapowany na właściwość w jednostce) jest znacznie trudniejsze niż w przypadku używania skojarzeń kluczy obcych. W związku z tym, jeśli zamierzasz w swojej aplikacji rozwiązywać problemy współbieżności, zaleca się, aby zawsze przypisywać klucze obce do encji. We wszystkich poniższych przykładach przyjęto założenie, że używasz skojarzeń kluczy obcych.
Wyjątek DbUpdateConcurrencyException jest zgłaszany przez program SaveChanges, gdy podczas próby zapisania jednostki używającej skojarzeń klucza obcego zostanie wykryty optymistyczny wyjątek współbieżności.
Rozwiązywanie wyjątków współbieżności w trybie optymistycznym za pomocą funkcji Reload (wygrywa baza danych)
Metoda Reload może być użyta do nadpisania bieżących wartości encji wartościami aktualnie znajdującymi się w bazie danych. Jednostka jest następnie zwykle zwracana użytkownikowi w jakiejś formie i musi spróbować ponownie wprowadzić zmiany i ponownie zapisać. Przykład:
using (var context = new BloggingContext())
{
var blog = context.Blogs.Find(1);
blog.Name = "The New ADO.NET Blog";
bool saveFailed;
do
{
saveFailed = false;
try
{
context.SaveChanges();
}
catch (DbUpdateConcurrencyException ex)
{
saveFailed = true;
// Update the values of the entity that failed to save from the store
ex.Entries.Single().Reload();
}
} while (saveFailed);
}
Dobrym sposobem symulowania wyjątku współbieżności jest ustawienie punktu przerwania w wywołaniu SaveChanges, a następnie zmodyfikowanie jednostki, która jest zapisywana w bazie danych przy użyciu innego narzędzia, takiego jak SQL Server Management Studio. Możesz również wstawić wiersz przed poleceniem SaveChanges, aby zaktualizować bazę danych bezpośrednio przy użyciu polecenia SqlCommand. Przykład:
context.Database.SqlCommand(
"UPDATE dbo.Blogs SET Name = 'Another Name' WHERE BlogId = 1");
Metoda Entries w DbUpdateConcurrencyException zwraca wystąpienia DbEntityEntry dla jednostek, które nie zostały zaktualizowane. Ta właściwość obecnie zawsze zwraca pojedynczą wartość z powodu problemów ze współbieżnością. Może zwracać wiele wartości w przypadku ogólnych wyjątków aktualizacji. Alternatywą w niektórych sytuacjach może być pobranie wpisów dla wszystkich jednostek, które mogą wymagać ponownego załadowania z bazy danych i ponowne załadowanie każdej z nich.
Rozwiązywanie optymistycznych wyjątków współbieżności, gdy klient wygrywa
W powyższym przykładzie użycie funkcji ponownego ładowania jest czasami określane jako zwycięstwo bazy danych lub magazynu, ponieważ wartości w jednostce są nadpisywane przez wartości pochodzące z bazy danych. Czasami możesz chcieć postąpić odwrotnie i zastąpić wartości w bazie danych wartościami aktualnie w encji. Jest to czasami nazywane zwycięstwem klienta i może być wykonywane przez pobranie bieżących wartości bazy danych i ustawienie ich jako oryginalnych wartości dla jednostki. (Zobacz Praca z wartościami właściwości , aby uzyskać informacje na temat bieżących i oryginalnych wartości). Na przykład:
using (var context = new BloggingContext())
{
var blog = context.Blogs.Find(1);
blog.Name = "The New ADO.NET Blog";
bool saveFailed;
do
{
saveFailed = false;
try
{
context.SaveChanges();
}
catch (DbUpdateConcurrencyException ex)
{
saveFailed = true;
// Update original values from the database
var entry = ex.Entries.Single();
entry.OriginalValues.SetValues(entry.GetDatabaseValues());
}
} while (saveFailed);
}
Niestandardowe rozwiązanie optymistycznych wyjątków współbieżności
Czasami warto połączyć wartości obecnie w bazie danych z wartościami aktualnie w jednostce. Zwykle wymaga to pewnej logiki niestandardowej lub interakcji użytkownika. Na przykład możesz przedstawić użytkownikowi formularz zawierający bieżące wartości, wartości w bazie danych i domyślny zestaw rozpoznanych wartości. Następnie użytkownik edytuje rozpoznane wartości w razie potrzeby i będzie to te rozpoznane wartości, które zostaną zapisane w bazie danych. Można to zrobić przy użyciu obiektów DbPropertyValues zwracanych z metod CurrentValues i GetDatabaseValues w rekordzie jednostki. Przykład:
using (var context = new BloggingContext())
{
var blog = context.Blogs.Find(1);
blog.Name = "The New ADO.NET Blog";
bool saveFailed;
do
{
saveFailed = false;
try
{
context.SaveChanges();
}
catch (DbUpdateConcurrencyException ex)
{
saveFailed = true;
// Get the current entity values and the values in the database
var entry = ex.Entries.Single();
var currentValues = entry.CurrentValues;
var databaseValues = entry.GetDatabaseValues();
// Choose an initial set of resolved values. In this case we
// make the default be the values currently in the database.
var resolvedValues = databaseValues.Clone();
// Have the user choose what the resolved values should be
HaveUserResolveConcurrency(currentValues, databaseValues, resolvedValues);
// Update the original values with the database values and
// the current values with whatever the user choose.
entry.OriginalValues.SetValues(databaseValues);
entry.CurrentValues.SetValues(resolvedValues);
}
} while (saveFailed);
}
public void HaveUserResolveConcurrency(DbPropertyValues currentValues,
DbPropertyValues databaseValues,
DbPropertyValues resolvedValues)
{
// Show the current, database, and resolved values to the user and have
// them edit the resolved values to get the correct resolution.
}
Niestandardowe rozwiązanie optymistycznych wyjątków współbieżności przy użyciu obiektów
Powyższy kod używa wystąpień DbPropertyValues do przekazywania bieżących, baz danych i rozpoznanych wartości. Czasami może być łatwiej używać wystąpień Twojego typu jednostki do tego. Można to zrobić przy użyciu metod ToObject i SetValues DbPropertyValues. Przykład:
using (var context = new BloggingContext())
{
var blog = context.Blogs.Find(1);
blog.Name = "The New ADO.NET Blog";
bool saveFailed;
do
{
saveFailed = false;
try
{
context.SaveChanges();
}
catch (DbUpdateConcurrencyException ex)
{
saveFailed = true;
// Get the current entity values and the values in the database
// as instances of the entity type
var entry = ex.Entries.Single();
var databaseValues = entry.GetDatabaseValues();
var databaseValuesAsBlog = (Blog)databaseValues.ToObject();
// Choose an initial set of resolved values. In this case we
// make the default be the values currently in the database.
var resolvedValuesAsBlog = (Blog)databaseValues.ToObject();
// Have the user choose what the resolved values should be
HaveUserResolveConcurrency((Blog)entry.Entity,
databaseValuesAsBlog,
resolvedValuesAsBlog);
// Update the original values with the database values and
// the current values with whatever the user choose.
entry.OriginalValues.SetValues(databaseValues);
entry.CurrentValues.SetValues(resolvedValuesAsBlog);
}
} while (saveFailed);
}
public void HaveUserResolveConcurrency(Blog entity,
Blog databaseValues,
Blog resolvedValues)
{
// Show the current, database, and resolved values to the user and have
// them update the resolved values to get the correct resolution.
}