多个独立数据库事务的并发性问题



您将如何使用以下代码解决并发问题在本例中,我们想知道用户身份验证失败的原因。问题是,这段代码对数据库进行了两次单独的调用,但我们希望整个方法发生在概念事务内部。特别是我们对孤立感兴趣。在确定身份验证失败的原因之前,我们不希望在执行此方法期间并发写入影响我们的读取。

脑海中浮现出几个解决方案:线程锁定、事务范围和乐观锁定。我真的很喜欢乐观锁定的想法,因为我认为冲突可能很少见,但.NET中没有内置任何东西可以做到这一点,对吧?

此外,在这种情况下,这真的值得关注吗?像这样的并发问题什么时候需要考虑,什么时候不需要考虑?在实施解决方案时需要考虑什么?表演锁定的持续时间?发生冲突的可能性有多大?

编辑:查看Aristos的答案后,我想我真正想要的是Authenticate方法的某种"快照"隔离级别。

public MembershipStatus Authenticate(string username, string password)
    {
        MembershipUser user = Membership.GetUser(username);
        if (user == null)
        {
            // user did not exist as of Membership.GetUser
            return MembershipStatus.InvalidUsername;
        }
        if (user.IsLockedOut)
        {
            // user was locked out as of Membership.GetUser
            return MembershipStatus.AccountLockedOut;
        }
        if (Membership.ValidateUser(username, password))
        {
            // user was valid as of Membership.ValidateUser
            return MembershipStatus.Valid;
        }
        // user was not valid as of Membership.ValidateUser BUT we don't really
        // know why because we don't have ISOLATION.  The user's status may have changed
        // between the call to Membership.GetUser and Membership.ValidateUser.
        return MembershipStatus.InvalidPassword;
    }

根据我在这里和这里的阅读,似乎包装整个方法的System.Transactions.TransactionScope应该自动将数据库调用登记在一个公共事务中,从而在整个事务范围内实现事务安全。

你可能想做这样的事情:

public MembershipStatus Authenticate(string username, string password)
{
    using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions { IsolationLevel = IsolationLevel.Snapshot }))
    {
    MembershipUser user = Membership.GetUser(username);
        if (user == null)
        {
            // user did not exist as of Membership.GetUser
            return MembershipStatus.InvalidUsername;
        }
        if (user.IsLockedOut)
        {
            // user was locked out as of Membership.GetUser
            return MembershipStatus.AccountLockedOut;
        }
        if (Membership.ValidateUser(username, password))
        {
            // user was valid as of Membership.ValidateUser
            return MembershipStatus.Valid;
        }
        // user was not valid as of Membership.ValidateUser BUT we don't really
        // know why because we don't have ISOLATION.  The user's status may have changed
        // between the call to Membership.GetUser and Membership.ValidateUser.
        return MembershipStatus.InvalidPassword;
    }
}

我将使用名称作为锁定参数的mutex,因此只有同一用户可以锁定一段时间。对我来说,这对一台计算机来说更安全,因为有了互斥,我可以从不同的池或web调用中捕获所有可能的线程。

public MembershipStatus AuthenticateLock(string username, string password)
{
    if(string.IsNullOrEmpty(username))
      return MembershipStatus.InvalidUsername;
    // TODO: Here you must check and clear for non valid characters on mutex name
    using (var mutex = new Mutex (false, username))
    {
         // possible lock and wait, more than 16 seconds and the user can go...
         mutex.WaitOne (TimeSpan.FromSeconds(16), false);
         // here I call your function anyway ! and what ever done...
         //  at least I get a result
         return Authenticate(username, password)
    }
}

更多评论:Membership.ValidateUserMembership.GetUser都对数据库进行了调用。

但是,如果您对进行此调用并影响此参数的页面使用标准的asp.net会话,则页面将锁定另一个,因此我认为不需要此互斥调用因为会话的锁足以同步和这个部分。我提醒您,会话从开始到结束都会锁定所有用户的页面。

关于会话锁定:替换ASP.Net';s会话完全

jQuery Ajax对web服务的调用似乎是同步的

最新更新