我用NHibernate构建了一个小型ASP.NET MVC Web应用程序作为ORM。在每个HTTP请求开始时,我创建一个会话并打开一个事务并在请求结束时关闭它。
// Begin request
session = StaticSessionManager.OpenSession();
session.BeginTransaction();
// End of request
currentSession.Transaction.Commit();
currentSession.Flush();
currentSession.Close();
现在我也在使用DotNetOpenAuth API进行Facebook / Google登录。为了让我注册一个新用户,API希望我用一些用户信息更新表(比如UserProfile)。我正在通过Nhibernate的Save()
来做session.Save(new UserProfile { UserName = userName });
在同一个Web请求中,DotNetOpenAuth API希望从该表中读取上面插入的数据(UserProfile)。
现在这导致死锁情况,因为NHibernate的Save在数据库上应用了某种锁定。即使我手动尝试使用SQL Server Management studio从该表读取数据,它似乎要等到Web请求超时。
现在我在Save
之后尝试了显式刷新session.Flush();
但即使这样也没有摆脱锁定。我希望我的DotNetOpenAUth API能够读取最新的数据。
目前,我在保存后将交易记录下来,之后再重新开放。我不确定这是最佳做法/有任何缺点。
答案 0 :(得分:4)
使用默认设置(FlushMode
上的ISession
),在NHibernate事务上调用Commit()
将自动刷新更改。在某些情况下,我们希望手动刷新,但在事务提交之后调用Flush()
有很多原因:
由于提交本身已经刷新,我们知道刷新没有更多更改,因此它没用。然而,NHibernate不知道它没用,所以它仍然会检查所有加载的对象。也就是说,超级氟化物冲洗对性能不利。
如果确实有一些脏状态要发送到数据库,这样的更新将在事务之外执行,这通常不是我们想要的。
通常,当数据库获取某种类型的锁时,该锁将一直保留,直到提交或回滚事务为止。刷新不会终止事务,因此对锁没有释放效果。
根据您的描述,您似乎使用一个数据库连接(通过NHibernate会话)来创建UserProfile,并使用另一个数据库连接(在同一个Web请求中)来读取它。后来的连接阻塞,因为第一个连接上的事务尚未完成。 (显然它必须锁定,因为数据库还不知道交易是否成功。)
尝试找到以下任何一种方法:
创建用户配置文件时,请在返回前提交的单独事务(可能还有会话)上执行此操作。
或者使用户配置文件的“回读”使用相同的数据库连接,以便可以在同一事务中完成。我不熟悉DotNetOpenAuth API,但看看是否有办法覆盖读取操作,因此您可以通过NHibernate完成,或者尝试将session.Connection注入DotNetOpenAuth。