2011-09-14 21 views
1

考虑,做沿东西线的应用程序:原子“插入,如果不存在”与NHibernate

var user = session.QueryOver<User>().Where(x => x.Name==name).SingleOrDefault(); 

if (user == null) 
{ 
    user = new User(name); 
    session.Save(user); 
} 

业务规则规定,一个用户名必须是唯一的,这是由一个UNIQUE INDEX支持数据库。上面的代码工作得很好,直到两个用户尝试同时注册同一个名称,都获得user == null并尝试创建新的User。第一个提交将会结束,第二个将失败,并从数据库中引发异常。

避免这种竞争条件的一种方法涉及将关键代码包装在lock { }块中,但当应用程序的多个实例针对同一数据库工作时,这无关紧要。现在,如果只有我可以使用RDBS的锁定机制(在这种情况下为MS SQL)...

这是我卡住的地方。从我可以在NHibernate文档中读到的内容,我可以通过向我的QueryOver()链添加一些提示来解决它,请求显式锁定。这将不得不是一个表锁,因为我还没有实际的行。这可能吗?

或者,我可以增加事务的隔离级别并自动获得所需的锁定吗?考虑到其他(无问题的)查询是在同一个工作单元内完成的,我怀疑这种改变会引入大量的阻塞/等待。是这样吗?

+0

可能是我刚刚发现这个问题的一个副本:[NHibernate事务和竞争条件](http://stackoverflow.com/questions/119762/nhibernate-transaction-and-race-condition) –

回答

0

确保您在保存时通过相同的NHibernate会话运行查询 - 这样做将确保您使用相同的事务,并自动利用数据库的锁定机制。

也就是说,您需要为事务做好准备,以便您的代码能够优雅地处理这种情况。使用序列化模块和锁定会增加代码的复杂性,但不能消除在数据库级别发生故障的可能性,因此您将这种复杂性增加了一点。

+0

两个查询都在已经有相同的事务,并且在异常日志中看到唯一索引违规。我是否需要增加事务的隔离级别以使自动锁定工作? –

2

将隔离级别增加到可序列化。这将在整个查询中获得读锁和写锁,包括'where'子句。