0

用户表结构 Id 用户名(唯一约束)

我有这样的 Nhibernate 和 SqlServer 问题。有两个并发事务试图在用户表中插入数据。两个事务都查询表中的数据以检查要插入的新用户名是否未出现在表中。问题是,让我们说。Transaction1和Transaction2读取User Table,发现User Table中没有用户名embarus。然后 Transaction2 尝试在 User 表中插入 embarus,而 Transaction1 已插入并已在表中提交 embarus。

因此 Transaction2 获得唯一约束的异常。

请帮助我解决这个问题,任何可能有用的想法或文章。

我发现 SqlServer 2008 使用 ReadCommitted 作为默认事务隔离级别。

太感谢了。

4

1 回答 1

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;
                }
            }

请注意,您不应在异常发生后重用会话。

于 2013-04-16T12:33:08.493 回答