Changing Order Number Behaviour in Commerce Server 2007

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.

Comments Subscribe to Post Comments Feed

Nihit Kaul said:
Hi Colin,

You do not need to use reflection to get the ConnectionStrings for any commerce resource. You can use the following code snippet instead:

           CommerceResourceCollection resources = new CommerceResourceCollection("StarterSite");

CommerceResource transactionResource = resources["transactions"];

CommerceResource transactionConfigResource = resources["Transaction Config"];

String connstr = transactionResource["connstr_db_Transactions"].ToString();

String configConnStr = transactionConfigResource["connstr_db_TransactionConfig"].ToString();


Thanks,
Nihit
Colin said:
Thanks!  I ended up moving to the resource collection as a way to get the strings in a later release.
Trevor said:

using that conn string:

SqlConnection conn = new SqlConnection(connstr);

won't execute!  it says:

Keyword not supported: 'provider'.

I'm not using Enterprise Library, what do i do?

Trevor said:

Oh.  I guess you have to use OleDbConnection.  Weird.

Colin said:

The Provider tag in the connection string is the key differentiator that will tell you that you need to use an OLE DB connection.  OLE DB supports numerous connections (Excel, SQL, text files, Oracle, etc...).  The Provider keyword tells the stack what connection class to use.  We're all SQL here so we've done some SQL-specific calls.  I might not have included the snippet that shows the code stripping the Provider key/value pair off the connection string in this case.

Trevor said:

Thanks for the info, and great article :)

Andreas said:

Could you do this in a pipeline component?

Andreas said:

Hi, Thanx, used this instead to get the result, worked fine. But remember to set security permissions on the stored procedure on the SQL server.

string TransactionsConnectionString = "server=myitsql01;database=startersite_transactions;Trusted_Connection=yes";

          SqlConnection connection = new SqlConnection(TransactionsConnectionString);

          connection.Open();

          SqlCommand command = new SqlCommand("RetrieveNextTrackingNumber", connection);

   command.CommandType = CommandType.StoredProcedure;

           string OrderNO  = Convert.ToString(command.ExecuteScalar());

           connection.Close();

Colin said:

Andreas - You can certainly use straight ADO.NET.  The Enterprise Library is just a wrapper for that.  As for using a pipeline question, yes you should be able to do it inside the pipeline.

Have Your Say