我想处理用户从Windows窗体应用程序中的数据库编辑对象,进行违反数据库约束(即列唯一值)的编辑,将实体保存回数据库的情况,和NHibernate抛出异常从而破坏会话。
我正在使用MSDN文章Building a Desktop To-Do Application with NHibernate中发布的指南,并使用每个演示者会话/表单方法。在创建演示者时,我保留对SessionFactory的引用,创建一个新会话,通过会话从数据库中检索对象并存储对它的引用[对象]。显示表单时,将从对象填充其字段。
当对表单进行更改并且用户希望保存数据时,将从字段值更新对象,并将对象保存回数据库。
我处理过时状态异常,其中对象在从数据库检索到保存时间之间被更改:创建新会话,再次加载原始对象,用户被告知冲突已发生并显示他的更改以及当前数据库中的内容。他可以选择保存更改或取消(并接受现在存储在数据库中的内容)。
如果违反约束,似乎可能发生以下两种情况之一:
但是,我认为我实际上无法检测到哪种情况发生,因为从未抛出过时状态异常,因为发生了约束异常(我已经测试了这个)。
处理案例1是微不足道的,因为我可以简单地显示一条错误消息,其中显示“FIELD-X具有已存在于DB中的值”并假装没有发生任何真正的错误。用户可以将FIELD-X更改为唯一值,并再次保存,而无需重新输入更改。
处理案例2就像处理常规的陈旧状态异常一样。
我知道我可以通过保留原始(或其值)的副本然后比较两个逐个字段来“暴力破解”。 但是,我怀疑有更好的方法可以通过利用NHibernate来解决这个问题。你会如何处理这个?
如果有帮助:
编辑2月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
{
// ????
}
}
答案 0 :(得分:1)
ISession.Refresh(Object obj)
方法最终为我工作了。除了最终方法之外,我的问题中的代码保持不变:
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
{
this.session.Refresh(this.userAccount);
}
this.form.ShowDialog("... Describe the constraint violation ...");
}
答案 1 :(得分:0)
你可以
// after ConstraintException with new session
session.Lock(loadedObject, LockMode.None); // attach object with session
// copy back from UI
session.Flush();
catch()
{
if (ConstraintException)
// repeat
else if (Stale)
// handle like you have
else
// all ok
}
如果您对db
中的内容不感兴趣// loads from the database, copy state from object into it and returns the loaded object (attached to session), changes will be updated on next flush
obj = session.Merge(obj);