事务范围插入重复值。如何锁定选择查询

时间:2015-03-03 06:40:55

标签: sql asp.net-mvc entity-framework sql-server-2008 transactionscope

我在我的项目中使用事务范围。隔离级别为IsolationLevel.ReadCommitted
我也尝试了IsolationLevel.RepeatableRead

using (DBOperations dboperation = new DBOperations())
{
  GetAccountVoucherNuber();
}

我从交易范围调用此方法。

private void GetAccountVoucherNuber() {

    var _dB0010013 = _dB0010013Repository.GetAll().Where(e = > e.Category == "D" && e.CategoryType == "TSNO" && e.Branch == Branch && EntityFunctions.TruncateTime(e.OnDate) == EntityFunctions.TruncateTime(TranInit.EntryDate) && e.CodeOne == BatchCode).FirstOrDefault();
    if (_dB0010013 == null) {
        _dB0010013 = new DB0010013();
        _dB0010013.Branch = Branch;
        _dB0010013.Category = "D";
        _dB0010013.CategoryType = "TSNO";
        _dB0010013.CodeOne = BatchCode;
        _dB0010013.CodeTwo = "";
        _dB0010013.CodeThree = "";
        _dB0010013.OnDate = TranInit.EntryDate;
        _dB0010013.Note = "";
        _dB0010013.LastNo = 1;

        var _operationStatus = _dB0010013Repository.AddAndSave(_dB0010013, false);
    } else {
        _dB0010013.LastNo += 1;
        var _operationStatus = _dB0010013Repository.UpdateAndSave(_dB0010013, false);
    }

}


当两个或多个用户同时提交页面时。
对于两次交易,我得到相同的号码。
例如。假设user1得到lastNo = 85 + = 1 = 86
同时user2也得到lastNo = 85 + = 1 = 86
因此两个不同的交易同样不适用。
获取值后如何锁定Select语句。或者是什么方法来处理它。
我搜索了很多,但没有找到任何解决方案。

我的DbOperataion课程已添加以供参考。

public class DBOperations:IDisposable
    {
        private TransactionScope transactionscope;

        public void Dispose()
        {
            if (this.transactionscope != null)
                this.transactionscope.Dispose();
        }
        public DBOperations()
        {
            this.Initialize();
        }
        public void Initialize()
        {
            try
            {
                this.Dispose();

                TransactionOptions transactionoption = new TransactionOptions();
                transactionoption.Timeout = new TimeSpan(1, 0, 0);
               transactionoption.IsolationLevel = System.Transactions.IsolationLevel.ReadCommitted;

                this.transactionscope = new TransactionScope(TransactionScopeOption.Required, transactionoption);

            }
            catch (Exception ex)
            {
                throw new Exception("Data Error " + ex.Message);
            }
        }
}

2 个答案:

答案 0 :(得分:0)

在这种情况下,我通常会制作一个额外的表AccountVoucherNumberGenerator来保存最后一个数字。您可以执行插入并使用Identity Insert(最简单的方法),也可以选择仅使用1行并使用上次使用的数字更新该行。

答案 1 :(得分:0)

我更喜欢按TransactionTransactionOptions类设置TransactionScope。从Isolation Level on Wiki开始,RepeatableRead级别就足够了。

using System.Transactions;
....

TransactionOptions options = new TransactionOptions();
options.IsolationLevel = IsolationLevel.RepeatableRead;
// Set timeout to avoid dead lock conditions.
options.Timeout = new TimeSpan(0, 5, 0); 

using (TransactionScope trans = new TransactionScope(TransactionScopeOption.Required, options)) {

    // Execute this operation in the transaction.
    GetAccountVoucherNuber();

    // Call `Complete()` explicitly about transaction commit. Or, it will do rollback..... 
    trans.Complete();
}

我想你应该在Complete()上致电TransactionScope

更新

Read uncommitteddo read-lock and write-lock in the transaction。换句话说,您的问题是要避免Non-repeatable reads,而Read uncommittedSerializable隔离级别可以避免此问题。如果您需要避免Phantoms问题,则需要使用Serializable隔离级别。不同的隔离级别将具有不同的锁定行为。关于数据库锁,我更喜欢this book, database management systems.

在事务提交或回滚后,它将释放它拥有的所有资源,包括read-lockswrite-locks