Ну, на самом деле точное число затронутых строк может быть выведено из вызова ObjectContext.SaveChanges ().Если вы посмотрите документацию ObjectContext.SaveChanges , вы увидите:
public int SaveChanges()
- Возвращаемое значение:число объектов в состоянии «Добавлено», «Изменено» или «Удалено» при вызове SaveChanges.
- «SaveChanges работает в транзакции. SaveChanges откатит эту транзакцию и выдаст исключение, если любой изГрязные объекты ObjectStateEntry не могут быть сохранены. "
(1) и (2) в основном означает, что , если ваш вызов SaveChanges () был успешно завершен, и вы неНе получайте никаких исключений, тогда EF гарантирует, что возвращаемое значение точно отражает количество объектов, которые были изменены.
Поэтому все, что вам нужно сделать, это: <pre>try {
// Try to save changes, which may cause a conflict.
int num = context.SaveChanges();
SendMail();
}
catch (OptimisticConcurrencyException) {
//Refresh the entity, using ClientWins
context.Refresh(RefreshMode.ClientWins, yourEntityObject);
//SaveChanges again;
context.SaveChanges();
}
Когда Refresh withВызывается ClientWins, он выполняет запрос для получения текущих значений этого объекта в базе данных, включая новую временную метку.Поэтому все исходные значения полей были обновлены, чтобы отразить последние значения базы данных, поэтому мы можем безопасно попробовать SaveChanges () еще раз.
Обновлено с вашим вопросом:
Задача: отправлять электронную почту только в том случае, если я могу изменить состояние с созданного на напоминание.Таким образом, не имеет смысла форсировать SaveChanges при обработке исключения OptimisticConcurrencyException.Обработчик ошибок должен завершиться, если изменение состояния вызвало исключение, и в противном случае повторить всю задачу (чтение и сохранение).Как это сделать, если оптимистичный параллелизм включен через столбец RowVersion, а не только по состоянию?
Ну, каждый раз, когда строка изменяется, поле rowversion автоматически обновляетсятак что в вашем случае было бы лучше, если бы вы выключили режим параллелизма на rowversion и включили его для свойства State , чтобы ваш код был таким простым: <pre>try {
context.SaveChanges();
SendMail();
}
catch (OptimisticConcurrencyException) {
// RowVersion Concurrency Mode = Fixed
// State Concurrency Mode = None
// If it reaches here it means that the State has been changed;
// so you do nothing except than throwing the exception
throw;
}
Но, если вы хотите установить Режим параллелизма = Фиксированный только для свойства rowversion (как вы упомянули), то это означает, что вы можете потенциальнополучите OptimisticConcurrencyException для изменения любого поля, включая State , так что работа будет немного больше: <pre>try {
ctx.SaveChanges();
SendMail;
}
catch (OptimisticConcurrencyException) {
// RowVersion Concurrency Mode = Fixed
// State Concurrency Mode = None
// If it reches here it means that ANY/All field(s) has changed
// So we need to see if it was State:
ctx.Refresh(RefreshMode.ClientWins, approval);
ObjectStateEntry ose = ctx.ObjectStateManager.GetObjectStateEntry(approval);
string stateValue = ose.OriginalValues["State"].ToString();
// If the value is still "Created" then something else should have changed,
// And caused the exception, so we can proceed with the Save:
if (stateValue == "Created") {
ctx.SaveChanges();
SendMail;
}
else {
// Nope, it was state, so we throw the exception to the caller:
throw;
}