В EF Core можно задействовать тип данных timestamp из SQL Server, реализуя внутри сущности свойство TimeStamp (представляемое в C# как byte[]). Свойства сущностей, определенные с применением атрибута TimeStamp либо Fluent API, предназначены для добавления в конструкцию where при обновлении или удалении записей. Вместо того чтобы просто использовать значение (значения) первичного ключа, в конструкцию where генерируемого оператора SQL добавляется значение свойства timestamp, что ограничивает результаты записями, у которых совпадают значения первичного ключа и отметки времени. Если запись была обновлена другим пользователем (или системой), тогда значения отметок времени не совпадут, так что оператор update не обновит, а оператор delete не удалит запись. Вот пример запроса обновления, в котором применяется столбец TimeStamp:

UPDATE [Dbo].[Inventory] SET [Color] = N'Yellow'

WHERE [Id] = 1 AND [TimeStamp] = 0x000000000000081F;

Когда хранилище сообщает о количестве затронутых записей, отличающемся от количества записей, изменения которых ожидает ChangeTracker, исполняющая среда EF Core генерирует исключение DbUpdateConcurrencyException и выполняет откат всей транзакции. Экземпляр DbUpdateConcurrencyException содержит информацию о записях, которые не были сохранены, куда входят первоначальные значения (полученные в результате загрузки из базы данных) и текущие значения (после их обновления пользователем/системой). Кроме того, существует метод для получения текущих значений в базе данных (требующий еще одного обращения к серверу). Располагая настолько большим количеством информации, разработчик затем может обработать ошибку параллелизма так, как того требует приложение. Ниже приведен пример:

try

{

  // Получить запись для автомобиля (неважно какую).

  var car = Context.Cars.First();

  // Обновить базу данных извне контекста.

   Context.Database.ExecuteSqlInterpolated($"Update dbo.Inventory set Color='Pink' where Id = {car.Id}");

  // Обновить запись для автомобиля в ChangeTracker

  // и попробовать сохранить изменения.

  car.Color = "Yellow";

  Context.SaveChanges();

}

catch (DbUpdateConcurrencyException ex)

{

  // Получить сущность, которую не удалось обновить.

  var entry = ex.Entries[0];

  /// Получить первоначальные значения (когда сущность была загружена).

  PropertyValues originalProps = entry.OriginalValues;

  // Получить текущие значения (обновленные кодом выше).

  PropertyValues currentProps = entry.CurrentValues;

  // Получить текущие значения из хранилища данных.

  // Примечание: это требует еще одного обращения к базе данных

  //PropertyValues databaseProps = entry.GetDatabaseValues();

}

<p id="AutBody_Root926"><strong>Устойчивость подключений</strong></p>

Кратковременные ошибки трудны в отладке и еще более трудны в воспроизведении. К счастью, многие поставщики баз данных имеют внутренний механизм повтора для сбоев в системе баз данных (проблемы с tempdb, ограничения пользователей и т.д.), который может быть задействован EF Core. Для SQL Server кратковременные ошибки (согласно определению команды разработчиков СУБД) перехватываются экземпляром класса SqlServerRetryingExecutionStrategy, и если он включен в объекте производного от DbContext класса через DbContextOptions, то EF Core автоматически повторяет операцию до тех пор, пока не достигнет максимального предела повторов.

При работе с SQL Server доступен сокращенный метод, который можно использовать для включения SqlServerRetryingExecutionStrategy со всеми стандартными параметрами. Метод, который применяется с SqlServerOptions — это EnableRetryOnFailure():

public ApplicationDbContext CreateDbContext(string[] args)

{

  var optionsBuilder = new DbContextOptionsBuilder();

Перейти на страницу:

Похожие книги