NHibernate - 如何在ASP.NET MVC操作中处理关键事务

时间:2014-10-15 15:21:40

标签: asp.net-mvc nhibernate transactions optimistic-locking pessimistic-locking

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

以下是我需要做的简化示例:

[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永远不会被调用两次并发同一订单的两个并发请求几乎同时进行乐观锁定(考虑到它可能会更快)? / p>

第二件事:我是以完全错误的方式做到这一点,并且有更好的方法来处理这样的案件?换句话说,我该如何处理? :)

1 个答案:

答案 0 :(得分: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

快乐的NHibernating:)