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

时间:2012-02-13 22:58:07

标签: c# asp.net database concurrency sqlmembershipprovider

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

我想到了一些解决方案:Thread LockingTransactionScopeOptimistic Locking。我非常喜欢乐观锁定的想法,因为我认为冲突可能很少见,但是.NET没有内置任何东西,对吧?

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

编辑:在回顾了Aristos的回答之后,我认为我真正追求的是Authenticate方法的某种“snapshot”隔离级别。

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

2 个答案:

答案 0 :(得分:1)

我将mutex使用名称作为锁定参数,因此只有同一个用户可能会锁定一段时间。对我来说这对于一台计算机来说更安全,因为使用互斥锁我可以捕获来自不同池或网络呼叫的所有可能线程。

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会话来进行此次调用并影响此参数的页面,那么那么页面都准备好锁定另一个,所以我认为那里没有机会需要这个互斥锁调用。因为会话的锁定足以同步和这部分。我提醒会话正在锁定所有用户从开始到结束的页面。

关于会话锁定: Replacing ASP.Net's session entirely

jQuery Ajax calls to web service seem to be synchronous

答案 1 :(得分:1)

根据我的阅读herehere,似乎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;
    }
}