处理座圈状况

本文关键字:处理 | 更新日期: 2023-09-27 18:26:13

我正在使用ASP.NET MVC和实体框架(版本5)开发财务应用程序。

在我的应用程序中,用户可以批准或拒绝交易。如果用户批准,将转账并发送批准通知电子邮件,否则将发送拒绝通知电子邮件。

我担心的是如果两个用户同时来审批同一事务。用户将获得交易状态"WaitForApprove"并进行双重转账(以防两个用户都批准)。

我该如何处理这种情况,什么是最佳解决方案(合理且资源消耗低)。

我做了一些研究,发现Mutex可以处理这个问题。但我不确定这是不是最好的解决方案。

这是我的原始代码(Mutex未应用)。

public void TransactionApproval(int sysTransferInfoID, string username, string command)
{
    // Get the transaction.
    var info = _transInfoRepo.GetSingle(i => i.SYS_TRANSFER_INFO_ID == sysTransferInfoID);
    if (info.SYS_TRANSFER_STATE_ID != (int)eTransferState.WaitForApprove) // Check status.
    {
        if (command == "Approve")
        {
            // Call another service to make transfer here.
            // Sent approve fund transfer notification email to other users.
            info.SYS_TRANSFER_STATE_ID = (int)eTranferState.Approved; // Change status.
            info.APPROVE_BY = username;
        }
        else if (command == "Reject")
        {
            // Sent reject fund transfer notification email to other users.
            info.SYS_TRANSFER_STATE_ID = (int)eTranferState.Rejected; // Change status.
            info.APPROVE_BY = username;
        }
        _context.SaveChanges();
    }
    else
    {
        throw new Exception("Cannot approve transaction.");
    }
}

处理座圈状况

您需要将转账的责任完全从用户界面中分离出来。不允许用户界面直接设置传输的状态。

相反,在对象上公开方法,如ApproveTransfer()和RejectTransfer(),它们检查对象的当前状态,并且只有在对象处于适当状态时才执行请求的操作。

对象还需要一种机制来确保任何对象实例都知道适当的状态,即使是在请求的操作在不同的对象实例中启动后创建的对象实例,也可能在不同的服务器上。要做到这一点,您必须转到数据库。

在数据库中保持该状态的最佳策略取决于实际执行事务的方式。

如果必须简单地在对象上设置一个标志(可能由另一个进程获取),那么具有乐观并发性的数据库事务可能就足够了。

如果您的对象正在启动对其他系统的调用(可能是启动传输的web服务调用),则需要锁定数据库中的行(具有隔离级别可重复读取的事务)。