Вы уверены, что последовательность вызовов выполняется так, как вы описали?В любом случае, основная проблема заключается в том, что вы не должны использовать метку времени, возвращаемую запросом в методе Upate.Вы должны использовать временную метку, полученную, когда пользователь получает начальные данные для обновления.Должно быть:
- Пользователь запрашивает данные из обновления - все поля и метка времени получаются из БД
- Метка времени сохраняется на клиенте (скрытое поле в веб-приложении)
- Пользователь изменяет данные и нажимает кнопку Сохранить - все данные, включая старую временную метку, отправляются в процесс
- Метод обновления загружает текущую сущность
- Все обновленные поля объединяются со старой сущностью.Временная метка объекта установлена на временную метку, полученную на первом шаге.
- SaveChanges называется
Причина этого в том, что он может передавать много времени между первым вызовом и методом обновлениявызов, так что может быть несколько обработанных изменений.Вы должны использовать начальную временную метку, чтобы избежать перезаписи других изменений без вывода сообщений.
Редактировать:
// You are updating category - update probably means that you had to load category
// first from database to show actual values. When you loaded the category it had some
// timestamp value. If you don't use that value, any change between that load and c
// calling this method will be silently overwritten.
public void Update(Category category)
{
try
{
Category catToUpdate = (from c in Contentctx.Categories where c.CategoryID == categoryId select c).FirstOrDefault();
...
// Now categoryToUpdate contains actual timestamp. But it is timestamp of
// actual values loaded now. So if you use this timestamp to deal with
// concurrency, it will only fire exception if somebody modifies data
// before you call SaveChanges few lines of code bellow.
if (catToUpdate != null)
{
//Set fields here....
// To ensure concurrency check from your initial load, you must
// use the initial timestamp.
catToUpdate.Timestamp = category.Timestamp;
Contentctx.SaveChanges();
}
...
}
catch (OptimisticConcurrencyException ex)
{
...
}
}