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