我正在尝试找到处理此异常的完美方法,并强制客户端更改覆盖导致冲突的任何其他更改。我想出的方法是将循环中的Session.Transaction.Commit()
包装在循环中,在循环内我会做一个try-catch块并通过复制其属性来单独处理每个陈旧对象,除了row-version属性然后刷新对象以获取最新的数据库数据,然后将原始值重新复制到刷新的对象,然后进行合并。一旦我循环,我将提交,如果发生任何其他StaleObjectStateException
,则同样适用。循环保持循环,直到所有冲突都得到解决。
此方法是UnitOfWork类的一部分。为了更清楚,我将发布我的代码:
// 'Client-wins' rules, any conflicts found will always cause client changes to
// overwrite anything else.
public void CommitAndRefresh() {
bool saveFailed;
do {
try {
_session.Transaction.Commit();
_session.BeginTransaction();
saveFailed = false;
} catch (StaleObjectStateException ex) {
saveFailed = true;
// Get the staled object with client changes
var staleObject = _session.Get(ex.EntityName, ex.Identifier);
// Extract the row-version property name
IClassMetadata meta = _sessionFactory.GetClassMetadata(ex.EntityName);
string rowVersionPropertyName = meta.PropertyNames[meta.VersionProperty] as string;
// Store all property values from client changes
var propertyValues = new Dictionary<string, object>();
var publicProperties = staleObject.GetType().GetProperties();
foreach (var p in publicProperties) {
if (p.Name != rowVersionPropertyName) {
propertyValues.Add(p.Name, p.GetValue(staleObject, null));
}
}
// Get latest data for staled object from the database
_session.Refresh(staleObject);
// Update the data with the original client changes except for row-version
foreach (var p in publicProperties) {
if (p.Name != rowVersionPropertyName) {
p.SetValue(staleObject, propertyValues[p.Name], null);
}
}
// Merge
_session.Merge(staleObject);
}
} while (saveFailed);
}
上面的代码工作正常,并使用client-wins规则处理并发。但是,我想知道NHibernate中是否有任何内置功能可以为我执行此操作,或者是否有更好的方法来处理此问题。
提前致谢,
答案 0 :(得分:1)
您所描述的是缺乏并发检查。如果您不使用并发策略(乐观锁,版本或悲观),则不会抛出StaleStateObjectException并且将发布更新。
好的,现在我了解你的用例。一个重要的一点是,在抛出异常后应该丢弃ISession。您可以使用ISession.Merge合并分离的持久对象之间的更改,而不是自己进行。不幸的是,Merge不会级联到子对象,因此您仍然需要自己遍历对象图。所以实现看起来像:
catch (StaleObjectStateException ex)
{
if (isPowerUser)
{
var newSession = GetSession();
// Merge will automatically get first
newSession.Merge(staleObject);
newSession.Flush();
}
}