Link blog.vitalyzhukov.ru

Блог переехал вот сюда: Blog.VitalyZhukov.ru

20 нояб. 2010 г.

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

Введение
В посте посмотрим как EntityFramework обеспечивает управление параллелизмом данных в контексте объекта.
Подготовка
Сделаем маленькую базку и консольное приложение с моделью данных.

 У меня получилось примерно так:
Диаграмма базы данных

Модель данных EF
Связь многие-ко-многим EF "проглотила", но если в таблице связей добавить ещё одно поле, то EF не станет вмешиваться в структуру данных.
Контрольная группа
Для начала попробуем проделать операцию модификации данных без включения оптимистических блокировок и посмотрим "внутрь".
Запустим нашу консоль со следующим кодом:

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) and ([TimeStamp] = @2))
select [TimeStamp]
from [dbo].[Article]
where @@ROWCOUNT > 0 and [Id] = @1',N'@0 nvarchar(1000),@1 uniqueidentifier,@2 binary(8)',@0=N'20.11.2010',@1='27713880-E3E3-4073-8F41-71FB511501E9',@2=0x00000000000007D5



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

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, т.к. обработки здесь нет
        }
    }
}
И получаем:

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


Комментариев нет:

Отправить комментарий