giovedì, gennaio 06, 2011

Using Unity in BizTalk Solutions

The Unity Framework is the pattern & practice solution for realizing Dependency Injection and Inversion Of Control containers.

In few words Dependency Injection is the simplest way to compose applications starting from replaceable components.

Let’s say you have to implement a configuration layer inside your application: you still don’t know if the configuration store will be implemented in SSO (if you’re a smart biztalk developer ;o) ) or on a database or in a xml file or in a text file or whatever, all you simply know is that you’ll need to GetConfigurationData in some ways.

So, as every good OO developers, you’ll start in developing an Interface declaring the method you’ll use to retrieve configuration data such as the following:

   1: public interface IConfigurationStore
   2: {
   3:     public string GetConfigurationData(params string keys)
   4: }

Then you’ll implement configuration concrete classes implementing this interface, you’ll have a SQLConfigurationStore,a XmlConfigurationStore and so on.

And when you’ll need to get a configuration value, you’ll use the interface instead of a concrete implementation in your code therefore removing dependency to the concrete classes from your application:

   1: private IConfigurationStore Config
   2: [...]
   3:  
   4: String ConnectionString = 
   5:     Config.GetConfiguration("MyApplication", "SQLLookupConnData")
   6:  

But there’s a catch:

In your application code you still have to add a dependency to the concrete class somewhere, in fact, in your code you’ll have soon or later to assign the variable

   1: IConfigurationStore Config = new SQLConfigurationStore(connstring);

and so, we’ve reduced the number of dependencies to just an assignment, but the dependency is still there.

Dependency Injection Containers comes to the rescue here, eliminating this last dependency point and allowing us to implement true decoupled components.

Instead of directly assign the variable to the concrete class we simply ask the DI Container to do the work for us (below the code necessary in Unity)

   1: [...]
   2: UnityContainer container= new UnityContainer();
   3: [...]
   4: IConfigurationStore store = container.Resolve<IConfigurationStore>();
   5: String connectionString = store.GetConfigurationData("MyApplication","SQLLookupConnData");

Unity is acting as a opaque factory and its instantiation work is driven from an external xml file; By default unity searches this information in the app.config file but you can redirect it to a separated xml file.

Using BizTalk I prefer this second approach so I don’t have to mess with the BTSNTSvc.exe.config.

Anyway a snippet of this xml file is the following.

   1: <container name="Container">
   2:     <types>
   3:         <type type="IConfigurationStore" mapTo="XmlConfigurationStore" >
   4:             <typeConfig extensionType="Microsoft.Practices.Unity.Configuration.TypeInjectionElement, Microsoft.Practices.Unity.Configuration, Version=1.2.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
   5:                 <constructor>
   6:                     <param name="fileName" parameterType="System.String">
   7:                         <value value="C:\Configurations\XmlFile.xml"/>
   8:                     </param>
   9:                 </constructor> 
  10:             </typeConfig>
  11:             <lifetime type="singleton" />
  12:         </type>
  13:         <type name="SQL" type="IConfigurationStore" mapTo="SQLConfigurationStore" >
  14:             <typeConfig extensionType="Microsoft.Practices.Unity.Configuration.TypeInjectionElement, Microsoft.Practices.Unity.Configuration, Version=1.2.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
  15:                 <constructor>
  16:                     <param name="connectionString" parameterType="System.String">
  17:                         <value value="...connection string to sql configuration store..."/>
  18:                     </param>
  19:                 </constructor> 
  20:             </typeConfig>
  21:             <lifetime type="singleton" />
  22:         </type>
  23:     </types>
  24: </container>

Notice that name=SQL attribute in the snippet, it is used by unity when a specific resolution if required: in the above example, since we used the parameterless Resolve method then store will be valorized with nameless entry (XmlConfigurationStore), but if we used Resolve<…>(“SQL”) in the above code, then Unity would have used the entry with name attribute equals to ”SQL” (SQLConfigurationStore) to instantiate the required concrete class.

Add BizTalk to the equation.

BizTalk Server Solution maintenance can be truly a nightmare: even if BizTalk architecture is definitely component-oriented each deployment must be done in a long, boring, and unavoidable procedure.

If you want to update an orchestration you must:

  1. Undeploy old orchestration.
    1. You can’t remove orchestration if you first don’t unenlist them
      1. you can’t unenlist orchestration if you first don’t terminate each service instance related to it.
  2. Deploy new orchestration.
  3. Bind new orchestration.
  4. Enlist new orchestration.

If you want to update a pipeline (to replace a pipeline component with another one) you’ve to follow a similar tedious and error prone procedure.

Even if there are ways to workaround this problem (such as using a versioning policy as I’ll explain in another post) the main lesson is: make your architecture so flexible that redeploy of biztalk assemblies are rarely needed and only for important changes (not just because I decided to move my configuration datastore from file to database or because I want to track the information X from messages while till now I was tracking information Y).

Unity, and other DI Containers are invaluable allied in this battle for maintainability and flexibility.

BAM Writer Component.

IMHO the killer application for DI in BizTalk environment is in BAM instrumentation.

A bit of history

BizTalk presents an interesting feature, inherited by BizTalk Server 2004 edition, called BAM Interceptors .

The goal of interceptors is to let biztalk developer deploy their application and decide afterwards which data to write to BAM, as explained in the msdn

In each step of your application where you could have data of interest, you call Interceptor OnStep, provide an identifier for the step, and provide some data or arbitrary object that you are using in your application.
You must implement a callback function so when the callback occurs, your callback procedure gets the current step ID and your data object.
The BAM interceptor decides which data to request at each step, based on the configuration that you can create programmatically.
The BAM Interceptor then uses the obtained data to call either DirectEventStream or BufferedEventStream that you need to keep around and pass each time as an argument to OnStep.
You may keep different pre-created interceptors representing different preferences for the data and milestones for BAM.

Every time I read this explanation I see in it a Dependency Injection description ante-litteram (even if someone could argue that in 2004 DI was pretty well known in IT…)

In the runtime code there are invocation to a BAMInterceptor object which is defined externally and loaded at runtime (the only difference is that instead of using a DI Container driven by an external configuration file BizTalk uses a naïve approach based on BinaryFormatter serialization of a previously defined object)

Coming back to 2011

Following the spirit of interceptors I wanted to monitor (and possibly peek data of  interest) every message which entered or exited from my BizTalk Solution.

And I wanted to decide after the solution is running in production (even after weeks or months, just when necessity arise), which milestones and kpi to track and extract from messages.

One pipeline (component) to rule them all.

To reach this goal i implemented a single pipeline component, called BAMTracking which took a single parameter (aside from the enabled/disabled flag) BAMContext.

image

image

Internally the Execute method of the pipeline simply uses Unity to resolve and instantiate the correct BAMWriter using BAMContext string as optional unity name to differentiate at runtime between different BAMWriter implementations

   1: private IBaseMessage doExecute(IPipelineContext pContext, IBaseMessage pInMsg)
   2: {
   3:     try
   4:     {
   5:         if (String.IsNullOrEmpty(this.bamContext))
   6:             dataExtractor = Container.Resolve<IBAMWriter>();
   7:         else 
   8:             dataExtractor = Container.Resolve<IBAMWriter>(this.bamContext);
   9:     }
  10:     catch (DependencyMissingException)
  11:     {
  12:         dataExtractor = Container.Resolve<IBAMWriter>();
  13:     }
  14:  
  15:     dataExtractor.Extract(pContext, pInMsg);
  16: }

IBAMWriter Interface

At this point the BAMWriter interface is very simple and mimic the Execute method

   1: public interface IBAMWriter
   2: {
   3:     void Extract(IPipelineContext pContext, IBaseMessage pInMsg);
   4: }
 
In this way I can deploy my BizTalk Infrastructure once and when the needs to track a particular information from a particular message will rise I’ll simply implement the correct logic in an BAMWriter class, register its assembly within the Unity configuration file and change BAMContext property on pipeline per-instance configuration screen.
 
No further deploy is needed.

Tips when using Unity with BizTalk.

Use external xml configuration.

As depicted above I prefer to externalize in a standalone xml file all unity configuration instead of using a section in the BTSNTSvc.exe.config file.

This allows me to safely experiment with the xml file and allow me to quickly bring unity configuration from an environment to another one leaving untouched other settings.

The following code snippet is used to load Unity configuration from an external xml file:

   1: public static void Create(string FileName)
   2: {
   3:     ExeConfigurationFileMap map = new ExeConfigurationFileMap();
   4:     map.ExeConfigFilename = FileName;
   5:     System.Configuration.Configuration config
   6:       = ConfigurationManager.OpenMappedExeConfiguration(map, ConfigurationUserLevel.None);
   7:     UnityConfigurationSection section
   8:       = (UnityConfigurationSection)config.GetSection("unity");
   9:     container = new UnityContainer();
  10:     section.Containers["Container"].Configure(container);
  11: }

Where to put filename.

Looking at the code above smartest will notice that we’ve reintroduced a dependency: the reason why, by default, unity uses the app.config to search for its configuration data is that the app.config is well known to the runtime without having to explicitly point to it.

If we start to use an external xml file we need to tell application where to find it and having a concrete codeline such as

Create(“C:\\Unity\\Config.xml”)

will vanish every effort to remove dependency we made till here.

To make everything work you’ll have to externalize this filename too; one could use the registry to store this information (because it gave you per user and per key permissions, hierarchical structure and a lot of other interesting things) but I actually prefer the plain old simple environment variables because of its simplicity.

I define an environment variable called “UNITY_CONFIGURATION” containing the full path to the Unity configuration file and from application code I can simply refer it with a:

Create(System.Environment.ExpandEnvironmentVariables(“%UNITY_CONFIGURATION%”));

and that’s all.

Use Unity with GACed assemblies.

Unity examples you’ll find around won’t work with gaced assemblies but only with in place assemblies, this is because in unity configuration files they refer to assemblies by name and not by strong name, therefore CLR can’t do a resolution against GAC

In BizTalk nearly all dll used must be placed in GAC to work but luckily the solution is very simple as hinted above: when compiling your Unity configuration file remember to use the full strong name instead of the partial name of assemblies you want to use and the problem will go away.

   1: <!-- This is NOT going to work for GACed Assembly -->
   2: <typeAlias alias="ILogger"
   3:     type="UnityExamples.Common.ILogger, UnityExamples.Common" />
   4:  
   5: <!-- This IS going to work for GACed Assembly -->
   6: <typeAlias alias="ILogger"
   7:     type="UnityExamples.Common.ILogger, UnityExamples.Common, Version=1.0.0.0, Culture=neutral, PublicKeyToken=45b4585645364e32" />

2 commenti:

Anonimo ha detto...

Thanks for posting this information. I'm in the process of doing exactly what you have described. Your post will save me a few hours of head scratching tring to get unity to work in a BizTalk environment.

Thanks again.

Anonimo ha detto...

Thanks a lot, exactly the information I was looking for :)