Я хотел бы обработать случай, когда пользователь редактирует объект из базы данных в приложении Windows Forms, выполняет редактирование, которое нарушает ограничение базы данных (то есть уникальное значение столбца), сохраняет объект обратно в базу данных,и NHibernate выдает исключение, таким образом разрушая сеанс.
Я использую руководство, опубликованное в статье MSDN Создание настольного приложения для работы с NHibernate и использование подхода «сессия на докладчика / форму».При создании докладчика я сохраняю ссылку на SessionFactory, создаю новый сеанс, извлекаю объект из базы данных через сеанс и сохраняю ссылку на него [объект].Когда форма отображается, ее поля заполняются из объекта.
Когда в форму вносятся изменения, и пользователь желает сохранить данные, объект обновляется из значений полей, и объект сохраняетсявернуться к базе данных.
Я обрабатываю исключения в устаревшем состоянии, когда объект изменяется между моментом, когда он был извлечен из базы данных, и когда он сохраняется обратно: создается новый сеанс, снова загружается исходный объект, пользователь информируетсячто конфликт произошел и представлен с его изменениями и что в настоящее время в базе данных.Он может сохранить свои изменения или отменить (и принять то, что сейчас хранится в базе данных).
В случае нарушения ограничения может возникнуть одна из двух вещей:
- Объект не изменился в базе данных, где его можно загрузить в новый сеанс.
- Объект также был изменен в базе данных и больше не отражает то, что было изначально загружено.
Тем не менее, я не думаю, что на самом деле могу определить, какой случай произошел, так как исключение устаревшего состояния никогда не генерируется, потому что произошло исключение ограничения (я проверял это).
Обработка случая 1 тривиальна, поскольку я могу просто отобразить сообщение об ошибке, в котором говорится, что «FIELD-X имеет значение, которое уже находится в БД», и сделать вид, что ничего плохого не произошло.Пользователь может изменить FIELD-X на уникальное значение и снова сохранить без необходимости повторного ввода своих изменений.
Обработка случая 2 будет аналогична обработке обычного исключения из устаревшего состояния.
Я знаю, что яможет "перебор", сохраняя копию оригинала (или его значения), а затем сравнивая два поля за полем. Однако я подозреваю, что есть лучший способ справиться с этим, используя NHibernate. Как бы вы справились с этим?
В случае, если это полезно:
- Версия NHibernate: 3.2
- Оптимистическая блокировка с использованием "грязной" стратегии
- .NET Framework 2.0 SP2
- База данных / драйвер SQLite
РЕДАКТИРОВАТЬ 23 февраля - я добавил пример кода, чтобы лучше проиллюстрировать, что я сейчас делаю.
/**
* Save handler called when user initiates save in UI.
*
* Returns true when save was successful (essentially, tells the presenter
* that the UI can be closed.
*/
private bool SaveData()
{
try
{
if (this.creatingNewUserAccount)
{
// Do whatever is necessary to instantiate a new object.
this.userAccount = new UserAccount();
// and copy values from the UI into the new object.
this.userAccount.Name = this.form.Name;
// etc.
}
else
{
// Just copy values from the UI into the existing object
// from the database.
this.userAccount.Name = this.form.Name;
// etc.
}
using (ITransaction tx = this.session.BeginTransaction())
{
this.accountRepository.store(this.userAccount);
tx.Commit();
}
return true;
}
catch (StaleObjectStateException)
{
HandleStaleStateException();
return false;
}
catch (ArgumentException e)
{
this.m_View.ShowOtherDialog(e.Message);
return false;
}
catch (GenericADOException e)
{
HandleConstraintViolationException();
return false;
}
}
private void HandleStaleStateException()
{
// The session was trashed when the exception was thrown,
// so close it and create a new one.
this.session.Dispose();
this.session = this.sessionFactory.OpenSession();
CurrentSessionContext.Bind(this.session);
// Reload the object from the database.
this.userAccount = LoadData();
// Do a bunch of things that deal with informing the user
// of the stale-state and displaying a form to merge changes.
HandleEditConflict();
}
private void HandleConstraintViolationException()
{
// The session was trashed when the exception was thrown,
// so close it and create a new one.
this.session.Dispose();
this.session = this.sessionFactory.OpenSession();
CurrentSessionContext.Bind(this.session);
// Determine if trying to save a new entity or editing an existing one.
if (this.creatingNewUserAccount)
{
// If saving a new entity, we don't care about the old object
// we created and tried to save.
this.userAccount = null;
}
else
{
// ????
}
}