Tallan's Technology Blog

Tallan's Top Technologists Share Their Thoughts on Today's Technology Challenges

A Better ReadPropertyBag in BizTalk Pipeline components

Dan Field

Just a quick one today.  I’ve been working on several BizTalk pipeline components lately, and had been using some code that’s been floating around here for a while – I’ve found them posted on other blogs/message boards as well, but the earliest posting I’ve found of it is from another Tallan blog post from 2006: http://blog.tallan.com/2006/11/22/custom-pipeline-components-part-2-archive-same-continued/.

The relevant code from that post is this:

private object ReadPropertyBag(Microsoft.BizTalk.Component.Interop.IPropertyBag pb,string propName)
{
  object val = null;
  try
  {
    pb.Read(propName, out val, 0);
  }
  catch (System.ArgumentException)
  {
    return val;
  }
  catch (System.Exception e)
  {
    throw new System.ApplicationException(e.Message);
  }
  return val;
}

This code works well enough, and isn’t terribly difficult to use (especially if you’re only dealing with a few properties).  Using it (from the same post), looks like this:

object val = null;
val = this.ReadPropertyBag(pb, “path”);
if ((val != null))
{
  this._path = ((string)(val));
}

This becomes a little tedious when dealing with multiple properties, and is difficult to edit if you have to refactor property names or values.  Instead, using generic types provides more flexibility.  We can also add a some logic to remove spaces so that a display name of the property can have spaces and still be used in the property bag (trying to save a property name with spaces causes a very unhelpful error; see http://geekswithblogs.net/michaelstephenson/archive/2013/07/17/153431.aspx for more details) :

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Threading.Tasks;

namespace Tallan.Common.PipelineComponents

{

internal static class PropertyBagUtil

{

/// <summary>

/// Reads property value from property bag.

/// </summary>

/// <param name="pb">Property bag.</param>

/// <param name="propName">Name of property.</param>

/// <param name="currentVal">A reference to where to store the current value, if needed</param>

public static void ReadPropertyBag<T>(Microsoft.BizTalk.Component.Interop.IPropertyBag pb, string propName, ref T currentVal)

{

object val = null;

try

{

pb.Read(propName.Replace(" ", ""), out val, 0);

if (val != null)

{

if (val is T) // don't use as - might not be a reference type

{

currentVal = (T)val;

}

else

{

Logger.LogError("Could not cast property '" + propName + "' to expected type '" + typeof(T).Name + "'");

currentVal = default(T);

}

}

}

catch (System.ArgumentException)

{

currentVal = default(T);

}

catch (System.Exception e)

{

Logger.LogError("ReadPropertyBag failed\r\n\r\n" + e.ToString());

throw;

}

}

/// <summary>

/// Writes value to property bag, replacing any spaces in the property name if needed.

/// </summary>

/// <param name="pb">Property bag</param>

/// <param name="propName">Property name, with or without spaces</param>

/// <param name="value">new value</param>

public static void WritePropertyBag(Microsoft.BizTalk.Component.Interop.IPropertyBag pb, string propName, object value)

{

try

{

pb.Write(propName.Replace(" ", ""), ref value);

}

catch (Exception e)

{

Logger.LogError("WritePropertyBag failed\r\n\r\n" + e.ToString());

throw;

}

}

}

}

and now, loading and saving the property bag becomes quite a bit clearer and easier to maintain:

const string PATH_PROP_NAME = "File Path";

const string ENABLED_PROP_NAME = "Enabled";

private string _pathName;

[DisplayName(PATH_PROP_NAME)] // the properties GUI will display 'File Name'

public string PathName

{

get { return _pathName; }

set { _pathName = value; }

}

private bool _isEnabled;

[DisplayName(ENABLED_PROP_NAME)]

public bool IsEnabled

{

get { return _enabled; }

set { enabled = value; }

}

...

public void IPersistPropertyBag.Load(IPropertyBag pb, int errorLog)

{

PropertyBagUtil.ReadPropertyBag(pb, PATH_PROP_NAME, ref _pathName); // the property will be read as 'FileName'

PropertyBagUtil.ReadPropertyBag<bool>(pb, ENABLED_PROP_NAME, ref _isEnabled);

}

public void IPersistPropertyBag.Save(IPropertyBag pb, bool clearDirty, bool saveAllProperties)

{

PropertyBagUtil.WritePropertyBag(pb, PATH_PROP_NAME, PathName);

PropertyBagUtil.WritePropertyBag(pb, ENABLED_PROP_NAME, IsEnabled);

}

5 Comments. Leave new

Paul Geldenhuys
June 28, 2016 8:59 am

Thanks Dan, I found your article useful. I’d like to suggest an edit though, something I picked up while debugging my pipeline component where I used your code.

Referring to this article by Charles Young, I would suggest you modify your code listing to make use of nullable value types, i.e. Nullable instead of bool. This would allow your public Load method to coalesce the return value of the private ReadPropertyBag – crucial for cases where the per-instance property bag contains no value for a specific property.

You Load method would then look like this:


public void IPersistPropertyBag.Load(IPropertyBag pb, int errorLog)
{
string PathName = this.ReadPropertyBag(pb, PATH_PROP_NAME) ?? PathName; // the property will be read as 'FileName'
bool bVal = this.ReadPropertyBag(pb, ENABLED_PROP_NAME) ?? bVal;
}

Thanks Paul, that’s a great suggestion. I’ll update the post.

Paul Geldenhuys
June 28, 2016 9:06 am

Re-commenting, HTML tags getting messed up…

Thanks Dan, I found your article useful. I’d like to suggest an edit though, something I picked up while debugging my pipeline component where I used your code.

Referring to this article by Charles Young, I would suggest you modify your code listing to make use of nullable value types, i.e. bool? instead of bool. This would allow your public Load method to coalesce the return value of the private ReadPropertyBag method – crucial for cases where the per-instance property bag contains no value for a specific property.

Your Load method would then look like this:

public void IPersistPropertyBag.Load(IPropertyBag pb, int errorLog)
{
string PathName = this.ReadPropertyBag(pb, PATH_PROP_NAME) ?? PathName; // the property will be read as ‘FileName’
bool bVal = this.ReadPropertyBag(pb, ENABLED_PROP_NAME) ?? bVal;
}

Paul,

I’ve updated my code, but not exactly as you specified. It’d be possible to do what you’re talking about a couple different ways, but I think it’s cleanest to just do it the way I’ve suggested. What do you think?

Paul Geldenhuys
June 30, 2016 8:01 am

Hi Dan

Either way would work, and at the end of the day I don’t think there is much difference except for semantics. Personal preference would see me using the coalesce operator with nullable value types instead of adding a new parameter to the ReadPropertyBag method, but that’s just me.

Attempt #3 at posting comment containing angular brackets in code block:

public void IPersistPropertyBag.Load(IPropertyBag pb, int errorLog)
{
string PathName = this.ReadPropertyBag<string>(pb, PATH_PROP_NAME) ?? PathName; // the property will be read as ‘FileName’
bool bVal = this.ReadPropertyBag<bool?>(pb, ENABLED_PROP_NAME) ?? bVal;
}

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>