Bram Veldhoen

C#, .NET, WCF, BizTalk
posts - 11, comments - 23, trackbacks - 0

My Links

News

Archives

BizTalk Bloggers

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);
    }


Where ResolutionDictionary looks like this:

    [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!

Print | posted on Friday, August 28, 2009 7:32 AM |

Feedback

Gravatar

# re: My beef with the ESB Toolkit 2.0 resolution framework

Heeeeee die Bram....

Switch ENGLISH

Are you telling me that I cannot deploy a property schema of my own and have the resolution framework use those in resolution ?

That could mean the Resolution framework is bound to iterinary... Cause I had a quick look and it seems the Resolution class has many (if not all) of the props an iterinary has...

I wonder by the way how

public bool Success { get; set; }

would resolve using strings ?
8/28/2009 8:47 AM | Patrick Wellink
Gravatar

# re: My beef with the ESB Toolkit 2.0 resolution framework

Hi Pat! Good to hear from you again.

The Success property is written in the Dictionary<string, string> as string using the following statement:

ResolverDictionary.Add("Resolver.Success", resolution.Success.ToString());

(It got cut out due to brevity; It would have been good if I had mentioned that booleans are also supported as properties. Booleans! woohoo!).

You describe the exact same scenario that I wanted to do (and eventually did do in the ESB.Extensions): using a custom schema that I want to use to resolve instances for (with custom hierarchy of elements and attributes etc.). Check out the SendPipelineServiceResolution class in the ESB.Extensions, this class is defined by a schema and I added some properties and functionalities to it using partial classes.

The SendPipelineService uses this SendPipelineServiceResolution class to resolve. The properties of this SendPipelineServiceResolution object are set during resolution by the BRE (in the ESB.Extensions, a resolver called BRE2).
I tried many ways to do this using the original resolvers, without success. I had a working workaround for it by serializing it into xml in the BRE, and than deserializing it back out in the orchestration, but I really didn't like that solution (slow and cumbersom).

HTH!

Greets,
Bram.
8/28/2009 12:30 PM | Bram Veldhoen
Gravatar

# re: My beef with the ESB Toolkit 2.0 resolution framework

Going to look into the ESB Extensions. Looks pretty interesting. Your resolver dictionary does look interesting and would be more flexible and a better design.

One note I would make is that the resolver dictionary can hold any property though and is not limited to the interfaces you mention. I actually take advantage of this in a sample I built to show how to create a custom message service. On my blog if you are interested.
11/10/2009 11:09 PM | Brendon Birdoes
Post A Comment
Title:
Name:
Email:
Website:
Comment:
Verification:
 
 

Powered by: