Upcoming courses:

  • Aarhus, Denmark, March 5 - 9, 2012
  • New York City, USA, March 26 - 30, 2012
Read more on our website

About me

Brian Holmgård Kristensen

Hi, I'm Brian. I'm a Danish guy primarily working with ASP.NET e-commerce solutions using Microsoft Commerce Server.

I'm co-founder and core-member of Aarhus .NET Usergroup (ANUG), which is a offline community for .NET developers in Denmark.

You can visit my View Brian Holmgård Kristensen's profile on LinkedIn or follow me on Twitter @brianh_dk. Also please feel free to contact me via e-mail Send me an e-mail.


On this page

Announcing CSUtilities library
Commerce Server as a Repository
REF: How to implement Custom Shipping Methods in Commerce Server
How to implement Custom Shipping Methods in Commerce Server
Commerce Server Training in November 2010 – still spots left
Only one OrderForm in Commerce Server 2009
Commerce Server training videos
How to retrieve Category Rank in Commerce Server 2009



The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

RSS 2.0

Send mail to the author(s) E-mail

Total Posts: 36
This Year: 0
This Month: 0
This Week: 0
Comments: 10

Sign In

Follow me on Twitter @brianh_dk
 Monday, 21 February 2011

As a contribution to the Commerce Server developer community I’ve decided to start working on a small library that will provide some useful functionality for developing Microsoft Commerce Server solutions.

The library was initially hosted on MSDN Code but due to recent restructuring of the way that hosting facility works, I’ve decided to move it to Google Code. From here you can follow the roadmap, change history, download the latest code base (using any SubVersion client) and read documentation on how to use the CSUtilities library.

More information on the CSUtilities library can be found at

Roadmap of CSUtilities

  • Avoid magic strings with Code Generation based on MetadataDefinitions.xml (RELEASED – see below)
  • Get Connection Strings to Commerce Server databases
  • Custom Shipments (replace the built-in Shipping Methods)
  • Custom Payments (replace the built-in Payment Methods)
  • Custom Pricing (pricing outside Commerce Server - ERP, Custom Database, other)
  • LineItemRollup with Commerce Server 2009
  • Multi-currency pricing in a single catalog
  • Replace the Profiles System with a real ORM
  • ASP.NET Templates (.NET 3.5/4.0, MVC/WebForms/Razor)
  • Auto generate Order Schema (SQL/XSD)
  • Foundation API extension methods
  • Deploy pipelines
  • and hopefully a lore more :)


The first release of the new CSUtilities library is now available for you to download on Google Code.

Read more about this release at

Posted on Monday, 21 February 2011 21:13:33 (Romance Standard Time, UTC+01:00)
# | Comments [3]
 Wednesday, 12 January 2011

Working with Enterprise Applications like large e-commerce sites often requires interaction with different systems for different purposes, like:

  • Retrieving catalog data (products and categories)
  • Retrieving customer information
  • Retrieving inventory skus
  • Retrieving prices
  • Sending fullfilled orders
  • … and the list continues …

In Vertica most of our projects are based on the principles of Domain-Driven Design. In short DDD is all about keeping the focus on the domain – e.g. a B2B-driven e-commerce site. The way we structure Commerce Server is to look at Commerce Server as a number of repositories applying the Repository Pattern. That means that we have somewhat few classes that encapsulate all Commerce Server specific logic and return not Commerce Server data types (Microsoft.CommerceServer…*) but data types that we have defined in our own application.

This is illustrated in the image below.



  • We need to map from Commerce Server types (ICommerceEntity in CS 2009)
    • Solution: We have created functionality that based on attributes and conventions can do the mappings automatically


  • All Commerce Server code/queries are encapsulated in few classes
    • Not all developers need to know about Commerce Server
    • Upgrading to newer versions becomes less painfull
  • Commerce Server repositories are mockable allowing us to unit-test functionality in caller classes
  • We cache only lightweight objects that we own and not the large DataSets returned by Commerce Server
    • We can use distributed caching

Properly there are more pros and cons than mentioned above. Please feel free to add your comment on this.

My point is that while Commerce Server is definitely a great platform and a good investment, and I’ll explain why I think so in the section below, it is reasonable to think outside the box when you develop your application. You don’t need to store anything in Commerce Server and you don’t need to spent that much time with their API’s rather than spending the time on the actual feature that you are implementing.

Commerce Server as a platform provides data storage, end-points and APIs for many of these purposes via the different subsystems (Catalog System, Orders System, Marketing System etc.). Most of these subsystems can be widely extended to fit business requirements, like having different product types, customized order schema and custom data entities. Also the product comes with out of the box client tools that that supports working on these different entities in Commerce Server, like creating products in Catalog Manager, viewing orders in Customer and Orders Manager and setting up campaigns in the Marketing Manager.

This is great stuff. If we were to implement the functionality that Commerce Server offers by ourselves most of us properly would not have gotten further than implementing a basic catalog system that could handle multiple products in multiple categories for the same budget than if we had bought a standard license to Commerce Server.

You can build a complete webshop by just utilizing the various components and subsystems in Commerce Server. Microsoft did it in Commerce Server 2007 with the ASP.NET StarterSite. In Commerce Server 2009 they did it again with the StoreFront/Contemporary Site in SharePoint/MOSS.

The problem is that in real world projects the business requirements to presenting data are often quite different that what can be expressed in e.g. a Catalog System. Pricing is one example. In Commerce Server a price is expressed as a single property on a product/variant in the Catalog System. If you add this product to your basket, the price will automatically be set on the Line Item. If you are building an international e-commerce site you properly need to express your price in different currencies. To do that, you would have to create different virtual catalogs, and your application logic would have to adapted to that making your design more complex and harder to maintain. Another scenario is when your price is dependent on the customer buying it – again you would need to have customer specific catalogs and maintain the prices there. In most of the B2B sites that I’ve seen prices are not even “static data” stored in some database, instead they are requested at runtime from the back-end ERP system.

To address the problem with custom pricing we need to tweak Commerce Server to override the price retrieved from the Product in the Catalog System with a price coming from ERP. This can be accomplished by creating a simple pipeline component.

As mentioned earlier there are many ways to extend Commerce Server to fit your business needs. The Profiles System allow you to create new data entities if you need to store custom data, like users, logins, roles, accounts, gift certificates, <insert data here> and the API on top supports all CRUD operations and returns .NET objects (like what you would expect from any ORM – object relational mapping software . While this might sound great: that you can extend and store any data that you would like in the Profiles System, it comes with a price. The price is bad performance (using non-parameterized OLEDB-queries, really bad support for one-to-many/many-to-many relationsships and cumbersome and ineffective extensibility/development model. And why would you even consider using anything remotely cumbersome when we have ORMs frameworks like Castle Active Record, NHibernate and Entity Framework to just name a few that very easily allows you to map data from a database and into your domain?

Besides using a ORM instead of the Profiles System we use and apply a number of different technologies and principles in our applications, like:

  • Model-View-Presenter/Model-View-Controller
    • Clean separation of UI and model
  • Dependency Injection pattern
    • With a Inversion of Control container in place
  • Unit-testing
    • With Mocking framework in place
  • Aspect Oriented Programming – DynamicProxy
    • Caching
    • Logging
  • Domain Events

I hope to see some comments on this blog-post as application design usually is very subjective :)

Posted on Wednesday, 12 January 2011 17:17:00 (Romance Standard Time, UTC+01:00)
# | Comments [0]
 Friday, 22 October 2010

For some reason my Feedburner feed is not showing my latest post on implementing custom shipping methods in Commerce Server, so I’m just publishing this post to make sure that people subscribing to my feed gets the latest information.

If you did get the latest post in your RSS-reader please ignore this post, and I’m sorry about the inconvenience.

Have a great weekend to all of you :-)

Posted on Friday, 22 October 2010 07:08:00 (Romance Standard Time, UTC+01:00)
# | Comments [0]
 Friday, 15 October 2010

A couple of years back I wrote a blog-post on How to extend Commerce Server Payment Methods and Shipment Methods which described how you could use the Profiles System in Commerce Server to extend properties of Shipment- and Payment Methods and with very little effort customize the UI of the Customer And Orders Manager to allow easy management of these new properties. This technique is definitely still valid, but I’ve would like to explain another approach that overrides the overall of how Commerce Server handles Shipping Methods to allow custom business logic, custom pricing etc.

Also this post serves as a (late, but promised) follow up answer to a question asked on the Commerce Server Forum on MSDN ( on how the best way to implement custom business logic for handling shipping in Commerce Server.

In this blog post I’ll show you can implement custom shipping methods in a Commerce Server 2007/2009 project. In order to better explain the “Why”-part, I’ll start by briefly explaining how Commerce Server works out of the box in regards to applying shipping on a basket. Last but not least I’ll explain the “How”-part, so you can implement this in your own project.

Shipping out of the box

Shipping Methods are configured in Customer And Orders Manager. Here you specify name and description for the different languages that you support, the cost of a given shipping method based on weight, subtotal or quantity and a Shipping Cost Calculator, which is a pipeline component that automatically gets executed from the Total-pipeline. As for prices you cannot specify multiple prices in multiple currencies – so you are stuck with only prices in one currency. Also important to mention is that there is no supported way to extend this schema, e.g. adding new properties, as you would also read about in my previously mentioned post.

This screenshot below shows the link between a Shipping Method created in Customer and Orders Manager and how and where this is persisted in the database.


In short Shipping Methods are added to a Basket by specifying a ShippingMethodId (Guid) to your Line Items as shown below for both 2007 and 2009 version of Commerce Server.

In Commerce Server 2007:

   1:  var lineItem = new LineItem("TestCatalog", "ProductID", null /* VariantID */, 1 /* Quantity */)
   2:  {
   3:      ShippingMethodId = new Guid("MethodId on Shipping Method")
   4:  };

In Commerce Server 2009:

   1:  lineItem.SetPropertyValue("CatalogName", "TestCatalog");
   2:  lineItem.SetPropertyValue("ProductId", "ProductID");
   3:  lineItem.SetPropertyValue("Quantity", 1 /* Quantity */);
   4:  lineItem.SetPropertyValue("ShippingMethodId", "GroupId on Shipping Method");


After adding a Line Item to the Basket you run the Total-pipeline in which there is a number of pipeline component responsible of requesting the database and actually create one or more instances of the Microsoft.CommerceServer.Runtime.Orders.Shipment class based on the ShippingMethodId on the Line Items. This new instance then gets added to the Shipments-collection on the OrderForm. Basically the same as I’m doing manually in the code example below:

   1:  var orderForm = basket.OrderForms[0];
   2:  var shipment = new Shipment();
   3:  orderForm.Shipments.Add(shipment);


The bottom-line is that you are not in control of the actual creation process of the Shipment instance, as this is taken care of in the Total-pipeline by the two components marked in red in the screenshot below.


If you do try to add it manually the Total-pipeline will throw an error because you did not specify a ShippingMethodId on your Line Items.

Why use custom shipping?

On most of the e-commerce projects I’ve been working on, pricing is a very complex thing – it heavily depends on a lot of context information like customer, country, currency, content of the basket, suppliers and sometimes more making it impossible to setup in Customer and Orders Manager. Sure you can try to create your own Shipping Cost Calculator pipeline component, but that requires you to do work and business logic in the Pipeline infrastructure of Commerce Server, which is cumbersome, because  you are dealing with COM, magic strings, and weak-types like the IDictionary and ISimpleList. Also it is difficult to interact with your existing infrastructure like services, repositories etc from your pipelines, and it is hard to automate tests for this. Pipeline components are just not the right place to put your business logic in my opinion.

So what we would really like is to be able to create the Shipping Methods from within our domain ourselves, being able to specify the price in that proces, e.g. having the price coming from a calculation made in the backend/ERP-system. And I’ll show you how to accomplish that in the next section.

How to implement custom shipping

First off we need to modify the Total-pipeline. The two pipeline components marked in red in the previous image needs to be removed thus making the Total-pipeline look like in the screenshot below:


The next steps really depend on whether you are working on a CS 2007 or CS 2009 project. I’ll start by explaining the CS 2007 approach:

CS 2007

   1:  public void CreateBasketInCs2007()
   2:  {
   3:      // Create a new basket
   4:      var basket =
   5:          CommerceContext.Current.OrderSystem.GetBasket(Guid.NewGuid(), "Default2007");
   7:      // Create a new OrderForm
   8:      if (basket.OrderForms.Count == 0)
   9:          basket.OrderForms.Add(new OrderForm());
  11:      // Add some line items
  12:      foreach (string productId in new[] { "2-1", "2-2" })
  13:      {
  14:          basket.OrderForms[0].LineItems.Add(new LineItem("TestCatalog", productId, null, 1));
  15:      }
  17:      // Add an address
  18:      var address = new OrderAddress("Default", String.Empty) { FirstName = "First Name" };
  19:      basket.Addresses.Add(address);
  21:      // Add your custom shipment with custom price (300)
  22:      basket.OrderForms[0].Shipments.Add(
  23:          new Shipment 
  24:      { 
  25:          ShippingMethodName = "MyShippingMethod",
  26:          ShipmentTrackingNumber = "My Tracking",
  27:          ShipmentTotal = 300m, 
  28:          ShippingAddressId = address.OrderAddressId 
  29:      });
  31:      // Associate Shipping and Line Items
  32:      basket.OrderForms[0].Shipments[0].LineItemIndexes.Add(0);
  33:      basket.OrderForms[0].Shipments[0].LineItemIndexes.Add(1);
  35:      // Execute the pipelines
  36:      using (var pipeline = new PipelineInfo("Basket", OrderPipelineType.Basket))
  37:      {
  38:          _writer.WriteLine(basket.RunPipeline(pipeline));
  39:      }
  41:      using (var pipeline = new PipelineInfo("Total", OrderPipelineType.Total))
  42:      {
  43:          _writer.WriteLine(basket.RunPipeline(pipeline));
  44:      }
  46:      basket.Save();
  47:  }

The example above creates a new Basket, adds some line items, and adds a Shipment instance with a custom price. The point is that this custom price could come from anywhere: web-service call to ERP, custom database, custom pricing calculation logic, etc. Here is the result of the basket shown in Customer and Orders Manager:

BasketCS2007-1 BasketCS2007-2

To illustrate the same in CS 2009 we need to do a bit more work as shown below.

CS 2009

Lets start by looking at the actual query code:

   1:  public void CreateBasketInCs2009()
   2:  {
   3:      var basketUpdateQuery =
   4:          new CommerceUpdate<CommerceEntity, CommerceModelSearch<CommerceEntity>, CommerceBasketUpdateOptionsBuilder>("Basket");
   6:      basketUpdateQuery.SearchCriteria.Model.SetPropertyValue("UserId", Guid.NewGuid().ToString());
   7:      basketUpdateQuery.SearchCriteria.Model.SetPropertyValue("BasketType", 0); // Basket
   8:      basketUpdateQuery.SearchCriteria.Model.SetPropertyValue("Name", "Default2009");
  10:      // Must be set to "ReadyForCheckout" in order to have Total pipeline executed
  11:      basketUpdateQuery.Model.SetPropertyValue("Status", "ReadyForCheckout");
  13:      basketUpdateQuery.Model.SetPropertyValue("BasketType", 0); // 0 = Basket
  15:      // Decides whether to run pipelines or not
  16:      basketUpdateQuery.UpdateOptions.RefreshBasket = true;
  18:      // Add an address
  19:      var address = new CommerceEntity("Address")
  20:      {
  21:          Id = Guid.NewGuid().ToString("B")
  22:      };
  24:      address.SetPropertyValue("AddressName", "Home");
  25:      address.SetPropertyValue("FirstName", "First Name");
  26:      address.SetPropertyValue("ProfileAddressId", String.Empty);
  28:      basketUpdateQuery.RelatedOperations.Add(
  29:          new CommerceCreateRelatedItem<CommerceEntity>("Addresses", "Address") { Model = address });
  31:      // Add some line items
  32:      foreach (string productId in new[] { "2-1", "2-2" })
  33:      {
  34:          var lineItem = new CommerceEntity("LineItem");
  36:          lineItem.SetPropertyValue("ProductId", productId);
  37:          lineItem.SetPropertyValue("Quantity", 1);
  38:          lineItem.SetPropertyValue("CatalogName", "TestCatalog");
  39:          lineItem.SetPropertyValue("ShippingAddressId", address.Id);
  41:          // if using multiple shippings - use this to pair line item and shipping
  42:          lineItem.SetPropertyValue("ShippingMethodName", "MyShippingMethod");
  44:          basketUpdateQuery.RelatedOperations.Add(
  45:              new CommerceCreateRelatedItem<CommerceEntity>("LineItems", "LineItem") { Model = lineItem });
  46:      }
  48:      // Add a custom shipment
  49:      var shipment = new CommerceEntity("Shipment");
  50:      shipment.SetPropertyValue("Total", 300m); // custom price right here!
  51:      shipment.SetPropertyValue("ShippingAddressId", address.Id);
  52:      shipment.SetPropertyValue("TrackingNumber", "some-tracking-code");
  53:      shipment.SetPropertyValue("StatusCode", "some-status");
  54:      shipment.SetPropertyValue("ShippingMethodName", "MyShippingMethod");
  56:      basketUpdateQuery.RelatedOperations.Add(
  57:          new CommerceCreateRelatedItem<CommerceEntity>("Shipments") { Model = shipment });
  59:      var operationServiceAgent = new OperationServiceAgent();
  61:      var response =
  62:          operationServiceAgent
  63:              .ProcessRequest(Request.CreateCommerceRequestContext(), basketUpdateQuery.ToRequest())
  64:              .OperationResponses
  65:              .Single() as CommerceUpdateOperationResponse;
  67:      foreach (var entity in response.CommerceEntities ?? Enumerable.Empty<CommerceEntity>())
  68:          entity.Output(_writer);
  70:      _writer.WriteLine("Count = {0}", response.Count);
  71:  }

Executing this query with the standard Operation Sequence Components and Translators in the Multi Channel Foundation API will fail. The reason why is that there is logic in these classes that validates whether Line Items have a valid ShippingMethodId in order to execute the Total-pipeline, and also there is simply no translator to translate from a CS 2009 Shipment-CommerceEntity to a Shipment-type in CS 2007.

More work needs to be put in this effort, lets start with the Translator part. First off we need to extend the current LineItemTranslator to have the ShippingMethodName translated:

   1:  public class CustomShippingSupportingLineItemTranslator : LineItemTranslator
   2:  {
   3:      protected override bool TranslateToStronglyTypedCommerceServerProperty(LineItem commerceServerObject, string commerceServerPropertyName, object value)
   4:      {
   5:          bool result =
   6:              base.TranslateToStronglyTypedCommerceServerProperty(commerceServerObject, commerceServerPropertyName, value);
   8:          if (!result && 
   9:              value is String &&
  10:              String.Equals(commerceServerPropertyName, "ShippingMethodName", StringComparison.OrdinalIgnoreCase))
  11:          {
  12:              commerceServerObject.ShippingMethodName = value.ToString();
  13:              result = true;
  14:          }
  16:          return result;
  17:      }
  18:  }

And register this in the ChannelConfiguration.config:

   1:  <Translator
   2:    sourceModelName="LineItem"
   3:    destinationType="Microsoft.CommerceServer.Runtime.Orders.LineItem, Microsoft.CommerceServer.Runtime, Version=, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
   4:    type="Extensions.Providers.Translators.CustomShippingSupportingLineItemTranslator, Extensions, Version=, Culture=neutral, PublicKeyToken=4c1d6c3df403d290"/>


Next up is extending the existing ShipmentTranslator to support External Entity translation, like this:

   1:  public class ExternalEntitySupportingShipmentTranslator : ShipmentTranslator, IToExternalEntityTranslator
   2:  {
   3:      private readonly PropertyTranslator<Shipment> _propertyTranslator;
   5:      public ExternalEntitySupportingShipmentTranslator()
   6:      {
   7:          _propertyTranslator = 
   8:              new PropertyTranslator<Shipment>(
   9:                  null, 
  10:                  null,
  11:                  TranslateToStronglyTypedCommerceServerProperty,
  12:                  TranslateToWeaklyTypedCommerceServerProperty);
  13:      }
  15:      public void Translate(CommerceEntity sourceCommerceEntity, object destination)
  16:      {
  17:          if (sourceCommerceEntity == null) throw new ArgumentNullException("sourceCommerceEntity");
  18:          if (destination == null) throw new ArgumentNullException("destination");
  20:          _propertyTranslator.TranslateToCommerceServer(sourceCommerceEntity, destination as Shipment, null);
  21:      }
  23:      protected virtual bool TranslateToWeaklyTypedCommerceServerProperty(Shipment commerceServerObject, string commerceServerPropertyName, object value)
  24:      {
  25:          commerceServerObject[commerceServerPropertyName] = value;
  26:          return true;
  27:      }
  29:      protected virtual bool TranslateToStronglyTypedCommerceServerProperty(Shipment commerceServerObject, string commerceServerPropertyName, object value)
  30:      {
  31:          switch (commerceServerPropertyName)
  32:          {
  33:              case "ShipmentTotal":
  34:                  if (value != null)
  35:                      commerceServerObject.ShipmentTotal = Convert.ToDecimal(value, CultureInfo.InvariantCulture);
  36:                  break;
  38:              case "ShippingAddressId":
  39:                  commerceServerObject.ShippingAddressId = value as string;
  40:                  break;
  42:              case "ShippingMethodName":
  43:                  commerceServerObject.ShippingMethodName = value as string;
  44:                  break;
  46:              case "ShippingMethodId":
  47:                  Guid methodId = (value is string) ? new Guid(value as string) : Guid.Empty;
  48:                  commerceServerObject.ShippingMethodId = methodId;
  49:                  break;
  51:              case "Status":
  52:                  commerceServerObject.Status = value as string;
  53:                  break;
  55:              case "ShipmentTrackingNumber":
  56:                  commerceServerObject.ShipmentTrackingNumber = value as string;
  57:                  break;
  59:              default:
  60:                  return false;
  61:          }
  63:          return true;
  64:      }
  65:  }

And again register this in the ChannelConfiguration.config:

   1:  <Translator
   2:    sourceModelName="Shipment"
   3:    destinationType="Microsoft.CommerceServer.Runtime.Orders.Shipment, Microsoft.CommerceServer.Runtime, Version=, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
   4:    type="Extensions.Providers.Translators.ExternalEntitySupportingShipmentTranslator, Extensions, Version=, Culture=neutral, PublicKeyToken=4c1d6c3df403d290" />


This covers the translation part, so we can map/translate from a CS 2009 entity to a CS 2007 entity.

The next part is where it gets a little tricky. We need to create a custom Operation Sequence Component, that will take care of the actual creation of the CS 2007 Shipment-instance, and add this to the Shipments collection of the Basket that we are executing a query against.

To create a custom Operation Sequence Component, we create the following class:

   1:  public class CustomShipmentsProcessor : BasketRelatedItemOperationSequenceComponent
   2:  {
   3:      protected override string RelationshipName
   4:      {
   5:          get { return "Shipments"; }
   6:      }
   8:      protected override void CreateRelatedItem(CommerceCreateRelatedItem createRelatedItemOperation)
   9:      {
  10:          CommerceEntity shipmentEntity = createRelatedItemOperation.Model;
  12:          var newShipment =
  13:              CommerceServerClassFactory.CreateInstance<Shipment>(
  14:                  shipmentEntity.ModelName, CommerceServerArea.Orders);
  16:          shipmentEntity.Id = newShipment.ShipmentId.ToString("B");
  18:          Translator.ToExternalEntity(shipmentEntity, newShipment);
  20:          OrderForm orderForm = CachedOrderGroup.GetDefaultOrderForm();
  22:          UpdateShippingLineItemsAssociation(newShipment, orderForm);
  24:          orderForm.Shipments.Add(newShipment);
  25:      }
  27:      public override void ExecuteUpdate(CommerceUpdateOperation updateOperation, Microsoft.Commerce.Broker.OperationCacheDictionary operationCache, CommerceUpdateOperationResponse response)
  28:      {
  29:          base.ExecuteUpdate(updateOperation, operationCache, response);
  31:          EnsureLineItemsHasShippingMethod();
  32:      }
  34:      private void EnsureLineItemsHasShippingMethod()
  35:      {
  36:          OrderForm orderForm = CachedOrderGroup.GetDefaultOrderForm();
  38:          foreach (LineItem lineItem in
  39:              orderForm.LineItems
  40:                  .OfType<LineItem>()
  41:                  .Where(x => x.ShippingMethodId == Guid.Empty))
  42:          {
  43:              lineItem.ShippingMethodId = Guid.NewGuid();
  44:          }
  45:      }
  47:      protected override void DeleteRelatedItem(CommerceDeleteRelatedItem deleteRelatedItemOperation)
  48:      {
  49:          string shipmentId = GetSearchModelId(deleteRelatedItemOperation, "Shipment", true);
  51:          if (!String.IsNullOrEmpty(shipmentId))
  52:          {
  53:              Shipment deletingShipment = GetShipmentFromCachedOrderGroup(shipmentId);
  54:              CachedOrderGroup.GetDefaultOrderForm().Shipments.Remove(deletingShipment);
  55:          }
  56:          else
  57:              CachedOrderGroup.GetDefaultOrderForm().Shipments.Clear();
  58:      }
  60:      protected override void UpdateRelatedItem(CommerceUpdateRelatedItem updateRelatedItemOperation)
  61:      {
  62:          OrderForm orderForm = CachedOrderGroup.GetDefaultOrderForm();
  64:          CommerceEntity model = updateRelatedItemOperation.GetModel("Shipment");
  65:          string shipmentId = GetSearchModelId(updateRelatedItemOperation, "Shipment", true);
  67:          if (!String.IsNullOrEmpty(shipmentId))
  68:          {
  69:              Shipment updatingShipment = GetShipmentFromCachedOrderGroup(shipmentId);
  70:              Translator.ToExternalEntity(model, updatingShipment);
  71:              UpdateShippingLineItemsAssociation(updatingShipment, orderForm);
  72:          }
  73:          else
  74:          {
  75:              foreach (Shipment shipment in orderForm.Shipments)
  76:                  Translator.ToExternalEntity(model, shipment);
  77:          }
  78:      }
  80:      protected virtual Shipment GetShipmentFromCachedOrderGroup(string shipmentId)
  81:      {
  82:          OrderForm orderForm = CachedOrderGroup.GetDefaultOrderForm();
  84:          int index = orderForm.Shipments.IndexOf(new Guid(shipmentId));
  86:          if (index < 0)
  87:              throw ItemDoesNotExist("Shipment", shipmentId);
  89:          return orderForm.Shipments[index];
  90:      }
  92:      protected virtual void UpdateShippingLineItemsAssociation(Shipment shipment, OrderForm orderForm)
  93:      {
  94:          foreach (LineItem lineItem in
  95:              orderForm.LineItems
  96:                  .OfType<LineItem>()
  97:                  .Where(x => 
  98:                      String.IsNullOrEmpty(x.ShippingMethodName) ||
  99:                      String.Equals(x.ShippingMethodName, shipment.ShippingMethodName, StringComparison.OrdinalIgnoreCase)))
 100:          {
 101:              lineItem.ShippingMethodId = shipment.ShipmentId;
 102:              shipment.LineItemIndexes.Add(lineItem.Index);
 103:          }
 104:      }
 106:      protected virtual FaultException<ItemDoesNotExistFault> ItemDoesNotExist(string entityName, string entityId)
 107:      {
 108:          string message = ProviderResources.ExceptionMessages.GetMessage("ItemDoesNotExist", new object[0]);
 110:          var detail = new ItemDoesNotExistFault(message);
 111:          if (!String.IsNullOrEmpty(entityName))
 112:          {
 113:              detail.CommerceEntityName = entityName;
 114:          }
 115:          if (!String.IsNullOrEmpty(entityId))
 116:          {
 117:              detail.CommerceEntityId = entityId;
 118:          }
 120:          return new FaultException<ItemDoesNotExistFault>(detail, message);
 121:      }
 122:  }


This Operation Sequence Component will make sure that all the prerequisites required for the Total-pipeline component to be executed by the standard OrderPipelineProcessor are met, and it will make sure that Line Items and the newly created Shipments are associated to each other. This is particular necessary if you need multiple shipment – having different line items using different shipment methods.

To register the new Operation Sequence Component we again have to do some work in the ChannelConfiguration.config:

   1:  <Component name="Requested Promo Codes processor" type="Microsoft.Commerce.Prov…">
   2:    <Configuration
   3:      customElementName="RequestedPromoCodesProcessorConfiguration"
   4:      customElementType="Microsoft.Commerce.Providers.Components.RequestedPromoCodesPr…">
   5:      <RequestedPromoCodesProcessorConfiguration promoUserIdentityProperty="Email"/>
   6:    </Configuration>
   7:  </Component>
   9:  <Component name="Custom Shipments processor" type="Extensions.Providers.Components.CustomShipmentsProcessor, Extensions, Version=, Culture=neutral, PublicKeyToken=4c1d6c3df403d290" />
  11:  <Component name="Payments processor" type="Microsoft.Commerce.Providers.Components.PaymentsProcessor, Microsoft.Commerce.Providers, Version=, Culture=neutral,PublicKeyToken=31bf3856ad364e35" />
  12:  <Component name="Targeting Profiles" type="Microsoft.Commerce.Providers.Components.TargetingProfilesProcessor, Microsoft.Commerce.Providers, Version=, Culture=neutral,PublicKeyToken=31bf3856ad364e35" />
  13:  <Component name="Order Pipelines Processor" type="Microsoft.Commerce.Providers.Components.OrderPipelinesProcessor, Microsoft.Commerce.Providers, Version=, Culture=neutral,PublicKeyToken=31bf3856ad364e35">

The component needs to be added before the Order Pipelines Processor.

Thats it. Execute the query, and the result will look like the following in Customer and Orders Manager:


BasketCS2009-1 BasketCS2009-2


I hope this was useful. If you have any questions or want me to elaborate more on this subject please don't hesitate to contact me in any way.

Posted on Friday, 15 October 2010 09:36:37 (Romance Standard Time, UTC+01:00)
# | Comments [0]
 Tuesday, 05 October 2010

Interested in taking your Commerce Server skills to the next level?

Do you want to know how you can apply custom pricing, custom shipping and custom payment?

These are just some of the subjects that we will be covering in our upcoming Commerce Server Developer Week course held in New York and Europe this November.

November 1st – 5th 2010
Hilton Times Square
234 West, 42nd Street
New York

November 22nd – 26th 2010
Quality Airport Hotel Dan
Kastruplundgade 15
2770 Kastrup, Copenhagen

Read more and sign-up on where you will also be able to read reviews from previous courses held.

Hope to see you there :-)

Posted on Tuesday, 05 October 2010 08:07:12 (Romance Standard Time, UTC+01:00)
# | Comments [0]
 Monday, 06 September 2010

There is a built-in restriction in the new Commerce Server 2009 Foundation API (MCCF - Multi Channel Commerce Foundation) to only expect one OrderForm instance on the aggregate root, being the OrderGroup. An OrderGroup is either a Basket or PurchaseOrder (or OrderTemplate if you are using that).

Below is a simplified diagram of the key classes in the Object Model for the Orders System. Please notice that we have the OrderForms collection on the OrderGroup class, where each instance of an OrderForm can be retrieved either by index or by a name (string).

Orders API - class hierachy

This allows us to create multiple OrderForms, each with their own data like Line Items, Payments, Shipments etc, and share other data like Addresses, TrackingNumber and a few more properties on the OrderGroup class.

In all projects I’ve been working on we don’t actually use the ability to have multiple OrderForms. We only have one single OrderForm at all times.

The Commerce Server 2007 code would then look something like:

   1:  Basket basket = OrderContext.Current.GetBasket(userId, basketName);
   3:  if (basket.OrderForms.Count == 0)
   4:      basket.OrderForms.Add(new OrderForm());


This retrieves a named Basket from the Orders System and if the basket is new, it will create an instance of OrderForm and add this to the OrderForms collection.

   1:  basket.OrderForms[0].LineItems.Add(
   2:      new LineItem("ProductCatalog", "ProductID", "VariantID", 1m));


At some point we would then like to add a Line Item to our basket as illustrated above where we hardcode the index-value to 0 to retrieve the one and only OrderForm we have.

In Commerce Server 2009 though, we never deal with the actual types, instead we work with the ICommerceEntity type. The Commerce Server 2009 code for retrieving a basket would be something like:

   1:  var query = new CommerceQuery<CommerceEntity>("Basket");
   3:  query.SearchCriteria.Model.SetPropertyValue("UserId", userId);
   4:  query.SearchCriteria.Model.SetPropertyValue("BasketType", 0); // 0 = Basket, 1 = Purchase Order
   5:  query.SearchCriteria.Model.SetPropertyValue("Name", basketName);
   7:  var operationServiceAgent = new OperationServiceAgent();
   9:  var response =
  10:      operationServiceAgent
  11:          .ProcessRequest(
  12:              new CommerceRequestContext
  13:              {
  14:                  Channel = "DefaultChannel",
  15:                  UserId = Guid.NewGuid().ToString("b"),
  16:                  UserLocale = CultureInfo.CurrentCulture.ToString(),
  17:                  UserUILocale = CultureInfo.CurrentUICulture.ToString(),
  18:                  RequestId = Guid.NewGuid().ToString()
  19:              }, 
  20:              query.ToRequest())
  21:          .OperationResponses
  22:          .Single() as CommerceQueryOperationResponse;
  24:  var basketEntity = response.CommerceEntities.Single();


Effectively this will execute the CommerceQueryOperation_Basket Operation Sequence defined in ChannelConfiguration.config which in short will load the Basket from the Orders System and map this to the generic ICommerceEntity type.

In the process of retrieving and mapping a Basket to a ICommerceEntity the following extension method from Microsoft.Commerce.Providers.Utility.OrderGroupExtensions is being called on the OrderGroup:

   1:  public static void EnsureDefaultOrderForm(this OrderGroup orderGroup, string modelName)
   2:  {
   3:      ParameterChecker.CheckForNull(orderGroup, "orderGroup");
   4:      if (orderGroup.OrderForms["Default"] == null)
   5:      {
   6:          if (string.IsNullOrEmpty(modelName))
   7:          {
   8:              modelName = "Basket";
   9:          }
  10:          OrderForm orderForm = CommerceServerClassFactory.CreateInstance<OrderForm>(modelName, 2, new object[] { "Default" });
  11:          orderGroup.OrderForms.Add(orderForm);
  12:      }
  13:  }

This method is responsible of making sure that a single OrderForm with the name “Default” is always available in the OrderForms collection of the OrderGroup instance.

Also there is an extension method to always retrieve this single OrderForm called Microsoft.Commerce.Providers.Utility.OrderGroupExtensions.GetDefaultOrderForm.

   1:  public static OrderForm GetDefaultOrderForm(this OrderGroup orderGroup, string modelName)
   2:  {
   3:      ParameterChecker.CheckForNull(orderGroup, "orderGroup");
   4:      if (orderGroup.OrderForms["Default"] == null)
   5:      {
   6:          if (string.IsNullOrEmpty(modelName))
   7:          {
   8:              modelName = "Basket";
   9:          }
  10:          OrderForm orderForm = CommerceServerClassFactory.CreateInstance<OrderForm>(modelName, 2, new object[] { "Default" });
  11:          orderGroup.OrderForms.Add(orderForm);
  12:      }
  13:      return orderGroup.OrderForms["Default"];
  14:  }

This method is used throughout the many Operation Sequence Components in the Foundation API and if you are developing your own custom Operation Sequence Components that works on the Orders System this method is properly something you would like to use.

As mentioned earlier we’ve just been using one OrderForm in all our Commerce Server projects, so this new limitation is not a problem for us, but on one of my Commerce Server Training courses I had a guy telling me that they where actually using multiple OrderForms on a CS 2007 project, so this new limitation would be a problem for them if they were to upgrade or create a similar solution on a new CS 2009 project.

What if we need more than OrderForm on our project, what to do?

In short: rethink. A solution with multiple OrderForms in CS 2009 will be too difficult to implement because of the many dependencies in the standard Operation Sequence Components that expecting there to be only one – remember they are all calling the GetDefaultOrderForm() extension method. Consider using multiple OrderGroups instead.


If you are reading this and have come up with a better solution to having multiple OrderForms in CS 2009 or have any comments at all I would really appreciate hearing from you :-)

Posted on Monday, 06 September 2010 07:54:00 (Romance Standard Time, UTC+01:00)
# | Comments [0]
 Friday, 03 September 2010

Max Akbar’s Commerce Server 2007 training videos have yet again become available for purchase. More than 19 hours of material can now be found on

We are working on producing and releasing new training videos targeting Commerce Server 2009.

Posted on Friday, 03 September 2010 13:27:37 (Romance Standard Time, UTC+01:00)
# | Comments [0]
 Tuesday, 27 April 2010

In Commerce Server the Rank property is used as a sorting discriminator when retrieving categories, products and variants. This property is usually managed using Catalog Manager as shown in the screenshot below:


The Rank property could also be managed in your own application which will be the topic of a later blog-post. This blog-post will focus on how to get the actual Rank value when retrieving categories in Commerce Server.

How did we do this in in Commerce Server 2007?

Pretty easy! We just include “Rank” as an item in the PropertiesToReturnArray for child categories (line 12) when requesting a specific category and later retrieve the value from the property-bag of the Category instance (line 24) as shown in the example below:

   1:  public void RequestUsing2007(string catalogName, string categoryName)
   2:  {
   3:      var catalogSystem = CommerceContext.Current.CatalogSystem;
   5:      var configuration = new CategoryConfiguration
   6:      {
   7:          LoadChildCategories = true,
   8:          ChildCategories =
   9:          {
  10:              SearchOptions =
  11:              {
  12:                  PropertiesToReturnArray = new[] { "DisplayName", "Rank" }
  13:              }
  14:          }
  15:      };
  17:      var category =
  18:          catalogSystem.GetCategory(catalogName, categoryName, "da-DK", configuration);
  20:      foreach (var childCategory in category.ChildCategories)
  21:      {
  22:          _writer.WriteLine("Id={0}", childCategory.Name);
  23:          _writer.WriteLine("DisplayName={0}", childCategory.DisplayName);
  24:          _writer.WriteLine("Rank={0}", childCategory["rank"]);
  26:          _writer.WriteLine();
  27:      }
  28:  }

And how can I do it in Commerce Server 2009?

In Commerce Server 2009 with the new Foundation API the overall way of retrieving entities in Commerce Server has changed a lot. In the example below I’ve added “Rank” as part of the properties to retrieve when requesting child categories for a specific category (line 10):

   1:  public void RequestUsing2009(string catalogName, string categoryName)
   2:  {
   3:      var categoryQuery = new CommerceQuery<CommerceEntity>("Category");
   4:      categoryQuery.Model.Properties.Add(new[] { "Id", "DisplayName" });
   6:      categoryQuery.SearchCriteria.Model.SetPropertyValue("CatalogId", catalogName);
   7:      categoryQuery.SearchCriteria.Model.SetPropertyValue("Id", categoryName);
   9:      var childCategoryQuery = new CommerceQueryRelatedItem<CommerceEntity>("ChildCategories", "Category");
  10:      childCategoryQuery.Model.Properties.Add(new[] { "Id", "DisplayName", "Rank" });
  11:      categoryQuery.RelatedOperations.Add(childCategoryQuery);
  13:      var operationServiceAgent = new OperationServiceAgent();
  15:      var response = 
  16:          operationServiceAgent.ProcessRequest(
  17:              new CommerceRequestContext
  18:              {
  19:                  Channel = "MyChannel",
  20:                  UserLocale = "da-DK",
  21:                  UserUILocale = "da-DK",
  22:                  RequestId = Guid.NewGuid().ToString()
  23:              }, 
  24:              categoryQuery.ToRequest());
  26:      var operationResponse = (CommerceQueryOperationResponse)response.OperationResponses.Single();
  28:      var entity = operationResponse.CommerceEntities.SingleOrDefault();
  30:      foreach (var childCategory in entity.Properties["ChildCategories"] as CommerceRelationshipList)
  31:      {
  32:          foreach (var categoryProperty in childCategory.Target.Properties)
  33:          {
  34:              _writer.WriteLine
  35:                  ("{0}={1} ({2})", 
  36:                  categoryProperty.Key, 
  37:                  categoryProperty.Value, 
  38:                  categoryProperty.Value.GetType().Name);
  39:          }
  41:          _writer.WriteLine();
  42:      }
  43:  }


One of the Operation Sequence components being executed in this operation is responsible for creating the CategoryConfiguration instance, that we manually created in the Commerce Server 2007 example (CS 2007 example line 5). This Operation Sequence component is named “CategoryConfiguration_Prepare”.

The actual logic for building the CategoryConfiguration instance is implemented in a class named CatalogConfigurationBuilder. This class will look in the MetadataDefinitions.xml file to match up the properties requested by the developer (in our case “Id, DisplayName, Rank”) with the properties registered for the actual entity being requested (“Category”).

If Rank is not registred in the MetadataDefinitions.xml file, it will be ignored by the CatalogConfigurationBuilder (method doing this is called TranslatePropertyName) and will therefore not be part of the “PropertiesToReturnArray” that in the end decides which columns to retrieve from the catalog table in SQL Server.

To make sure that Rank is registred in MetadataDefinitions.xml verify it by follow these steps:

  1. Open MetadataDefinitions.xml
  2. Locate <CommerceEntity name=”Category”>
  3. Make sure that there is a <PropertyMapping> element for property=”Rank” (<PropertyMapping property=”Rank” csProperty=”rank” />)
  4. Make sure that there is a <Property> element for name=”Rank” (<Property name=”Rank” dataType=”String” />)

Step 3 explained:

   1:  <EntityMappings>
   2:    <EntityMapping
   3:          csType="Microsoft.CommerceServer.Catalog.Category"
   4:          csAssembly="Microsoft.CommerceServer.Catalog, Version=, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
   6:      <PropertyMappings>
   7:        <!-- 
   8:              The following mappings are required here to ensure that all
   9:              the product common properties are represented in the Microsoft Multi-Channel Commerce Foundation 
  10:              metadata. These are not returned by the Commerce Server
  11:              metadata automatically as the common properties.
  12:              -->
  13:        <PropertyMapping property="BaseCatalogName" csProperty="BaseCatalogName" />
  14:        <PropertyMapping property="Rank" csProperty="rank" />


Step 4 explained:

   1:  <Properties>
   2:    <!-- 
   3:      The following property definitions are required here to ensure that all
   4:      the product common properties are represented in the 
   5:      metadata. These are not returned by the Commerce Server
   6:      metadata automatically as the common properties.
   7:      -->
   8:    <Property name="BaseCatalogName"        dataType="String" >
   9:      <DisplayName value="Base Catalog Name"/>
  10:    </Property>
  11:    <Property name="Rank" dataType="String" />


That’s it! The response returned by the Foundation API should now have the actual Rank value returned for each child category entity:

Id=ThirdRankingCategory (String)
Rank=1 (Int32)
DisplayName=ThirdRankingCategory (String)

Id=AnotherRankingCategory (String)
Rank=2 (Int32)
DisplayName=AnotherRankingCategory (String)

Id=SomeRankingCategory (String)
Rank=3 (Int32)
DisplayName=SomeRankingCategory (String)
Posted on Tuesday, 27 April 2010 12:00:00 (Romance Standard Time, UTC+01:00)
# | Comments [0]