EntityFramework. Оптимистические блокировки

EntityFramework

EntityFramework позволяет работать с оптимистическими блокировками, возникающими при работе с СУБД, и обрабатывать возникающие исключения. В посте я покажу небольшой пример того, как можно применить этот функционал.

Подготовка

Сделаем маленькую базку и консольное приложение с моделью данных. У меня получилось примерно так:

Диаграмма базы данных

Диаграмма базы данных

Модель данных EntityFramework

Модель данных EntityFramework

Связь многие-ко-многим EntityFramework "проглотила", но если в таблице связей добавить ещё одно поле, то EntityFramework не станет вмешиваться в структуру данных.

Контрольная группа

Для начала попробуем проделать операцию модификации данных без включения оптимистических блокировок и посмотрим "внутрь".

Запустим нашу консоль со следующим кодом:

class Program
{
  static void Main(string[] args)
  {
    // Наш контекст
    using(var ctx = new ZhukPointEntities())
    {
      // Берем заранее подготовленный объект
      var article = ctx.Article.First(a => a.Id == new Guid("27713880-e3e3-4073-8f41-71fb511501e9"));
      // Меняем свойство
      article.Note = DateTime.Today.ToShortDateString();
      // Сохраняем изменения
      ctx.SaveChanges();
    }
  }
}

В профайлере смотрим и видим:

exec sp_executesql N'update [dbo].[Article]
set [Note] = @0
where ([Id] = @1)
select [TimeStamp]
from [dbo].[Article]
where @@ROWCOUNT > 0 and [Id] = @1',N'@0 nvarchar(1000),@1 uniqueidentifier',@0=N'20.11.2010',@1='27713880-E3E3-4073-8F41-71FB511501E9'

Понеслась

Теперь включаем фичу. Для этого в свойствах поля TimeStamp объекта Article переключаем Concurrency Mode в Fixed.

Включение оптимистичной блокировки

Включение оптимистичной блокировки

Повторяем процедуру, смотрим профайлер:

exec sp_executesql N'update [dbo].[Article]
set [Note] = @0
where ([Id] = @1)
select [TimeStamp]
from [dbo].[Article]
where @@ROWCOUNT > 0 and [Id] = @1',N'@0 nvarchar(1000),@1 uniqueidentifier',@0=N'20.11.2010',@1='27713880-E3E3-4073-8F41-71FB511501E9'

За что боролись, на то и напоролись. Теперь сделаем вид, что данные были изменены после считывания из таблицы и теперь мы их пытаемся сохранить. Код будет примерно такой:

class Program
{
  static void Main(string[] args)
  {
    // Наш контекст
    using(var ctx = new ZhukPointEntities())
    {
      // Берем заранее подготовленный объект
      var article = ctx.Article.First(a => a.Id == new Guid("27713880-e3e3-4073-8f41-71fb511501e9"));
      // Меняем свойство
      article.Note = DateTime.Now.ToShortDateString();
      using(var _ctx = new ZhukPointEntities())
      {
        var _article = _ctx.Article.First(a => a.Id == new Guid("27713880-e3e3-4073-8f41-71fb511501e9"));
        _article.Note = DateTime.Now.ToShortDateString();
        _ctx.SaveChanges();
      }
      // Сохраняем изменения
      ctx.SaveChanges();
    }
  }
}

Запускаем, смотрим и видим:

Исключение, полученное после включения оптимистической блокировки

Исключение, полученное после включения оптимистической блокировки

Обрабатываем исключение

Сначала решим задачу "сохранить любой ценой". Изменим код:

class Program
{
  static void Main(string[] args)
  {
    // Наш контекст
    using (var ctx = new ZhukPointEntities())
    {
      // Берем заранее подготовленный объект
      var article = ctx.Article.First(a => a.Id == new Guid("27713880-e3e3-4073-8f41-71fb511501e9"));
      // Меняем свойство
      article.Note = DateTime.Now.ToShortDateString();
      using (var _ctx = new ZhukPointEntities())
      {
        var _article = _ctx.Article.First(a => a.Id == new Guid("27713880-e3e3-4073-8f41-71fb511501e9"));
        _article.Note = DateTime.Now.ToShortDateString();
        _ctx.SaveChanges();
      }
      try
      {
        // Сохраняем изменения
        ctx.SaveChanges();
      }
      catch (OptimisticConcurrencyException ex)
      {
        // Обновляем объект с приоритетом клиентской стороне
        ctx.Refresh(RefreshMode.ClientWins, article);
        // Сохраняем изменения
        ctx.SaveChanges();
      }
    }
  }
}

На стороне SQL Server'а происходит загрузка объекта и очередная попытка сохранения. Все работает. Теперь попробуем вывести информацию о свойствах элемента, из-за которых и произошло исключение. Теперь код будет таким:

static void Main(string[] args)
{
  // Наш контекст
  using (var ctx = new ZhukPointEntities())
  {
    // Берем заранее подготовленный объект
    var article = ctx.Article.First(a => a.Id == new Guid("27713880-e3e3-4073-8f41-71fb511501e9"));
    // Меняем свойство
    article.Note = DateTime.Now.ToString();
    using (var _ctx = new ZhukPointEntities())
    {
      var _article = _ctx.Article.First(a => a.Id == new Guid("27713880-e3e3-4073-8f41-71fb511501e9"));
      _article.Note = DateTime.Now.ToString();
      _ctx.SaveChanges();
    }
    try
    {
      // Сохраняем изменения
      ctx.SaveChanges();
    }
    catch (OptimisticConcurrencyException ex)
    {
      // Перебираем объекты, сохранить которые не удалось
      foreach (var stateEntry in ex.StateEntries)
      {
        // Перебираем измененный свойства у объекта
        var properties = stateEntry.GetModifiedProperties();
        foreach (var property in properties)
        {
          // Текущее значение на клиенте
          var _new = stateEntry.CurrentValues[property];
          // Значение на сервере
          var _old = stateEntry.OriginalValues[property];
          // Выводим информацию об измененных данных
          Console.WriteLine(property + "\t" + _old + "\t" + _new);
          // Дальше ваша логика, для делегирования принятия решения клиенту (или логике)
        }
      }
      // Здесь Exception, т.к. обработки здесь нет
    }
  }
}

И получаем:

Информация о конфликте при сохранении данных

Информация о конфликте при сохранении данных

Осталось только начать применять.

Теги

Поделиться

Виталий Жуков

Виталий Жуков

Техлид, Архитектор, Разработчик, Microsoft MVP. Более 20 лет опыта в области системной интеграции и разработки программного обеспечения. Специализируюсь на проектировании и внедрении масштабируемых высокопроизводительных программных решений в различных отраслях.