Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Оптимистическая конкуренция подразумевает оптимистическую попытку сохранить сущность в базе данных в надежде, что данные не изменились с момента загрузки сущности. Если оказывается, что данные изменились, создается исключение, и необходимо устранить конфликт перед попыткой сохранить еще раз. В этом разделе описывается обработка таких исключений в Entity Framework. Методы, показанные в этом разделе, применяются одинаково к моделям, созданным с помощью code First и ef Designer.
Это сообщение не является подходящей платформой для полноценного обсуждения оптимистического параллелизма. В следующих разделах предполагается наличие знаний о разрешении параллелизма и приводятся шаблоны для распространенных задач.
Многие из этих шаблонов используют темы, рассмотренные в разделе "Работа со значениями свойств".
Разрешение проблем с параллелизмом при использовании независимых ассоциаций (где внешний ключ не сопоставлен свойству в вашей сущности) значительно сложнее, чем при использовании ассоциаций на основе внешнего ключа. Таким образом, если вы собираетесь выполнять разрешение конкурентности в вашем приложении, рекомендуется всегда сопоставлять внешние ключи с вашими сущностями. Все приведенные ниже примеры предполагают, что вы используете связи внешнего ключа.
DbUpdateConcurrencyException вызывается методом SaveChanges, когда обнаруживается исключение оптимистической конкуренции при попытке сохранить сущность, использующую связи внешнего ключа.
Разрешение исключений оптимистичной конкуренции с помощью Reload (победа базы данных)
Метод Reload можно использовать для перезаписи текущих значений сущности текущими значениями в базе данных. Затем сущность обычно возвращается пользователю в некоторой форме, и они должны попытаться снова внести изменения и повторно сохранить. Рассмотрим пример.
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);
}
Хорошим способом имитации исключения параллелизма является установка точки останова в вызове SaveChanges, а затем изменение сущности, сохраненной в базе данных, с помощью другого средства, например SQL Server Management Studio. Можно также добавить строку перед вызовом SaveChanges, чтобы обновить базу данных непосредственно с помощью SqlCommand. Рассмотрим пример.
context.Database.SqlCommand(
"UPDATE dbo.Blogs SET Name = 'Another Name' WHERE BlogId = 1");
Метод Entries в DbUpdateConcurrencyException возвращает экземпляры DbEntityEntry для сущностей, которые не удалось обновить. (В настоящее время это свойство всегда возвращает одно значение для проблем параллелизма. Он может возвращать несколько значений для общих исключений обновлений.) Альтернативой для некоторых ситуаций может быть получение записей для всех сущностей, которые могут потребоваться перезагрузить из базы данных и перезагрузить для каждой из этих сущностей.
Разрешение исключений оптимистического параллелизма при победе клиента
Приведенный выше пример, в котором используется перезагрузка, иногда называется "победа базы данных" или "победа хранилища", так как значения в сущности перезаписываются значениями из базы данных. Иногда может потребоваться выполнить противоположное действие и перезаписать значения в базе данных значениями, которые в данный момент находятся в сущности. Иногда это называется победами клиента, и их можно сделать, получив текущие значения базы данных и установив их в качестве исходных значений для сущности. (Сведения о текущих и исходных значениях см. в разделе "Работа со значениями свойств ".) Например:
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);
}
Настраиваемое разрешение исключений оптимистического параллелизма
Иногда может потребоваться объединить значения в базе данных с значениями, которые в данный момент находятся в сущности. Обычно это требует пользовательской логики или взаимодействия с пользователем. Например, вы можете представить пользователю форму, содержащую текущие значения, значения в базе данных и набор разрешенных значений по умолчанию. Затем пользователь отредактирует разрешенные значения по мере необходимости, и это будут эти разрешенные значения, которые сохраняются в базе данных. Это можно сделать, используя объекты DbPropertyValues, которые возвращаются методами CurrentValues и GetDatabaseValues в записи сущности. Рассмотрим пример.
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.
}
Настраиваемое разрешение исключений оптимистического параллелизма с помощью объектов
Приведенный выше код использует экземпляры DbPropertyValues для передачи текущих, баз данных и разрешенных значений. Иногда для этого может быть проще использовать экземпляры вашего типа сущностей. Это можно сделать с помощью методов ToObject и SetValues dbPropertyValues. Рассмотрим пример.
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.
}