我们有一个WCF Web服务处理一些业务实体。 ORM是nhibernate 4.0.4,.NET 4.0。 我们使用IDispatchMessageInspector在AfterReceiveRequest中打开会话和事务,上下文类是wcf_operation:
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
if (!CurrentSessionContext.HasBind(this.factory))
{
this.log.Log.Debug("Creating NH Session");
var session = this.factory.OpenSession();
session.FlushMode = FlushMode.Never;
session.BeginTransaction();
CurrentSessionContext.Bind(session);
}
return null;
}
提交事务并在BeforeSendReply中关闭会话。 只要一次只有一个呼叫处理特定实体,这就有效。
如果两个并发的webservice尝试更新同一个实体,我会得到一个nhibernate异常
NHibernate.HibernateException:非法尝试将集合与两个打开的会话相关联
据我所知,两个更新在数据库级别上发生冲突,我不明白nhibernate在这里遇到的问题。 根据我的理解,这两个电话的两个会话应该是相互独立的;我在这里错过了什么吗?配置可能吗?
如上所述,我看到了数据库存在的问题;尽管如此,我希望有一个例外声明有关conccurent更新的事情。 由于Web服务上的流量增长,我担心我的会话处理存在一般性问题。
答案 0 :(得分:1)
此问题不是并发问题。问题是您正在以不适合的方式使用Sessions。
当从数据库加载实体对象时,它属于到加载它的会话。会话保存其已加载的所有对象的内部映射。您尝试使此会话与由不同会话加载的对象进行交互,它将识别该对象不在其内部映射中并抛出异常。
请记住,会话遵循UnitOfWork设计模式。它们意味着短暂的生命。您创建一个会话,从数据库中读取,进行更改,然后丢弃会话以及所有内存中的对象。
NHibernate确实提供了一种从会话中分离对象然后将其附加到不同会话的方法,但这种方法在并发环境中会充满危险。相反,我建议您修改您的设计,以便共享对象不是nHibernate实体,而是对实体的某种键引用,这可以允许任何单独的会话在需要时重新加载它。
如果您认为某些对象确实应该在WCF服务的生命周期内保留在内存中,则可以使用nHibernate二级缓存。 This blog entry很好地解释了二级缓存。