I was surprised to see the order numbers jumping by 5,000 after each unit test I ran on a Commerce Server 2007 site. It appears that functionality had changed from 2002. To give you a bit of background, Commerce Server assigns a tracking number to an order as it is converted from a Basket to PurchaseOrder object. This allows you to present a number for the end-user to refer to that isn't a big ugly GUID. The data itself is stored inside the IdentityCounter table in the Transactions database in a row where the CounterName is PuchaseOrder.TrackingNumber.
In Commerce Server 2007 the behaviour was changed, probably for performance reasons, to pre-allocate 5,000 tracking numbers upon the first request for a tracking number. The problem is that if you recycle the application pool, do unit testing, or anything else that sets up and tears down the application pool with some frequency you'll realize that your order numbers are jumping quite quickly.
To get around this behaviour you will need to assign the tracking number before calling Basket.SaveAsOrder. Here's a small stored procedure that will help you do this:
CREATE PROCEDURE [dbo].[RetrieveNextTrackingNumber]
AS
BEGIN
BEGIN TRANSACTION
DECLARE @CurrentId bigint
UPDATE IdentityCounter
SET @CurrentId = CurrentId = CurrentId + 1
WHERE CounterName = 'PuchaseOrder.TrackingNumber'
SELECT @CurrentId
COMMIT TRANSACTION
END
(Thanks to Vinayak Tadas for pointing out an optimization in my original stored procedure!)
And here's some sample C# code using the Enterprise Library to make this call:
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2116:AptcaMethodsShouldOnlyCallAptcaMethods")]
public static Int64 RetrieveNextTrackingNumber(string connectionString)
{
SqlDatabase database = new SqlDatabase(connectionString);
Int64 trackingNumber = (Int64)database.ExecuteScalar("RetrieveNextTrackingNumber");
return trackingNumber;
}
You're probably wondering how to get the transactions connection string though. The last thing you want to do is store it in multiple places. The OrderContext object does expose it, but it's internal. Never fear -- reflection can help us out here:
string TransactionsConnectionString
{
get
{
return typeof(OrderContext).GetProperty("TxnResourceConnectionString", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(MyOrderContext, null) as string;
}
}
Now of course there is the standard disclaimer when using an internal property - it may change behaviour, extenistence, or other nasty things at any given time. If you're writing unit tests though (which you are, aren't you?) then you should be able to pick up any changes in behaviour.