用户表结构 ID 用户名(唯一约束)
我有像这样的Nhibernate和SqlServer的问题。 有两个并发事务试图在用户表中插入数据。 两个事务都查询表中的数据,以检查要插入的新用户名是否未出现在表中。 问题是,让我们说。 Transaction1和Transaction2读取用户表,发现用户表中没有用户名embarus。 然后,Transaction2尝试在插入Transaction1时在User表中插入embarus并在表中提交embarus。
因此,Transaction2会获得唯一约束的异常。
请帮我解决这个问题,任何可能有用的想法或文章。
我发现SqlServer 2008使用ReadCommitted作为默认事务隔离级别。
非常感谢你。
答案 0 :(得分:1)
您需要捕获并处理唯一约束违规。最好的方法是创建一个ISqlExceptionConverter实现,将RDBMS特定的异常转换为应用程序中的自定义异常。
public class SqlServerExceptionConverter : ISQLExceptionConverter
{
public Exception Convert(AdoExceptionContextInfo adoExceptionContextInfo)
{
var sqlException = adoExceptionContextInfo.SqlException as SqlException;
if (sqlException != null)
{
// 2601 is unique key, 2627 is unique index; same thing:
// http://blog.sqlauthority.com/2007/04/26/sql-server-difference-between-unique-index-vs-unique-constraint/
if (sqlException.Number == 2601 || sqlException.Number == 2627)
{
return new UniqueKeyException(sqlException.Message, sqlException);
}
}
return adoExceptionContextInfo.SqlException;
}
}
public class UniqueKeyException : Exception
{
public UniqueKeyException(string message, Exception innerException)
: base(message, innerException)
{ }
}
用法:
using (var txn = _session.BeginTransaction())
{
try
{
var user= new User
{
Name = "embarus"
};
_session.Save(user);
txn.Commit();
}
catch (UniqueKeyException)
{
txn.Rollback();
var msg = string.Format("A user named '{0}' already exists, please enter a different name or cancel.", "embarus");
// Do something useful
}
catch (Exception ex)
{
if (txn.IsActive)
{
txn.Rollback();
}
throw;
}
}
请注意,发生异常后不应重复使用会话。