问题描述
我试图找到处理此异常的完美方法,并强制客户端更改以覆盖导致冲突的任何其他更改。我想出的方法是在循环中包装对 Session.Transaction.Commit()
的调用,我会做一个try-catch块和句柄每个陈旧对象单独通过复制其属性,除了行版本属性,然后刷新对象以获取最新的数据库数据,然后重新复制原始值到刷新的对象,然后进行合并。一旦我循环,我将提交,如果任何其他 StaleObjectStateException
发生,那么同样适用。循环继续循环,直到所有冲突解决。
I am trying to find the perfect way to handle this exception and force client changes to overwrite any other changes that caused the conflict. The approach that I came up with is to wrap the call to Session.Transaction.Commit()
in a loop, inside the loop I would do a try-catch block and handle each stale object individually by copying its properties, except row-version property then refreshing the object to get latest DB data then recopying original values to the refreshed object and then doing a merge. Once I loop I will commit and if any other StaleObjectStateException
take place then the same applies. The loop keeps looping until all conflicts are resolved.
此方法是UnitOfWork类的一部分。为了更清楚,我将发布我的代码:
This method is part of a UnitOfWork class. To make it clearer I'll post my code:
// '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);
}
上述代码工作正常,并使用客户端胜利规则处理并发。但是,我想知道是否有任何内置的功能,NHibernate为我做这个或如果有一个更好的方法来处理这个。
The above code works fine and handle concurrency with the client-wins rule. However, I was wondering if there is any built-in capabilities in NHibernate to do this for me or if there is a better way to handle this.
推荐答案
你所描述的是缺乏并发检查。如果不使用并发策略(乐观锁,版本或悲观),则不会抛出StaleStateObjectException,并且将发出更新。
What you're describing is a lack of concurrency checking. If you don't use a concurrency strategy (optimistic-lock, version or pessimistic), StaleStateObjectException will not be thrown and the update will be issued.
好吧,现在我明白你的用例了。一个重要的一点是,在抛出异常后应该丢弃ISession。您可以使用ISession.Merge合并在一个分离的持久化对象之间的更改,而不是自己做。不幸的是,Merge不会级联到子对象,所以你仍然需要自己走对象图。所以实现看起来像:
Okay, now I understand your use case. One important point is that the ISession should be discarded after an exception is thrown. You can use ISession.Merge to merge changes between a detached a persistent object rather than doing it yourself. Unfortunately, Merge does not cascade to child objects so you still need to walk the object graph yourself. So the implementation would look something like:
catch (StaleObjectStateException ex)
{
if (isPowerUser)
{
var newSession = GetSession();
// Merge will automatically get first
newSession.Merge(staleObject);
newSession.Flush();
}
}
这篇关于NHibernate - 处理StaleObjectStateException总是提交客户端更改 - 需要建议/建议的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!