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.

Comments (5)
  1. Brian Holmgaard Kristensen says:

    Hi Kerry,

    Great example. But I think it's worth mentioning that in order to have your custom price to be effective on the Line Item when adding the product/variant to the basket, you need to do additional development work (pipeline customization).

    But it's certainly doable 🙂

    Best Regards,

    Brian Holmgaard Kristensen

    blog.brianh.dk / http://www.commerceservertraining.com

  2. khavas says:

    You beat me to it.  My next article is on how to overcome that obstacle, though I intend to do it without any pipeline customization (other than removing steps…)

    Thanks for the feedback!

  3. Brian Holmgaard Kristensen says:

    Cool 🙂 Looking forward to reading your next article then

  4. Gadi Berqowitz says:

    for some reason i don't get it to work , i'm getting the following error:

    Type : System.Configuration.ConfigurationErrorsException, System.Configuration, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a

    Message : The value of the property 'type' cannot be parsed. The error is: The type 'PipeComponent.Temp, PipeComponent, Version=1.0.0.0, Culture=neutral,PublicKeyToken=a9d8a9b645e1cf53' cannot be resolved. Please verify the spelling is correct or that the full type name is provided. (C:inetpubwwwrootwssVirtualDirectories23231ChannelConfiguration.config line 124)

    Source : System.Configuration

    Help link :

    BareMessage : The value of the property 'type' cannot be parsed. The error is: The type 'PipeComponent.Temp, PipeComponent, Version=1.0.0.0, Culture=neutral,PublicKeyToken=a9d8a9b645e1cf53' cannot be resolved. Please verify the spelling is correct or that the full type name is provided.

    Filename : C:inetpubwwwrootwssVirtualDirectories23231ChannelConfiguration.config

    maybe it's an issue with the signing of the DLL.

    "gacutil -l PipeComponent.dll" indicates that the assembly is in the GAC,

    but i can't see it in Windowsassembly , I'm not sure it's related but it's worth a try.

  5. khavas says:

    Sorry I didnt see your comment and did not get a notification about it.

    This does appear to be some kind of GAC-related issue.  You should be able to load the assembly if it can find it in the GAC.  I would check to make sure the public key is correct, but if you cant see it in Windows/Assembly then there is something more dramatic wrong.  Is it possible that you compiled for the wrong version (32 vs 64 bit?)

Comments are closed.

Skip to main content