lunedì, gennaio 10, 2011

Dynamic Maps

Some times ago, while searching for a method to mock up Extension Objects during unit tests (in a future post the solution I came out with…) I stumbled into this interesting technique for realizing dynamic maps I want to share.

(For Dynamic Map here I mean a map whose content is not statically compiled into the map dll and therefore whose behavior can be changed without any redeploy)

BizTalk maps are simply .NET classes, as depicted in this article of Paolo Salvatori, our btm is took by BizTalk compiler and transformed into a sealed class inheriting by TransformBase class.

The component responsible to execute maps in BizTalk is the EPMTransform class contained in the Microsoft.BizTalk.EPMTransform assembly.

This class does a lot of things but the central point is in its Initialize method:

  • First of all it load the assembly containing our map class
  • Then, using reflection, it instantiate our map class and cast it to a TransformBase.
  • It reads just instantiated StreamingTransform and TransformArgs properties from our map class.

To have a dynamic map we should be able therefore to override these StreamingTrasform and TransformArgs methods but, unfortunately, both methods are declared in TransformBase and we can’t override these two methods because they’re not declared as virtual.

Luckily what these properties are doing is no rocket-science:

StreamingTransform will simply load the xslt contained in the XmlContent property in the transform and return it.

TransformArgs is a bit more complex but it simply parse the Extension Object format used by BizTalk and contained in property XsltArgumentListContent to return the corresponding XsltArgumentList object.

Both XmlContent and XsltArgumentListContent are strings properties containing Xml data and both are abstract in TransformBase, therefore we can redefine them in our map classes.

Beware of private const.

Maps classes are very simple classes, in the following image the result of a BizTalk map (called MyMap) disassembled using reflector:

image

SchemaReferenceAttribute are used to point to schema classes referenced by the map (in my case both source and destination schemas are referring to a schema named MySchema and contained in TCPSoftware.Sample.DynamicMap) and strings _strSrcSchemasList0, _strTrgSchemasList0, SourceSchemas and TargetSchemas are repeating the same information.

Aside from XmlContent and XsltArgumentListContent you’ll find the same private const twins called _strMap and _strArgList

Notice that set of private const fields… they’re totally redundant and should be removed (private, const, and never used inside the class… if you ask me they’re not necessary) BUT if you remove them BizTalk will be unable to deploy the map (probably those fields are extracted using reflection by administration console, not a smart thing to do since they’re private and their content is duplicated in public properties but hey, biztalk is still using XslTransform so there’s definitely space for improvements in TransformBase design…) anyway, for the rest of the article, you may safely ignore them, letting their default value.

Dynamic Maps.

Imagine to redefine our class inheriting from TransformBase and containing the following definition of XmlContent:

   1: public override string XmlContent
   2: {
   3:     get
   4:     {
   5:         string MyXsltFileLocation = "C:\\XSLTStore\\Xslt1.xslt";
   6:         return System.IO.File.ReadAllText(MyXsltFileLocation);
   7:     }
   8: }

Just a demo snippet but I hope you will see the value in it: we can redirect the true map logic to an xslt file written externally to the deployed map (in the example on our filesystem. but nothing prevent us to put it on a db) allowing us to fix problems or enhance mapping logic just by using notepad (or doing a db update) without any long and error prone deployment process.

And if you hate having to mess with XSLT directly, what about the following snippet:

   1: public override string XmlContent
   2: {
   3:     get
   4:     {
   5:         string assemblyToTestName = "Maps1.dll";
   6:         string mapToTestName = "Map"; 
   7:         Type type = Assembly.Load(assemblyToTestName).GetType(mapToTestName);
   8:         TransformBase b = (TransformBase)Activator.CreateInstance(type);
   9:         return b.XmlContent;
  10:     }
  11: }

With the above implementation the deployed map will simply redirect BizTalk requests to another map.

Possibilities are endless: What about combining both approaches and having a lookup (on a db or on the filesystem) to check if map should be redirected before returning the default (compiled) map?

In this way the system will continue to work as intended (compiled) but, in case of necessity you may simply add an entry in a database or in a configuration file to override the deployed map redirecting processing to another one.

Will it work?

The answer is yes (and you can experiment with this sample) but there’s a catch (which IMHO is a great pro of using this technique):

Remember when I said that the EPMTransform will load our maps inside a method called Initialize?

Well, as the name imply, this method is just called when a map needs to be initialized, not every time the map is applied because BizTalk will cache the XslTransform once initialized and this means that our custom code won’t be called at every map invocation, just when the map is first initialized.

So, if you want that your custom XmlContent method to be invoked, you HAVE to restart the host instance hosting the map.

On the downside this means that no, you can’t simply start notepad, do a modify, and see biztalk use the modified map; in fact you have to start notepad, do a modify, restart a service, and see biztalk use the modified map (not a big deal…)

On the contrary, this also means that your custom code can be pretty complex (in the previous section we imagined to access a database or to read a file or even to create a class instance via reflection) and your mapping performance won’t suffer.

Conclusions

Even if I’m not suggesting the usage of such technique in production I can’t really see a single reason for not using it during developments: being able to test new solution simply changing a map file with notepad will increase your efficiency tremendously.

1 commento:

Anonimo ha detto...

excellent !