Passing in a Weakly Typed Property to a Pipeline

on Thursday, September 17, 2009

Simply adding a property to the model's property bag is not enough if you wish to pass on some information down to the pipelines. Without having to create a full fledged strongly typed property, here is how you can pass in a weakly typed property to a pipeline. For this example, we will try and pass a OrderNumberPrefix property to the pipeline.

Changes to MetadataDefinitions.xml

  1. Open the MetadataDefinitions.xml and look for the following tag:
    <CommerceEntity name="Basket">
  2. Within this tag, locate the <PropertyMappings> tag.
  3. Add a new property inside this tag as follows:
    <PropertyMapping property="OrderNumberPrefix" csProperty="OrderNumberPrefix"/>
  4. Scroll further down within the same CommerceEntity tag for the basket, until you find the <Properties> tags. You will see all the properties listed here. Add the following tag within this tag:
    <Property name="OrderNumberPrefix" dataType="String">
    <DisplayName value="OrderNumberPrefix"/>
    </Property>

Changes to Order.cs & PipelineConstants.cs

  1. Add the following public property to the PipelineConstants.cspublic const string OrderNumberPrefixField = "OrderNumberPrefix";
  2. Open the Order.cs business entity class, and add a new public property called OrderNumberPrefix (just like the existing OrderNumber property). It will look something like this:
    public string OrderNumberPrefix
    {
    get { return GetValue(PipelineConstants.OrderNumberPrefixField); }
    set { this[PipelineConstants.OrderNumberPrefixField] = value; }
    }

This just makes it easier for you to access the property in the pipeline.

Passing in the weakly typed property to the pipeline

The last method that gets called when submitting an order is "AddBasketOrderSubmitRequest" inside the ShoppingController.cs. Add the following line inside this method:

update.Model.Properties["OrderNumberPrefix"] = SiteContext.Current.OrderNumberPrefix;

assuming your OrderNumberPrefix is coming from the site context. In theory, you could be retreiving it from wherever you like.

Retreiving the weakly typed property in the pipeline

Within the execute method of your pipeline processor, after you populate the order object using the pdispOrder dictionary, you can simple access the new property as follows:

var order = new Order((IDictionary)pdispOrder);
order.OrderNumberPrefix

Profile MetaDataException

on Friday, July 17, 2009

If you get an error of the following type:


An exception of type 'Microsoft.Commerce.Providers.Metadata.MetadataException'
occurred and was
caught.
---------------------------------------------------------------------------
07/17/2009
10:46:57
Type : Microsoft.Commerce.Providers.Metadata.MetadataException,
Microsoft.Commerce.Providers, Version=1.0.0.0, Culture=neutral,
PublicKeyToken=31bf3856ad364e35
Message : Commerce Server class
'Microsoft.CommerceServer.Runtime.Profiles.Profile' or its definition 'Something'
is not found in the Commerce Server metadata.
Source :
Microsoft.Commerce.Providers
Help link :
Data :
System.Collections.ListDictionaryInternal
TargetSite :
Microsoft.Commerce.Providers.Metadata.CommerceServerEntity
GetCommerceServerEntity(System.String, System.String)
Stack Trace : at
Microsoft.Commerce.Providers.Metadata.MergedMetadata.GetCommerceServerEntity(String
commerceClassName, String commerceDefinitionName)
at
Microsoft.Commerce.Providers.Metadata.MergedMetadata.CreateMergedMetadata(CommerceEntityDelta
repositoryСommerceEntityDelta, EntityMappingDelta repositoryEntityMapping)
at
Microsoft.Commerce.Providers.Metadata.MergedMetadata.MergeMetadata(InheritableCollection`1
repositoryMetadata, MetadataCollection`1 commerceServerMetadata)
at
Microsoft.Commerce.Providers.Metadata.MergedMetadataLoader.GetMergedMetadata(String
siteChannelKey, OperationCacheDictionary operationCache)
at
Microsoft.Commerce.Providers.Metadata.MergedMetadataLoader.ExecuteQuery(CommerceQueryOperation
queryOperation, OperationCacheDictionary operationCache,
CommerceQueryOperationResponse response)
at
Microsoft.Commerce.Providers.Components.OperationSequenceComponent.Execute(CommerceOperation
operation, OperationCacheDictionary operationCache, CommerceOperationResponse
response)
at
Microsoft.Commerce.Broker.OperationSequence.ExecuteComponentTree(List`1
executionTreeList, CommerceOperation operation, OperationCacheDictionary
operationCache, CommerceOperationResponse response)
at
Microsoft.Commerce.Broker.OperationSequence.Execute(CommerceOperation
operation)
at Microsoft.Commerce.Broker.MessageHandler.ProcessMessage(String
messageHandlerName, CommerceOperation operation)
at
Microsoft.Commerce.Broker.OperationService.InternalProcessRequest(CommerceRequest
request)
at
Microsoft.Commerce.Providers.Utility.CommerceEntityMetadata.ExecuteMetadataQuery(MetadataCacheKey
cacheKey, String modelName)
at
Microsoft.Commerce.Application.Common.CachedFactory`2.GetOrCreate(TKey key,
CreateInstance`2 factory)
at
Microsoft.Commerce.Providers.Utility.CommerceEntityMetadata.Get(String
modelName, Nullable`1 commerceArea)
at
Microsoft.Commerce.Providers.Utility.ProfileMetadata.<>c__DisplayClass5.b__4(MetadataCacheKey
key)
at
Microsoft.Commerce.Application.Common.CachedFactory`2.GetOrCreate(TKey key,
CreateInstance`2 factory)
at
Microsoft.Commerce.Providers.Utility.ProfileMetadata.Get(String modelName)
at
Microsoft.Commerce.Providers.Components.ProfileOperationSequenceComponent.get_Metadata()
at
Microsoft.Commerce.Providers.Components.ProfileOperationSequenceComponent.AreSearchParametersValid(CommercePropertyCollection
properties)
at
Microsoft.Commerce.Providers.Components.ProfileOperationSequenceComponent.ValidateSearchCriteria(CommerceModelSearch
searchCriteria)
at
Microsoft.Commerce.Providers.Components.ProfileOperationSequenceComponent.GetMatches(CommerceModelSearch
searchCriteria, Nullable`1& totalItemCount, Boolean throwIfNotFound)
at
Microsoft.Commerce.Providers.Components.ProfileLoaderBase.ExecuteQuery(CommerceQueryOperation
queryOperation, OperationCacheDictionary operationCache,
CommerceQueryOperationResponse response)
at
Microsoft.Commerce.Providers.Components.OperationSequenceComponent.Execute(CommerceOperation
operation, OperationCacheDictionary operationCache, CommerceOperationResponse
response)
at
Microsoft.Commerce.Providers.Components.ProfileOperationSequenceComponent.Execute(CommerceOperation
operation, OperationCacheDictionary operationCache, CommerceOperationResponse
response)
at
Microsoft.Commerce.Broker.OperationSequence.ExecuteComponentTree(List`1
executionTreeList, CommerceOperation operation, OperationCacheDictionary
operationCache, CommerceOperationResponse response)
at
Microsoft.Commerce.Broker.OperationSequence.Execute(CommerceOperation
operation)
at Microsoft.Commerce.Broker.MessageHandler.ProcessMessage(String
messageHandlerName, CommerceOperation operation)
at
Microsoft.Commerce.Broker.OperationService.InternalProcessRequest(CommerceRequest
request)
at
Microsoft.Commerce.Broker.OperationService.ProcessRequest(CommerceRequest
request)
A couple of things you may want to check are:

1. Make sure the entity definition inside MetaDataDefinitions.xml is correct.
3. Verify the entries into the Channel Config.
2. If you have extended the Profile entity make sure that the changes you have made to the Profile Schema don't have any name mismatches. So open up Commerce Server Manager and make sure that the Profile Definition and the Data Object is named appropriately.

Creating and Accessing Site Terms through Mojave API

on Thursday, January 1, 2009

The purpose of this entry is to demonstrate how to access site terms through Mojave API.

What is a Site Term?

A site term is a set of valid values for information that the user provides. For example, if you added a custom property named "Gender" to the profile named "User," you could define a site term to represent the values "male" and "female," which would provide the valid values for the "Gender" property [1].

How do you Create a Site Term?

There are several ways that you can create the site terms including direct user interaction with the Microsoft Commerce Server Customer and Orders management application. To do this, after opening the Customer And Orders management application,

  • Click on "Site Terms" in the Views pane on the left hand side.
  • Click "Create New Site Term" in the Tasks pane.
  • Fill in the information in the window that pops up to create the new site terms. In the following screenshot we are creating a site term called Security Questions which will be used to hold the security questions that will be presented to the user upon account creation.

  • Click "Save and Close" and you will have created a new site term in Commerce Server 2007.
If you wish, you could also create site terms through code. The purpose of this entry is to show you how you can bubble up existing site terms in your code through Mojave, so we will leave creating site terms through code for another time.

Accessing Site Terms through Mojave API

So now that you have a site term, the next thing you want to do with it is to have access to it in your code. So you can actually reach into the Commerce Server boundary, extract this particular site term and show it in your UI, e.g. extract the security questions from the site terms and display it on your user registration page.

If you are working with the default website that ships with the current Mojave download (CTP4, CTP5), one of the first things you need to do is to add a enumeration entry for your newly created site term. Inside the Common/DateItems folder you will find enums.cs which contains the definition for an enum called SiteTermName. This is simply an enumeration of the site terms that you have available inside Commerce Server. You can add your newly created site term to the bottom of this enum, and make sure that you spell it correctly as you did in the Commerce Server Customers and Orders management application.


SecurityQuestions = 7


The next thing you need to identify is that inside the ProfileController class, there is a method called GetSiteTerm, which takes an SiteTermName enum as a parameter, and returns a CommerceRelationshipList of the specific site term. By inspecting the code closely, you will see that this is done by creating a Commerce Query on the SiteTerm model, and passing in the Site Term Name as the model ID. This returns the entire contents of the site terms as a list.


public static CommerceRelationshipList GetSiteTerm(SiteTermName siteTermName)
{
CommerceRelationshipList siteTerms = new CommerceRelationshipList();
CommerceQueryRelatedItem elementQuery = new CommerceQueryRelatedItem(SiteTerm.RelationshipName.Elements);
elementQuery.Model.Properties.Add(SiteTermElement.PropertyName.Id);
elementQuery.Model.Properties.Add(SiteTermElement.PropertyName.DisplayName);
CommerceQuery query = new CommerceQuery();
query.SearchCriteria.Model.Id = siteTermName.ToString();
query.Model.Properties.Add(SiteTerm.PropertyName.Id);
query.RelatedOperations.Add(elementQuery);
CommerceResponse response = SiteContext.ProcessRequest(query.ToRequest());
CommerceQueryOperationResponse queryResponse = response.OperationResponses[0] as CommerceQueryOperationResponse;
SiteTerm st = queryResponse.CommerceEntities[0];
if (st != null)
{
siteTerms = st.Elements;
}
return siteTerms;
}


So now you have a method that reaches inside the Commerce Server and grabs the Site Term you are interested in. All that remains is a call to this method. You may want to do this from say a helper class or the presenter itself. Here is a function that returns the Security Question site term contents:


public static CommerceRelationshipList GetSecurityQuestions()
{
return ProfileController.GetSiteTerm(SiteTermName.SecurityQuestions);
}


If you like you can convert this CommerceRelationshipList into a keyed collection as you wish.

References:

1. What is a site term?