Commerce Server 2009 Operation Sequence Component Extensibility

Commerce Server 2009’s Commerce Foundation added a great extensibility layer. Every call into the foundation flows through a series of components called Operation Sequence Components.

image

 All of the out-of-the-box functionality, which wraps the Commerce Core Services is implemented as Operation Sequence Components.

The Operation Sequence Components that get called when a particular message comes through the foundation is defined in the ChannelConfiguration.config file. This file has an entry for every message and a list of components to call to build up a response to that message.

<MessageHandler name="CommerceQueryOperation_Product" responseType="Microsoft.Commerce.Contracts.Messages.CommerceQueryOperationResponse, Microsoft.Commerce.Contracts, Version=1.0.0.0, Culture=neutral,PublicKeyToken=31bf3856ad364e35">
<OperationSequence>
<Component name="Product Query Processor" type="Microsoft.Commerce.Providers.Components.ProductLoader, Microsoft.Commerce.Providers, Version=1.0.0.0, Culture=neutral,PublicKeyToken=31bf3856ad364e35">
<Component name="Cross-Sell Provider" type="Microsoft.Commerce.Providers.Components.ProductCommerceServerRelationshipProcessor, Microsoft.Commerce.Providers, Version=1.0.0.0, Culture=neutral,PublicKeyToken=31bf3856ad364e35">
<Configuration customElementName="CommerceServerRelationshipConfiguration" customElementType="Microsoft.Commerce.Providers.Components.CommerceServerRelationshipConfiguration, Microsoft.Commerce.Providers, Version=1.0.0.0, Culture=neutral,PublicKeyToken=31bf3856ad364e35">
<CommerceServerRelationshipConfiguration commerceServerRelationshipName="CrossSell" internalRelationshipName="CrossSells" />
</Configuration>
</Component>
<Component name="Up-Sell Provider" type="Microsoft.Commerce.Providers.Components.ProductCommerceServerRelationshipProcessor, Microsoft.Commerce.Providers, Version=1.0.0.0, Culture=neutral,PublicKeyToken=31bf3856ad364e35">
<Configuration customElementName="CommerceServerRelationshipConfiguration" customElementType="Microsoft.Commerce.Providers.Components.CommerceServerRelationshipConfiguration, Microsoft.Commerce.Providers, Version=1.0.0.0, Culture=neutral,PublicKeyToken=31bf3856ad364e35">
<CommerceServerRelationshipConfiguration commerceServerRelationshipName="UpSell" internalRelationshipName="UpSells" />
</Configuration>
</Component>
<Component name="Product Variant Provider" type="Microsoft.Commerce.Providers.Components.ProductVariantProcessor, Microsoft.Commerce.Providers, Version=1.0.0.0, Culture=neutral,PublicKeyToken=31bf3856ad364e35" />
<Component name="ProductAncestorCategoryProcessor" type="Microsoft.Commerce.Providers.Components.ProductAncestorCategoryProcessor, Microsoft.Commerce.Providers, Version=1.0.0.0, Culture=neutral,PublicKeyToken=31bf3856ad364e35" />
<Component name="ProductCanonicalCategoryProcessor" type="Microsoft.Commerce.Providers.Components.ProductCanonicalCategoryProcessor, Microsoft.Commerce.Providers, Version=1.0.0.0, Culture=neutral,PublicKeyToken=31bf3856ad364e35" />
<Component name="ProductParentCategoryProcessor" type="Microsoft.Commerce.Providers.Components.ProductParentCategoryProcessor, Microsoft.Commerce.Providers, Version=1.0.0.0, Culture=neutral,PublicKeyToken=31bf3856ad364e35" />
<Component name="ProductPrimaryCategoryProcessor" type="Microsoft.Commerce.Providers.Components.ProductPrimaryCategoryProcessor, Microsoft.Commerce.Providers, Version=1.0.0.0, Culture=neutral,PublicKeyToken=31bf3856ad364e35" />
<Component name="ProductRelatedCategoryProcessor" type="Microsoft.Commerce.Providers.Components.ProductRelatedCategoryProcessor, Microsoft.Commerce.Providers, Version=1.0.0.0, Culture=neutral,PublicKeyToken=31bf3856ad364e35" />
<Component name="ProductRelatedProductProcessor" type="Microsoft.Commerce.Providers.Components.ProductRelatedProductProcessor, Microsoft.Commerce.Providers, Version=1.0.0.0, Culture=neutral,PublicKeyToken=31bf3856ad364e35" />
<Component name="ProductInventoryItemsProcessor" type="Microsoft.Commerce.Providers.Components.ProductInventoryItemsProcessor, Microsoft.Commerce.Providers, Version=1.0.0.0, Culture=neutral,PublicKeyToken=31bf3856ad364e35" />
<Component name="Product catalog relationship" type="Microsoft.Commerce.Providers.Components.ProductCatalogRelationshipProcessor, Microsoft.Commerce.Providers, Version=1.0.0.0, Culture=neutral,PublicKeyToken=31bf3856ad364e35" />
<Component name="Product base catalog relationship" type="Microsoft.Commerce.Providers.Components.ProductBaseCatalogRelationshipProcessor, Microsoft.Commerce.Providers, Version=1.0.0.0, Culture=neutral,PublicKeyToken=31bf3856ad364e35" />
<Component name="Product Discounts Relationship" type="Microsoft.Commerce.Providers.Components.CatalogEntityDiscountsProcessor, Microsoft.Commerce.Providers, Version=1.0.0.0, Culture=neutral,PublicKeyToken=31bf3856ad364e35" />
</Component>
</OperationSequence>
</MessageHandler>

This creates a great extensibility model because you can create your own custom Operation Sequence Component and insert it before, after or in between the out-of-the-box Operation Sequence Components. You can even replace the out-of-the-box components with your own if you want to connect to an LOB system or create your own customer data providers.

If you create a component and insert it before the out-of-the-box components, then you have an opportunity to examine and/or modify the query. If you insert the component after, then you can examine and/or modify the results that will be returned.

It’s possible, for example to implement a component that executes business logic to modify the price of a product based on certain conditions.

As a simple, albeit ridiculous, example, suppose I wanted to lower the price of my products to $1 if it is Saturday.

To start with I would create a new class in Visual Studio like this:

using Microsoft.Commerce.Providers.Components;
using Microsoft.Commerce.Contracts;

namespace Microsoft.Commerce.Samples.PriceEngine
{
public class PriceEngine:OperationSequenceComponent
{
public override void ExecuteQuery(Microsoft.Commerce.Contracts.Messages.CommerceQueryOperation queryOperation, Microsoft.Commerce.Broker.OperationCacheDictionary operationCache, Microsoft.Commerce.Contracts.Messages.CommerceQueryOperationResponse response)
{
if (System.DateTime.Now.DayOfWeek == System.DayOfWeek.Saturday)
{
foreach (var commerceEntity in response.CommerceEntities.FindAll(c => c.ModelName == "Product"))
{
commerceEntity.Properties["ListPrice"] = 1;
foreach (var variant in commerceEntity.Properties["Variants"] as CommerceRelationshipList)
{
variant.Target.Properties["ListPrice"] = 1;
}
}
}
}
}
}

Now sign, build and copy this component to the GAC.

Next open up the ChannelConfiguration.config file and add an entry to the end of the list of compoents to call when a product is retrieved.

<MessageHandler name="CommerceQueryOperation_Product" responseType="Microsoft.Commerce.Contracts.Messages.CommerceQueryOperationResponse, Microsoft.Commerce.Contracts, Version=1.0.0.0, Culture=neutral,PublicKeyToken=31bf3856ad364e35">
<OperationSequence>
<!—out-of-the-box components here—>
<Component name="PriceEngine" type="Microsoft.Commerce.Samples.PriceEngine.PriceEngine, Microsoft.Commerce.Samples.PriceEngine, Version=1.0.0.0, Culture=neutral,PublicKeyToken=c70c9a2d1f784c32" />

</OperationSequence>
</MessageHandler>

Now, when a product is retrieved, it will automatically execute your additional business logic and reduce the price to $1 when it is Saturday.

image

Obviously, you can execute much more complex logic or even call out to an external pricing service.