Sequential(Gapless) Invoice Number Creation

2019-09-04 23:11发布

问题:

We have implemented a webapplication for Billing system.I have a string column to store invoice number. This column has to forbid gap between number.

  Id     InvoiceNumber(Desired)    InvoiceNumber(Actual)
   1         001                        001
   2         002                        002
   3         003                        005

In pseudo-code

using (TransactionScope _ts = new TransactionScope())
{

     var _lastGeneratedRecDetails = _db.StudentReceipts
                                            .Where(r => r.Status == true
                                                     && r.StudentRegistration.StudentWalkInn.CenterCode.Id == _centreCodeId
                                                     && EntityFunctions.TruncateTime(r.DueDate.Value) >= _startDate.Date
                                                     && EntityFunctions.TruncateTime(r.DueDate.Value) <= _endDate.Date)
                                            .AsEnumerable()
                                            .OrderByDescending(x => x.ReceiptNo != null ? Int32.Parse(x.ReceiptNo) : 0)
                                            .FirstOrDefault();

       var _newReceiptNo=Convert.ToInt32(_lastGeneratedRecDetails.ReceiptNo) + 1
     ....
     ....
     _ts.complete();

}

The Problem Everything was running ok till 50thinvoice, after which the system suddenly skipped 2 invoices and generated Invoice 52. We investigated the system state and found that system was not tampered externally and we also inferred that nobody accessed database from workbench. This is a major issue since it also has legal ramifications.

I have gone through several research and pinpointed to following solutions

1.Lock the entire table during transaction and release it after complete.Here my concern is what happens if server crashes/internet connection gets disconnected before the release of the lock

  using (Entities entities = new Entities())
  using (TransactionScope scope = new TransactionScope())
  {
      //Lock the table during this transaction
       entities.Database.ExecuteSqlCommand("SELECT TOP 1 KeyColumn FROM     MyTable WITH (TABLOCKX, HOLDLOCK)");

      //Do your work with the locked table here...

      //Complete the scope here to commit, otherwise it will rollback
      //The table lock will be released after we exit the TransactionScope block
      scope.Complete();
  }

2.Use storedprocedure for generating the invoicenumber and the procedure will be some thing like this

START TRANSACTION;
SELECT number FROM invoice_numbers WHERE name='main' FOR UPDATE;

assign the number to the invoice and set its status to finalized;

UPDATE invoice_numbers SET number = number + 1 WHERE name='main';
COMMIT;

If anyone could help me out in choosing which one could be right solution it will be of great help