Saturday, January 16, 2010

ShareTalk Integration (SharePoint/BizTalk) – Part 10 Adding Metadata to SharePoint columns in a messaging only solution

I had a requirement where I needed to upload images to a SharePoint library and use the information from the image filename to populate columns within the document library. Performing these tasks within an orchestration is pretty trivial as you can set context properties in a Message Assignment shape that will drive the behavior of the WSS Adapter.  I couldn’t justify performing these tasks in an Orchestration since it would involve an extra hop to the MessageBox in order for Orchestration to be invoked.

Another option is to do all of this work in a Pipeline component.  I can create  a Send Port subscription that would allow me to access the context properties from the message received and update them to include the WSS properties all within in a pipeline. This would allow the solution to become a pure messaging solution and I can save an extra Message Box hop.  When building this solution, I referenced Saravana Kumar’s white paper on Pipeline Components.  It came in handy,  especially in the area of creating the Design time pipeline properties.

Within the Pipeline Component, the first thing that I wanted to to was retrieve the source file name.  I am able to do this by reading the “ReceivedFileName” property from the File Adapter’s context properties. I then wanted to clean this file name up since the value of this property includes the entire UNC path: \\Servery\RootFolder\SubFolder\filename.jpg.  I have written some utility methods to parse the file name from this long string.

There are 3 parts to the image file name(1213455_NEW_20100110120000.jpg) that I am particularly interested in.  They include an Asset Number, Asset State and the Date/Time that the image was taken.  The scenario itself is a field worker who needs to capture an image of an asset, indicate the Asset Number and its state i.e. New/Old.  This information is then captured in the name of the image.  Since it is an image, there is no other reasonable way to store this meta data outside the file name.  This in itself is the reason why I need to to use a pipeline component.  Standard WSS adapter functionality includes the ability to use XPATH statements to extract data from the message payload and populate a SharePoint document library column. 

Once I have captured this meta data and massaged it to my liking, I want to then provide this context data to the WSS Adapter.  The WSS adapter is a little different than most of the other adapters in that you can populate an XML document and push that into the ConfigPropertiesXml context property.  The document structure itself is a flat XML structure that uses a “key-value” convention.

<ConfigPropertiesXml><PropertyName1>Column 1 Name</PropertyName1><PropertySource1>Column 1 Value</PropertySource1><PropertyName2>Column 2 Name</PropertyName2><PropertySource2>Column 2 Value</PropertySource2><PropertyName3>Column 3 Name</PropertyName3><PropertySource3>Column 3 Value</PropertySource3></ConfigPropertiesXml>

 

I also want to populate the WSS Adapter’s Filename context property.  I can achieve this by the following statement:

pInMsg.Context.Write("Filename",
        "http://schemas.microsoft.com/BizTalk/2006/WindowsSharePointServices-properties", ImageFileName);

 

Below is my Execute method, in my pipeline component, where all of this processing takes place.  You can download the entire sample hereThis code is at a proof of concept stage so you will want to evaluate your own error handling requirements. Use at your own risk.

public IBaseMessage Execute(IPipelineContext pContext, IBaseMessage pInMsg)
{
    //Get received file name by retrieving it from Context
    string FilePath = pInMsg.Context.Read("ReceivedFileName", "http://schemas.microsoft.com/BizTalk/2003/file-properties") as string;

    //strip path from filename
    string ImageFileName = GetFileNameFromPath(FilePath);

    //Utility methods to parse filename
    string msgAssetNumber = GetAssetNumberFromFileName (ImageFileName);
    string msgAssetState = GetAssetStateFromFileName(ImageFileName);
    string msgImageDateTime = GetImageDateFromFileName(ImageFileName);

    //Write desired file name to context of WSS Adapter
    pInMsg.Context.Write("Filename",
        "http://schemas.microsoft.com/BizTalk/2006/WindowsSharePointServices-properties", ImageFileName);

    // Populate Document Library Columns with values from file name
    string strWSSConfigPropertiesXml = string.Format("<ConfigPropertiesXml><PropertyName1>{0}</PropertyName1><PropertySource1>{1}</PropertySource1>" +
        "<PropertyName2>{2}</PropertyName2><PropertySource2>{3}</PropertySource2><PropertyName3>{4}</PropertyName3><PropertySource3>{5}</PropertySource3></ConfigPropertiesXml>",
        this.AssetNumber,msgAssetNumber,this.AssetState,msgAssetState,this.ImageDateTime,msgImageDateTime);

    pInMsg.Context.Write("ConfigPropertiesXml", "http://schemas.microsoft.com/BizTalk/2006/WindowsSharePointServices-properties", strWSSConfigPropertiesXml);
    return pInMsg;
}

A feature that I wanted to provide is the ability to provide the SharePoint column names at configuration time.  I didn’t want to have to compile code if the SharePoint team wanted to change a column name.  So this is driven from the pipeline configuration editor.  The values that you provide (on the right hand side) will set the column names in the ConfigPropertiesXml property that is established at run time.

 

image

If you provide a value in this configuration that does not correspond to a column in SharePoint, you will get a warning/error on the Send Port.

Event Type:    Warning
Event Source:    BizTalk Server 2009
Event Category:    (1)
Event ID:    5743
Date:        1/10/2010
Time:        7:38:19 PM
User:        N/A
Computer:   
Description:
The adapter failed to transmit message going to send port "SendDocToSharePoint" with URL "wss://SERVER/sites/BizTalk%%20Repository/Inbound%%20Documents". It will be retransmitted after the retry interval specified for this Send Port. Details:"The Windows SharePoint Services adapter Web service encountered an error accessing column "Missing Column" in document library http://SERVER/sites/BizTalk%%20Repository/Inbound%%20Documents. The column does not exist. The following error was encountered: "Value does not fall within the expected range.".

This error was triggered by the Windows SharePoint Services receive location or send port with URI wss://SERVER/sites/BizTalk Repository/Inbound Documents.

Windows SharePoint Services adapter event ID: 12295".

For more information, see Help and Support Center at http://go.microsoft.com/fwlink/events.asp.

 

The end result is that I can use information contained in the file name to populate meta data columns in SharePoint without an Orchestration.

image

Also note, I have built this pipeline component so that it can be used in a Receive Pipeline or as Send Pipeline by including the CATID_Decoder and CATID_Encoder attributes.

 

   [ComponentCategory(CategoryTypes.CATID_PipelineComponent)]
   [ComponentCategory(CategoryTypes.CATID_Decoder)]
   [ComponentCategory(CategoryTypes.CATID_Encoder)]

   [System.Runtime.InteropServices.Guid("9d0e4103-4cce-4536-83fa-4a5040674ad6")]
   public class AddSharePointMetaData : IBaseComponent, IComponentUI, IComponent, IPersistPropertyBag 
  

image

 

image

2 comments:

Ran said...

My current project requires ability to display choice column value via color or image associated with a choice

But Sharepoint standard packaged misses that control

I am looking for available solutions on market

I came across

http://amazepoint.com/Products/VisualChoiceColumn/Overview.aspx>


Does anybody has experiece using it?

Kent Weare said...

Hi Ran,

Sorry, I have no experience with it. I would contact Bil Simser who is a colleague of mine. He is a SharePoint Wiz. His blog is http://weblogs.asp.net/bsimser/ or look for him on Twitter.

Kent