使用NHibernate进行原子“插入,如果不存在”

时间:2011-09-14 21:46:30

标签: sql-server nhibernate transactions rdbms

考虑一个按照以下方式执行某项操作的应用程序:

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()链添加一些请求显式锁定的提示来解决它。这必须是一个表锁,因为我还没有实际的行。这可能吗?如何

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

2 个答案:

答案 0 :(得分:2)

将隔离级别提高到Serializable。这将在整个查询中获得读取和写入锁定,包括“where”子句。

答案 1 :(得分:0)

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

也就是说,总是需要为事务准备失败做好准备,并且您的代码需要优雅地处理该情况。使用序列化的块和锁获得花哨会增加代码的复杂性,但无法消除在数据库级别出现故障的可能性,因此您将这种复杂性添加到一点点。