2

我有一个处理关键事务的操作,但我不确定处理该事务的最佳方式是什么。

这是我需要做的一个简化示例:

[HttpPost]
public ActionResult BeginOrderProcess(Guid orderKey)
{
    // Not sure what isolation level I sould use here to start with...
    IsolationLevel isolationLevel = IsolationLevel.ReadCommitted;
    using(new TransactionScope(isolationLevel)){

        // Retreive the order
        var order = GetExistingOrder(orderKey);

        // Validate that the order can be processed
        var validationResult = ValidateOrder(order);

        if (!validationResult.Successful)
        {
            // Order cannot be processed, returning
            return View("ErrorOpeningOrder");
        }

        // Important stuff going on here, but I must be sure it 
        // will never be called twice for the same order
        BeginOrderProcess(order);

        return View("OrderedProcessedSuccessfully");
    }
}

我要问的第一件事是:在这种操作中,我们可以同时对同一订单有多个请求(即:浏览器对同一订单的快速请求),我是否应该使用悲观锁定来真正确保一个交易时间或有一种方法可以确保BeginOrderProcess几乎不会同时使用乐观锁定(考虑到它可能会更快)对同一订单的两个并发请求进行两次调用?

第二件事:我这样做是完全错误的方式吗?有更好的方法来处理这样的案例吗?换句话说,我应该如何处理这个?:)

4

1 回答 1

1

好的,经过一番研究,我想我已经找到了我想要的。

对于这样的情况,将悲观锁与 nhibernate 一起使用(通过使用session.Lock(order))将是矫枉过正

我选择乐观锁只是因为我以前不知道如何使用它。

下面是代码的样子:

[HttpPost]
public ActionResult BeginOrderProcess(Guid orderKey)
{
    // I confirm, here I really want ReadCommit since I need optimistic lock
    IsolationLevel isolationLevel = IsolationLevel.ReadCommitted;
    using(var tx = new TransactionScope(isolationLevel)){

        // Retreive the order
        var order = GetExistingOrder(orderKey);

        // Validate that the order can be processed
        var validationResult = ValidateOrder(order);

        if (!validationResult.Successful)
        {
            // Order cannot be processed, returning
            return View("ErrorOpeningOrder");
        }

        // Important stuff going on here, but I must be sure it 
        // will never be called twice for the same order
        BeginOrderProcess(order);

        // The main difference is here
        // I need to do an explicit commit here to catch the stale object exception
        // and handle it properly. Before that, 
        // I was handling the commit in the dispose of my TransactionScope
        // Since my transaction scope is in ReadCommit, no one but this request 
        // should be able to read the modified data whatever the changes are
        try{
            try{
                tx.Commit();
            }catch(Exception){
                tx.RollBack();
                throw;
            }            
        }catch(StaleObjectStateException){
            return View("OrderIsCurrentlyBeeingProcessedBySomeoneElse");
        }

        return View("OrderedProcessedSuccessfully");
    }
}

正如我的评论所示,主要区别在于我手动处理我的提交,然后按原样处理异常。有了这个实现,我就不用担心阻塞其他用户的请求,我可以根据需要处理异常。

我正在使用流利的 nhibernate 并且我已经将我的实体配置为在我的映射中使用版本:

OptimisticLock.Version();

Version(x => x.Version)
.Column("EntityVersion")
.Generated.Never()
.Default(0)
.UnsavedValue("null");

有了这个,当我进行提交时,NHibernate 将查看版本并StaleObjectStateException在提交与正确版本不匹配时抛出一个。

NHibernate 快乐 :)

于 2014-10-16T15:48:06.233 回答