Generate Invoice Number for Multi user environment in C# - c#

I am generating invoice numbers using Max function and adding 1 to the last invoice number. Very straightforward. but i applied this into Multi User environment getting problem. because two users open a invoice window at a same time both could get same id, invoice number should be the first thing to appear and not the last so i cannot use identity (auto generate id) for invoice number. I want to generate invoice number for Multi User environment windows form application in C#...
The other problem is what will happen two users accessing and updating the same record same time.
I hope you will understand the problem. i read about the Optimistic vs. Pessimistic locking but i need a solution. so can anyone please reply to me

Have a different database table that will store the max invoice number.
When a user opens the invoice window, run a stored procedure to:
Lock the table
Get the current number
Store the current+1 number
Unlock the table
Return the current+1 number
This will ensure that even though there are simultaneous requests you will always get a unique invoice number.
The flip side:
1. This stored procedure cannot run simultaneously for multiple users so it would be a bottleneck in case of high traffic.
2. There will be holes in the invoice numbers - holes where the invoices are cancelled.
If you are not positive with this approach then the invoice number must be generated at the time of saving and it would be an IDENTITY column but you have mentioned that users would like to see the invoice number when they start working on the invoice.
Update:
I found an excellent article that elaborates the above approach using sp_getapplock. The article link is HERE. I would recommend to use this approach.

Generally the problem is your application makes bad requirements and the approach is not suitable.
The invoice number should not be allocated when the invoice is created for editing, but when it is put into the system for transaction. This way an abort does not leave gaps.
Collect the invoice details, then create the insert via a stored procedure that puts the proper locks in place. This is trivial - if you know how to put either a lock on the relevant table, or use an applock for this SP.
Generally this is one of the few places a stored procedure makes sense. Generates a non-accounted for invoice with 0 line items so your application then can add the details. Voila, problems solved. If the user aborts, mark the invoice as "cancelled" and finished.

The auto increment column is the best solution. this is what we call Identity column in SQL Server.
If in your case you are not able to use it you could make a thread safe method in your server side code to generate the invoice id based on a static attribute.
Make sure that you are using a service in a single instance mode.
static long invoiceId = getMaxFunction();
...
...
public long GenerateInvoiceId()
{
lock(this)
{
return invoiceId++;
}
}

Related

Will DbContextTransaction.BeginTransaction prevent this race condition

I have a method that needs to "claim" a payment number to ensure it is available at a later time. I cannot just get a new payment number when ready to commit to the database, as the number is added to a signed token, and then the payment number is taken from the signed token later on when committing to the database to allow the token to be linked to the payment afterwards.
Payment numbers are sequential and the current method used in existing code is:
Create a Payment
Get the last payment number from the database
Increment the payment number
Use this payment number for the Payment
Update the database with the incremented payment number
In my service I am trying to prevent the following race-condition:
My service reads the payment number (eg. 100)
Another service uses and updates the payment number (now 101)
My service increments the number locally (to 101) and updates the database (still 101)
This would produce two payments with a payment number of 100.
Here is my implementation so far, in my Transaction class:
private DbSet<PaymentIdentifier> paymentIdentifier;
//...
private int ClaimNextPaymentNumber()
{
int nextPaymentNumber = -1;
using(var dbTransaction = db.Database.BeginTransaction())
{
int lastPaymentNumber = paymentIdentifier.ElementAt(0).Identifier;
nextPaymentNumber = lastPaymentNumber + 1;
paymentIdentifier.ElementAt(0).Identifier = nextPaymentNumber;
db.SaveChanges();
dbTransaction.Commit();
}
return nextPaymentNumber;
}
The PaymentIdentifier table has a single row and a single column "Identifier" (hence the .ElementAt(0)). I am unable to change the database structure as there is lots of legacy code relying on it that is very brittle.
Will having the code wrapped in a transaction (as I have done) protect against the race condition, or is there some Entity Framework / PostgreSQL idiosyncrasies I need to deal with to protect the identifier from being read whilst performing the transaction?
Thank you!
(As a side point, I believe lots of legacy code in the other software connecting to the database simply ignores the race condition and relies on it being "very fast")
It helps you with the race condition only if all code, including legacy, will use this method. If there is still code that continue using client side incrementing without transaction, you'll get the same problem. Just exchange 'My service' and 'Another service' in your description.
1. Another service reads the payment number (eg. 100) **without** transaction
2. My service uses and updates the payment number (now 101) **with** transaction
3. Another service increments the number locally (to 101) and updates the database (still 101) **without** transaction
Note that you can replace your code with simpler one by executing this query without explicit transaction.
update PaymentIdentifier set Identifier = Identifier + 1 returning Identifier;
But again, it will not solve your concurrency problem until you replace all places where the Identifier is incremented. If you can change that, you would better use SEQUENCE or Generators that will safely provide you with incremental Ids.
A transaction does not automaticaly lock your table. A Transaction just ensures that multiple changes to the database are done altogether or nothing at all (see the A (atomic) in ACID). But the thing you want is that only one session can read, add one, update the value. And after that is done the next session is allowed to do the same thing.
So you now have different possibilities:
Use a Sequence you can get the next value for example like that SELECT nextval('mysequencename'). If if two sessions try to get a value at the same time they will get two differnt values.
If you have more complex needs and want to store every "token" with additional data in a table. so every token is a row in the table with additional colums you could use table locking. With this you could restrict the access to table. So only one session is allowed to access the table at a time. But make sure that you use locks for as short as possible because this will become your performance bottleneck.
The database prevents the race condition by throwing a concurrency violation error in this case. So, I looked at how this is handled in the legacy code (following the suggestion by #sergey-l) and it uses a simple retry mechanism. So, I did the same:
private int ClaimNextPaymentNumber()
{
DbContextTransaction dbTransaction;
bool failed;
int paymentNumber = -1;
do
{
failed = false;
using(dbTransaction = db.Database.BeginTransaction())
{
try
{
paymentNumber = TryToClaimNextPaymentNumber();
}
catch(DbUpdateConcurrencyException ex)
{
failed = true;
ResetForClaimPaymentNumberRetry(ex);
}
dbTransaction.Commit();
concurrencyExceptionRetryCount = 0;
}
}
while(failed);
return paymentNumber;
}

C# inventory updating stock

I am building an app with a SQL Server database. I have a main table of products (tblProducts) with a column that holds the quantity in hand (quantity). Another table holds the orders (tblOrders) that come from the supplier.
When an order comes in, I add the order to my database (tblOrders) and then I edit tblProducts to add to the quantity column the new received product.
As far, everything is good.
My question: after let's say 1 year of many many orders, with a lot of edits in quantity, do you guys, periodically check all orders to check if the quantity in main table tblProducts is correct ? Or do I just assume that it is always correct?
What procedures do you use for updating this kind of database? Do you sum all orders every time when you need quantity in hand?
Thanks!
This is really up to how you want to implement it.
Trusting that the values will always check out (with adequate testing to ensure only stable code will see production) is the easiest and the fastest way, but might be vulnerable to data corruption, and thus, not that recommended.
Always summing up the orders is the safest way and correct way, but will become increasingly slower as the size of your tables grow. If this is not an issue for you, then this is the recommended option.
What I consider a good intermediate method is to have a separate tblProductLogs table which stores the stock of an item at a specific timestamp. You can sum the inventory at set periods (daily, hourly, up to you), and when you want to retrieve the current inventory stock you only need to sum the values that were registered after the last log entry for that item, saving you query time. This could be made more safe if update operations were disabled on the log table, since you won't need to modify the entries there. This is faster than the second option, and somewhat more stable than the first.

Generate Unique Random Number

I know similar questions have been asked, but I have a rather different scenario here.
I have a SQL Server database which will store TicketNumber and other details. This TicketNumber is generated randomly from a C# program, which is passed to the database and stored there. The TicketNumber must be unique, and can be from 000000000-999999999.
Currently, what I do is: I will do a select statement to query all existing TicketNumber from the database:
Select TicketNumber from SomeTable
After that, I will load all the TicketNumber into a List:
List<int> temp = new List<int>();
//foreach loop to add all numbers to the List
Random random = new Random();
int randomNumber = random.Next(0, 1000000000);
if !(temp.Contain(randomNumber))
//Add this new number to the database
There is no problem with the code above, however, when the dataset get larger, the performance is deteriorating. (I have close to hundred thousand of records now). I'm wondering if there is any more effective way of handling this?
I can do this from either the C# application or the SQL Server side.
This answer assumes you can't change the requirements. If you can use a hi/lo scheme to generate unique IDs which aren't random, that would be better.
I assume you've already set this as a primary key in the database. Given that you've already got the information in the database, there's little sense (IMO) in fetching it to the client as well. That goes double if you've got multiple clients (which seems likely - if not now then in the future).
Instead, just try to insert a record with a random ID. If it works, great! If not, generate a new random number and try again.
After 1000 days, you'll have a million records, so roughly one in a thousand inserts will fail. That's only one a day - unless you've got some hard limit on the insertion time, that seems pretty reasonable to me.
EDIT: I've just thought of another solution, which would take a bunch of storage, but might be quite reasonable otherwise... create a table with two columns:
NaturalID ObfuscatedID
Prepopulate that with a billion rows, which you generate by basically shuffling all the possible ticket IDs. It may take quite a while, but it's a one-off cost.
Now, you can use an auto-incrementing ID for your ticket table, and then either copy the corresponding obfuscated ID into the table as you populate it, or join into it when you need the ticket ID.
You can create a separate table with only one column . Lets just name it UniqueID for now. Populate that column with UniqueID = 000000000-999999999. Everytime you want to generate a random number, do something like
SELECT TOP 1 UniqueID From (Table) WHERE UniqueID NOT IN (SELECT ID FROM (YOUR TABLE))
Code has not been tested but just to show the idea

Concurrency issue

We are creating a client server application using WPF/C# with SQL. Here we are generating a unique number b checking DB(To get the last maximum number) and with that max value, we are increment '1' and storing the value in DB. At this time another user also working on the same screen and creating unique numbers, in some case the the unique numbers gets duplicated and throws exception.
We found this is a concurrency issue.
Indeed, fetching a number out, adding one, and hoping it still isn't in use is a thread-race and a race between multiple clients - and should be avoided.
Options:
use an IDENTITY column in the database, and let the database generate the value itself during INSERT; the database server knows how to do this safely and reliably
if that isn't possible, you might want to delay this code until you are ready to INSERT so it is all part of a single database operation - and even then, if it isn't in a "serializable transaction" (with key-range read locks, etc), then you would have to loop on "get the max, increment, try to insert but note that we might have lost a race, so only insert if the value doesn't exist - which it might; repeat from start if unsuccessful"
alternatively, you could create the new record when you first need the number (even though the rest of the data isn't available), noting that you might still need the "loop until successful" approach
Frankly, the IDENTITY column approach is the simplest.
Finally, We have follwed Singleton pattern with lock to resolver this issue.
Thanks.

Get Identity Attribute value before inserting in Database

I have a table and it has one of the attribute set as identity. I want to get the value of the identity attribute that would be generated after I enter a value to the database.
I have EmpTable made of EmpID and EmpName. EmpID is set as Identity. I want to fetch the EmpID value before inserting a new row to the database.
I would advise against trying to do this with a table that is set up to use an integer column as the primary key. You will run into concurrency problems if you simply fetch the previous ID and increment it. Instead you should use a GUID (uniqueidentifier in SQL) as your primary key.
This will allow you to generate a new GUID in your code that can safely be saved to the database at a later stage.
http://msdn.microsoft.com/en-us/library/system.guid.newguid.aspx
http://msdn.microsoft.com/en-us/library/ms187942.aspx
Sure the server knows where the auto-increment count is in its sequence, but there is almost nothing useful you can do with that information. Imagine you go to the Post Office and they hand out numbered tickets so they can serve customers in order. Of course you could ask them what the next number they'll give out is, but since anyone can walk in at any time you don't know you'll get that number. If you don't know that you'll get it, you can't do anything with it - e.g. writing it as a reference number on a form would be a mistake.
Depending on what you're trying to do, your two main options are:
Use a client-generated guid as your identifier. This kind of messes up the order so the analogy isn't great, but imagine if each customer who walked in could generate a random number that they are sure would never have been used before. They could use that to fill out forms before taking a number.
Take a number, but do it in a transaction with the other operations. A customer can take a number and use it to fill out some paperwork. If they realize they left their money at home, they just throw everything away and you never call their number.
Why do you think you need this information? Can you use either of these strategies instead?

Categories