My beef with the ESB Toolkit 2.0 resolution framework

Lately, I have been working on an extension for the ESB Toolkit 2.0 for BizTalk Server 2009. It can be found here on CodePlex. More posts about this ESB.Extensions solution will follow later, this particular post will describe why I think the ESB Toolkit’s resolution framework should be improved.  

A short summary of how the ESB Toolkit resolution framework works. Resolvers are configured in the esb.config file using a specific name that can be coupled to a .NET type that needs to implement IResolveProvider. From the esb.config:  

<resolver name="BRE" type="Microsoft.Practices.ESB.Resolver.BRE.ResolveProvider, Microsoft.Practices.ESB.Resolver.BRE, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>

The above type implements IResolveProvider, and the ESB Toolkit provides a ResolverMgr and ResolverConfigHelper to allow for retrieving and caching the appropriate resolver types when the resolvers are retrieved from the itinerary and to be used by the itinerary service.  

Let’s look at IResolveProvider:  

public interface IResolveProvider
{
    Dictionary<string, string> Resolve(ResolverInfo resolverInfo, XLANGMessage message);
    Dictionary<string, string> Resolve(string config, string resolver, XmlDocument message);
    Dictionary<string, string> Resolve(string config, string resolver, IBaseMessage message, IPipelineContext pipelineContext);
}

IResolveProvider provides three methods that all perform more or less the same task: resolve some information into a Dictionary<string, string>. My main problem with this approach it that everything that needs to be resolved needs to be in the form of string in order to be applicable for this way of resolution. I would expect a generic resolution framework to be more generic than that!  

To make matters worse, there are limitations to which keys you are allowed to use in the resolution dictionary, if you’re using the out of the box resolvers (i.e. the BRE resolver). Basically, you’re stuck with the keys that are provided on the Microsoft.Practices.ESB.Resolver.Resolution type:  

[Serializable]
public sealed class Resolution : IContextParameters, ITransformParameters, IEndpointParameters
{
    ... private members & constructor (left out for brevity) ...  

    public string Action { get; set; }
    public string ActionField { get; set; }
    public string CacheTimeout { get; set; }
    public string DocumentSpecNameField { get; set; }
    public string DocumentSpecStrongNameField { get; set; }
    public string EndpointConfig { get; set; }
    public string EpmRRCorrelationTokenField { get; set; }
    public bool FixJaxRpc { get; set; }
    public string InboundTransportLocationField { get; set; }
    public string InboundTransportTypeField { get; set; }
    public string InterchangeIdField { get; set; }
    public string IsRequestResponseField { get; set; }
    public string MessageExchangePattern { get; set; }
    public string MessageType { get; set; }
    public string MethodNameField { get; set; }
    public string OutboundTransportCLSID { get; set; }
    public string ReceiveLocationNameField { get; set; }
    public string ReceivePortNameField { get; set; }
    public bool Success { get; set; }
    public string TargetNamespace { get; set; }
    public string TransformType { get; set; }
    public string TransportLocation { get; set; }
    public string TransportNamespace { get; set; }
    public string TransportType { get; set; }
    public string WindowUserField { get; set; }
}

Where the three Interfaces (IContextParameters, ITransformParameters, IEndpointParameters) all define a seperate set of properties (which are implemented by the Resolution type, see above).  

The resolvers use this Resolution type to fill the (more generic) Dictionary<string, string> type using ResolverMgr.SetResolverDictionary:  

public static void SetResolverDictionary(Resolution resolution, Dictionary<string, string> ResolverDictionary)
{
    try
    {
        ResolverDictionary.Add("Resolver.Action", resolution.Action ?? string.Empty);
        ResolverDictionary.Add("Resolver.ActionField", resolution.ActionField ?? string.Empty);  

        ... more calls to ResolverDictionary.Add("Resolver.Xxx", resolution.Xxx ?? string.Empty); (removed for brevity) ...   

        ResolverDictionary.Add("Resolver.TransportType", resolution.TransportType ?? string.Empty);
        ResolverDictionary.Add("Resolver.WindowUserField", resolution.WindowUserField ?? string.Empty);
    }  

   ... some exception handling ...  

}

This basically means that you can only work with the properties that the Resolution type provides. I see that as another limitation, because it means that the current resolvers are limited to only be able to resolve those properties. If I want to resolve a string value, that does not correspond to one of the properties of the Resolution type, I have two options:  

  • Abuse one of the properties of the Resolution type. I.e. put a pipeline type name in the property TransformType. I started with this workaround, but it became a problem because I needed to resolve more complex objects than just strings (more on that later).
  • Write a custom Resolver. For instance, this means that I would need to implement a custom BRE resolver, that does resolution using the BRE in exactly the same way as the standard BRE resolver, just because of the different property.

The conclusion is that at the moment, the resolvers are coupled to what those resolvers are actually resolving. That should be decoupled if you ask me.  

I took a stab at improving this situation by implementing a resolution framework that uses the following interface:  

    public interface IResolutionProvider
    {
        ResolutionDictionary Resolve(ResolverInfo resolverInfo, XLANGMessage message);
        ResolutionDictionary Resolve(string config, string resolver, XmlDocument message);
        ResolutionDictionary Resolve(string config, string resolver, IBaseMessage message, IPipelineContext pipelineContext);
    }  

    [Serializable]
    public class ResolutionDictionary : Dictionary<string, object>, ISerializable
    {
        public ResolutionDictionary()
        {
        }  

        public void SetValue(string key, object value)
        {
            base.Add(key, value);
        }  

        public object GetValue(string key)
        {
            if (base.ContainsKey(key))
            {
                return base[key];
            }
            return null;
        }  

        public string GetString(string key)
        {
            return GetValue(key) as string;
        }
    }

This dictionary allows resolvers to resolve anything, for as long as the resolution objects are serializable. It also allows for using any key value (string). The code, that’s calling this kind of resolution, will be responsible for casting the object to the type that it expects.  

The ESB.Extensions resolution framework, that uses the above IResolutionProvider and ResolutionDictionary, is still compatible with the esb configuration and the itinerary designer.  

A full example can be found the in the ESB.Extensions solution on codeplex. I use the resolution framework there to resolve parameters needed in a couple of services: ReceivePipelineService, SendPipelineService and Resequencer services. I implemented some BRE vocabularies to enable for creating the resolution objects.  

Off course, any feedback is welcome!

Advertisements

About Bram Veldhoen

I'm an independant senior software development consultant, working mostly in the area of integration using Microsoft BizTalk Server, Windows Communication Foundation and .NET.
This entry was posted in BizTalk and tagged , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s