Sep 102013
 

So a couple of years I started learning about the ESB and when I discovered that you can pass values into an orchestration through a resolver, I thought ‘this is awesome, now I can send custom values into an orchestration based on a particular itinerary.’

I struggled in actually doing this, and finally put values in the Static Resolver and did sneaky string parsing of the Resolver in an orchestration.

I spent today (yes, one day) and created a simple custom resolver. I read the huge volumes of documentation on how to do it:

  1. Create an assembly with a class that implements the IResolveProvider interface and contains a Resolve method that returns resolver facts as an instance of the Dictionary class.
  2. Register the resolver by adding it to the Esb.config configuration file using a <resolver> element that contains the root moniker as the name attribute and the fully qualified assembly name as the type attribute.
  3. (Optional) Create a schema that defines the root moniker and the query parameters, and then save it in the ESB.Schemas.Resolvers folder. The name should follow existing ESB naming conventions; this means it should use the name of the root moniker appended with “_Resolution.xsd”.
  4. (Optional) Generate a class from the new schema and save it in the custom resolver assembly. This exposes typed parameters in the custom resolver.
  5. Register the new assembly in the global assembly cache.

Not terribly helpful, so I wanted to create the simple steps I took to get it all working:

Resolver

  1. I created a schema that had two values I wanted to pass into the orchestration:
    image
    the actual code is:
    <?xml version="1.0" encoding="utf-16"?> <xs:schema xmlns="http://schemas.microsoft.biztalk.practices.esb.com/itinerary" xmlns:b="http://schemas.microsoft.com/BizTalk/2003" elementFormDefault="qualified" targetNamespace="http://schemas.microsoft.biztalk.practices.esb.com/itinerary" id="HardCoded" xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:element name="HardCoded"> <xs:complexType> <xs:attribute name="value" type="xs:string" use="required" /> <xs:attribute name="enable" type="xs:boolean" use="required" /> </xs:complexType> </xs:element> </xs:schema>

  2. I used xsd.exe and created the class for it. I then added decorations to the class:
    using System.Xml.Serialization; using Microsoft.Practices.Modeling.ExtensionProvider.Metadata; using Microsoft.Practices.Services.ItineraryDsl; using System.ComponentModel; using Microsoft.Practices.Modeling.ExtensionProvider.Extension; namespace StottCreations.ESB.HardCodedResolver { [System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.0.30319.17929")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] [System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "http://schemas.microsoft.biztalk.practices.esb.com/itinerary")] [System.Xml.Serialization.XmlRootAttribute(Namespace = "http://schemas.microsoft.biztalk.practices.esb.com/itinerary", IsNullable = false)] [ObjectExtender(typeof(Resolver))] public partial class HardCoded { private string valueField; private bool enableField; /// <remarks/> [Category(HardedCodedProvider.ExtensionProviderPropertyCategory), Description("Value to be passed into the service"), DisplayName("Value"), ReadOnly(false), Browsable(true)] [System.Xml.Serialization.XmlAttributeAttribute()] public string value { get { return this.valueField; } set { this.valueField = value; } } /// <remarks/> [Category(HardedCodedProvider.ExtensionProviderPropertyCategory), Description("Enable Property"), DisplayName("Enable"), ReadOnly(false), Browsable(true)] [System.Xml.Serialization.XmlAttributeAttribute()] public bool enable { get { return this.enableField; } set { this.enableField = value; } } } [ExtensionProviderAttribute("E5D2F3B5-C792-4050-BF9F-61234B30A3AC", "HardCoded", "Hard Coded Resolver Extension", typeof(ItineraryDslDomainModel))] [ResolverExtensionProvider] public partial class HardedCodedProvider : ExtensionProviderBase { public const string ExtensionSourcePropertyCategory = "Source Settings"; public HardedCodedProvider() : base(typeof(HardCoded)) { } } }

  3. I included the .cs file as part of the project
  4. Build and place the assembly in the following folder: C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\IDE\Extensions\Microsoft.Practices.Services.Itinerary.DslPackage\Lib
  5. Restart Visual Studio

Provider

  1. Created a new C# project, gave it a strong named key (because BizTalk will execute this in the orchestration)
  2. In the class I wrote the following code:
    using System; using System.Collections.Generic; using Microsoft.Practices.ESB.Resolver; using System.Xml; using System.Reflection; using Microsoft.XLANGs.BaseTypes; using Microsoft.Practices.ESB.Exception.Management; using Microsoft.BizTalk.Message.Interop; using Microsoft.BizTalk.Component.Interop; using Microsoft.Practices.ESB.Resolver.Container; using Microsoft.Practices.Unity; namespace StottCreations.ESB.HardCodedResolverProvider { public class HardCodedResolverProvider : IResolveProvider, IResolveContainer { /// <summary> /// Resolve implementation for use with Resolver Web Service. This method is typically used with unit tests to test for the correct Resolved entities, such as itinerary, maps and endpoint addresses. /// This method invokes the Hard Coded Values that were configured through the Config and Resolver connection string values. /// </summary> /// <param name="config">string containing name, value property values</param> /// <param name="resolver">Resolver connection string</param> /// <param name="message">Xml document containing the message to pass to the resolver if configured properly</param> /// <returns>Resolver Dictionary Collection containing resolved entries, such as itinerary name, map name, and endpoint address resolution values</returns> Dictionary<string, string> IResolveProvider.Resolve(string config, string resolver, XmlDocument message) { throw new NotImplementedException(); } /// <summary> /// Resolve implementation for use within a Pipeline component. /// This method is typically used with one of the ESB Pipeline components such as the Itinerary Selector, or ESB Dispatcher to resolve entities, such as itinerary, maps and endpoint addresses. /// This method invokes the Values that were configured through the Config and Resolver connection string values. /// </summary> /// <param name="config">string containing name, value property values</param> /// <param name="resolver">Resolver connection string</param> /// <param name="message">BizTalk IBaseMessage class which is used to pass to the resolver if configured properly</param> /// <param name="pipelineContext">BizTalk Pipeline configuration</param> /// <returns>Resolver Dictionary Collection containing resolved entries, such as itinerary name, map name, and endpoint address resolution values</returns> Dictionary<string, string> IResolveProvider.Resolve(string config, string resolver, IBaseMessage message, IPipelineContext pipelineContext) { throw new NotImplementedException(); } /// <summary> /// Resolve implementation for use within an Orchestration. /// This method is typically used by creating an instance of the BRE Resolver Provider class inside an orchestration expression to resolve entities, such as itinerary, maps and endpoint addresses. /// This method invokes the BRE Policies that were configured through the Config and Resolver connection string values. /// </summary> /// <param name="resolverInfo">Resolver collection of entries</param> /// <param name="message">BizTalk XLangMessage class which is used to pass to the resolver if configured properly</param> /// <returns>Resolver Dictionary Collection containing resolved entries, such as itinerary name, map name, and endpoint address resolution values</returns> Dictionary<string, string> IResolveProvider.Resolve(ResolverInfo resolverInfo, XLANGMessage message) { Dictionary<string, string> dictionary; if (message == null) { throw new ArgumentNullException("message"); } Resolution resolution = new Resolution(); try { dictionary = ResolverMgr.GetFacts(resolverInfo.Config, resolverInfo.Resolver); } catch (Exception exception) { EventLogger.Write(MethodBase.GetCurrentMethod(), exception); throw; } finally { if (resolution != null) { resolution = null; } } return dictionary; } private static IUnityContainer container; public void Initialize(Microsoft.Practices.Unity.IUnityContainer container) { HardCodedResolverProvider.container = container; } } }

  3. Compile and place the code into the GAC
  4. In the esb.config, you need to tell the ESB how to look up this: so you add the following line to the configuration\esb\resolvers section (upper case of the class of the resolver (public partial class HardCoded))
    <resolver name="HARDCODED" type="StottCreations.ESB.HardCodedResolverProvider.HardCodedResolverProvider, HardCodedProvider, Version=1.0.0.0, Culture=neutral, PublicKeyToken=41bb35272df047ea"/>

  5. In the orchestration, add the following code in an expression shape (after creating to string variables called Enable and Value)
    Resolver=Resolvers.Current; ResolverDictionary = Microsoft.Practices.ESB.Resolver.ResolverMgr.Resolve(InMsg, Resolver); Enable = ResolverDictionary.Item("ENABLE"); Value = ResolverDictionary.Item("VALUE");

  6. Deploy the orchestration and add the reference to the newly deployed orchestration in the esb.config
  7. Create the itinerary that calls the orchestration you created in step 5 image
  8. Deploy the itinerary, setup the on ramp to call the itinerary
  9. Run a message through the itinerary
  10. I put a breakpoint as I ran it and here is the Orchestration debugger image

Please don’t flame me, I know that there are a series of try/catch blocks I should be putting in this, but this is 15 steps on how to create a custom resolver that you can pass values directly into an orchestration (and do the same for a messaging based solution also).