1

I'm developing financial application with ASP.NET MVC and Entity Framework(version 5).

In my application, users can approve or reject transaction. If a user make approve, money will be transfer and approve notification email will be sent, otherwise reject notification email will be sent.

What i'm concern is, if two users come to make approval to the same transaction simultaneously. User will get transaction status "WaitForApprove" and do double transfer(in case that both user make approve).

How can i handle this situation, what is the best solution(make sense & low resources consume).

I've do some research, i found Mutex can handle this. But i'm not sure is it the best solution.

Here is my original code look like(Mutex not applied).

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.");
    }
}
abatishchev
  • 98,240
  • 88
  • 296
  • 433
TaeV
  • 746
  • 12
  • 19
  • Have you looked at SQL Server data type of [`ROWVERSION`](http://msdn.microsoft.com/en-gb/library/ms182776.aspx)? – DavidG Oct 10 '14 at 16:25
  • 1
    Use a database transaction, probably with optimistic concurrency. – Joe Oct 10 '14 at 16:25
  • If you do follow the advice to use a database-stored state for your transaction (and it is good, conventional advice), you may not want to use EF for it. While it probably has wrappers around isolation levels, figuring out the wrappers may be tougher than actually just calling a stored procedure you write yourself (depending on your familiarity with EF I guess). – welegan Oct 10 '14 at 18:03

1 Answers1

3

You need to separate the responsibility of transferring money from the user interface entirely. Do not allow the user interface to set the state of the transfer directly.

Instead, expose methods on the object such as ApproveTransfer() and RejectTransfer() that check the current state of the object and only perform the requested action if the object is in an appropriate state.

The object also needs a mechanism to ensure that the appropriate state is known to any object instance, even one created after the requested action is started in a different object instance, possibly on a different server. To do that, you must turn to the database.

The best strategy for holding that state in the database depends on how you actually perform the transaction.

If you must simply set a flag on the object (that is possibly picked up by another process), a database transaction with optimistic concurrency is likely sufficient.

If your object is initiating calls to other systems (perhaps a web service call to initiate a transfer), you will need to lock the row in the database (transaction with isolation level repeatable read).

Community
  • 1
  • 1
Eric J.
  • 147,927
  • 63
  • 340
  • 553