Archive for the ‘CommerceServer’ Category
Limit Exceptions in CS2009
Thursday, March 26th, 2009View Comments
One flaw with the current design of Commerce Server 2009 is how exceptions are trapped. CS2009 wraps the 2007 object model in a service boundary, logging all exceptions that occur within that boundary and raising a generic FaultException with the following message:
The operation service has encountered an error while processing the request. The error details have been logged by the service.
This removes the ability to recover from specific exceptions, e.g. ConfiguredLimitExceededException when a user adds too many line items.
A decent workaround for this is to use the other error reporting mechanism that exists in Commerce Server, tunneling via weak properties, e.g. _Basket_Errors. To do this we need two things:
- Access to the limits element within the Commerce Server section of web.config
- A custom OperationSequenceComponent that will check for limit violations and add a custom status code property to the returned order form.
Accessing the limits element
Commerce Server 2007 uses an internal class, CommerceOrdersConfiguration, to read attributes of the limits element. You can get an instance of this object from the OrderContext using reflection and further use reflection to extract the limit properties needed. See the OrderLimitSettings class in the example code.
Creating a custom OperationSequenceComponent
When new items are added to a basket a call is issued to BasketLineItemsProcessor.CreateRelatedItems. This is part of the CommerceUpdateOperation_Basket operation defined within the channel configuration file. This method receives a collection of line items to create, so we can extend this component to check for limit violations. There’s actually not a lot of code for this:
protected override void CreateRelatedItems(
List<CommerceCreateRelatedItem> createRelatedItemOperations) {
var orderForm = CachedOrderGroup.GetDefaultOrderForm();
if ((orderForm.LineItems.Count + createRelatedItemOperations.Count) >
Limits.LineItemCountPerOrderFormLimit) {
orderForm[StatusCodeKey] = LineItemLimitExceeded;
return;
}
base.CreateRelatedItems(createRelatedItemOperations);
}
Note: For this example it’s all or nothing when adding a batch of line items. If adding a new batch will exceed the configured limit, a status code is returned to indicate error preventing the entire batch from being created. Alternatively you could extend BasketLineItemsProcessor.CreateRelatedItem allowing individual line items to be inserted until one violates a limit.
Once the component is complete we need to register it in the CommerceUpdateOperation_Basket operation, within the channel configuration file, by modifying the existing Line Items processor to point to the new class.
You can download an example here.
Using Commerce Server 2009 outside of a Web Application
Tuesday, February 10th, 2009View Comments
If you try running Commerce Server 2009 outside of a web application you’ll likely encounter a few exceptions along the way. The hurdles are setting up configuration settings and initializing the context objects.
Setting up a Console Application
I created an example to query a catalog product, using the following steps for setting up a Visual Studio 2008 Console Application with Commerce Server 2009 RC1:
- Unpup
C:\Program Files\Microsoft Commerce Server 2007\Microsoft Commerce Server 2009\Site\MicrosoftCommerceDefaultSiteWithSampleData.pupto a site namedConsoleSite. - Add the assemblies located at
C:\Program Files\Microsoft Commerce Server 2007\Microsoft Commerce Server 2009\Assembliesas references to the project. - Copy
ChannelConfiguration.config(editing siteName) andMetadataDefinitions.xmlfromto the solution directory and add them to the solution.
C:\Program Files\Microsoft Commerce Server 2007\Microsoft Commerce Server 2009\Sdk\Samples - Unpack
CommerceSharePointExtensibilityKit.zipfromC:\Program Files\Microsoft Commerce Server 2007\Microsoft Commerce Server 2009\Sdk\SamplestoC:\CommerceSharePointExtensibilityKit\. - Piece together an
app.configfile using the configuration fragment files in the unpacked extensibility kit located atC:\CommerceSharePointExtensibilityKit\SharePointCommon\FeatureActivation\DefaultConfigs\.
Initializing Context Objects
In Commerce Server 2007 you have to manually create each subsystem context during your application initialization process, when executing outside of a web site. In Commerce Server 2009 context initialization is controlled by the useSharedCommerceContexts attribute in the channel configuration file:
<ServiceConfiguration useSharedCommerceContexts="true">
...
</ServiceConfiguration>
The default value for useSharedCommerceContexts is true. If you try running your non-web application with this setting, you will see the following error in the event log:
...
Microsoft.Commerce.Providers.Exceptions.ContextCreationException, ...
Message : Configuration specifies useSharedCommerceContexts=true however this is only valid for a Commerce Server enabled web site runtime environment.
...
You need to set this value to false and add some additional configuration elements.
Searching the CS2009 documentation for useSharedCommerceContexts reveals one result explaining a work around when specifying useSharedCommerceContexts="false". It outlines additional configuration settings to include in the channel configuration file and displays the following warning regarding useSharedCommerceContexts:
Currently only “true” is supported, meaning that Commerce Server 2009 is running as a shared Commerce Server resource
FWIW, I’ve been using this technique for automated unit testing on my current project without any issues.
The example I created queries the Adventure Works catalog for a product and displays a few of its properties. Here’s what the output looks like:

You can download the example application here.
TDD and Commerce Server 2007: Custom Rollback
Wednesday, January 28th, 2009View Comments
In my previous post I mentioned using a TransactionScope to rollback Commerce Server database updates, when unit testing, may not always work. Turns out it doesn’t work with the profile subsystem.
To verify this, I coded an example similar to the address example on MSDN, resulting in the following test:
[Fact]
public void Should_save_address_changes() {
var addressId = Guid.NewGuid();
var addressService = new AddressService(TestProfileContext);
var address = createNewAddress(addressService, addressId);
addressService.Save(address);
var savedAddress = addressService.Find(addressId);
Assert.Equal(address.Line1, savedAddress.Line1);
Assert.Equal(address.City, savedAddress.City);
Assert.Equal(address.State, savedAddress.State);
Assert.Equal(address.ZipCode, savedAddress.ZipCode);
}
For this test, I extended the CommerceFixture class (created in my previous post) to create a TestProfileContext instance. If this test is executed, it will always save a new address to the Commerce Server Profiles Database. If we try adding an AutoRollback attribute to the test, the new address doesn’t get rolled back.
To fix this we can call TestProfileContext.DeleteProfile when the test completes. An easy way to do this, while making the code exception safe, is to create a scope class that implements IDisposable. This allows us to wrap the test in a using block, reducing the amount of code we need to type. Here’s an example implementation:
public class ProfileScope : IDisposable {
private readonly ProfileContext _profileContext;
private readonly string _keyValue;
private readonly string _profileType;
public ProfileScope(ProfileContext context, string keyValue, string profileType) {
_profileContext = context;
_keyValue = keyValue;
_profileType = profileType;
}
public void Dispose() {
_profileContext.DeleteProfile(_keyValue, _profileType);
}
}
Now we can update the test, adding a using block with our scope object:
[Fact]
public void Should_save_address_changes() {
//arrange
using (new ProfileScope(TestProfileContext, addressId.ToString(),
AddressService.ProfileName)) {
addressService.Save(address);
//assert
}
}
We can trim the code down even further by adding a helper method to the base test class, CommerceFixture:
public class CommerceFixture {
...
public static ProfileScope ProfileScope(string keyValue, string profileType){
return new ProfileScope(TestProfileContext, keyValue, profileType);
}
...
}
Which gives us our final version:
[Fact]
public void Should_save_address_changes() {
//arrange
using (ProfileScope(addressId.ToString(), AddressService.ProfileName)) {
addressService.Save(address);
//assert
}
}
You can download the code for this example here.
TDD and Commerce Server 2007: Getting Started
Thursday, January 22nd, 2009View Comments
Mixing Commerce Server 2007 with unit testing frameworks is a little tricky. The few examples I’ve seen use MsTest specific features to route tests through ASP.NET or IIS when executing, providing the following drawbacks:
- Increased test execution time (sucks worse when running on a VPC)
- Increased cost for creating tests
Techniques like dependency injection can be used to significantly reduce the number of tests that require a web host for execution. I’m using the Orders subsystem here, but the same techniques can be used with other subsystems as well.
Example Code
For this example I unpacked the CSharp.pup file to create a new site named CommerceSample. You can download the example code here.
Breaking Dependencies
Commerce Server is tightly coupled to ASP.NET and this causes problems when trying to execute Commerce Server API code outside of a web application. Suppose we try to execute the following code within a unit testing framework:
var userId = Guid.NewGuid();
var basket = OrderContext.Current.GetBasket(userId);
This produces a NullReferenceException due to an uninitialized singleton, OrderContext.Current. In a web application scenario this context is initialized by the HttpModule CommerceOrderModule.
Establishing a Test Context
To execute code like this within a testing framework, we need to create an instance of OrderContext. One approach is to create a custom base class for tests requiring the use of an OrderContext, as shown below:
public class CommerceFixture {
public const string SiteName = "CommerceSample";
public static readonly OrderContext TestOrderContext =
OrderContext.Create(SiteName);
}
If I have a test that requires an OrderContext, my test class can now extend this class and have access to the TestOrderContext instance. One thing to point out: OrderContext.Create will also initialize OrderContext.Current, so technically you don’t need to store the reference in an accessible property.
Wrapping and Injecting Context Objects
When working with context objects, I like to create wrappper types and inject the context into the wrapper. This allows for the following benefits:
- The ability to add new behavior to context objects (the usual benefit of wrapper classes)
- No dependence on singletons (a lot of material has been written about this already)
- Narrowing the API. Sometimes context objects can be a dumping ground for anything and everything. Wrapper classes provide a nice way to offer a subset of this functionality specific to my application.
Rolling Back Updates
If a test updates a database, we want to make sure the updates are removed upon test completion, preventing unwanted side effects. Most mature testing frameworks provide some form of a rollback attribute, which wraps each test in a TransactionScope instance (if you’re stuck with MsTest, you can wrap your test code in a using TransactionScope block).
Note: Originally I was told using a TransactionScope might not work with Commerce Server API’s due to legacy COM code, so I’ve actually never used this during full project development. I didn’t realize this worked until playing with this example while creating this post. One context type it might not work with is the ProfileContext. That said, there’s more than one way to rollback changes. I’ll discuss the rollback approach I’ve used before in a subsequent post.
An Example
Lets polish the recurring order example from MSDN. I’d like to have a method that accepts a user id and a template name (basket name) and returns a basket that is already populated with items from the saved template:
var cartService = new ShoppingCartService(orderContext);
var cart = cartService.CreateFromRecurringOrder(userId, templateName);
ShoppingCartService is an example of a thin wrapper, where the context is injected in.
To setup a test I need to create a recurring order template in the database, so my service class can find it. This is the update I want rolled back upon test completion. My final test looks like this:
[Fact, AutoRollback]
public void Should_create_new_order_from_recurring_order_for_user() {
var userId = Guid.NewGuid();
var templateName = "MonthlyOrder";
var cartService = new ShoppingCartService(TestOrderContext);
var recurringOrder = buildRecurringOrder(cartService, userId, templateName);
var cart = cartService.CreateFromRecurringOrder(userId, templateName);
Assert.Equal(recurringOrder.LineItems.Count, cart.LineItems.Count);
}
Where buildRecurringOrder is a helper function that creates an order template in the database. For this test I used xUnit.net. When the test completes, the order I saved will no longer exist. You can use the Customer and Orders Manager to verify that your tests are not leaking baskets and orders.
Future Topics
In subsequent posts I’ll discuss the following topics:
- Using custom scope objects for situations where TransactionScope might not work
- How to handle pipeline execution with testing frameworks
- Testing with Commerce Server 2009
Related Information
- Dependency Injection
- Misko Hevery discussing hard to test third party code
- Context object pain. See Misko here
- Singleton pain. See Misko again
