Monday, December 19, 2011

Using XML-based Plug-in Configuration

For more complicated and feature-rich Plug-in customizations for CRM, it can be appealing to use highly dynamic configurations.  The Plug-in model offers simple string parameters to allow for the open interpretation by Plug-in developers.  One of the more efficient and flexible configuration mechanisms is XML.

With XML configuration, there are three methods that can be used to varying degrees of efficacy:

  1. Hard-parsing; dissecting XML with String manipulation (useful in dire situations)
  2. XmlDocument parsing and node traversal; similar to JavaScript DOM manipulation and perhaps easier for web developers to grasp
  3. XML-serialized classes; deserializing XML into one or more strongly-typed classes that represent the configuration

For the purposes of this post, I’m going to focus on the method that I prefer and find superior to the others:  XML-serialized classes.  The reason I believe this is superior is for the following reasons:

  • Serialized classes can be versioned with namespace declarations; meaning that updating code and providing for new configuration specifications can preserve older versions
  • Hard-parsing and XmlDocument traversal requires significantly more code to determine what was passed
  • Serialization and deserialization work both ways; Plug-in configuration can be performed by a utility (think Silverlight!) that understands the .Net class representing the configuration, and easily construct the XML; this ability is simplified to a great extent with WCF Data Contracts (which we’ll get to in a moment)
  • Hard-parsing and XmlDocument traversal code requires more effort to update or alter the configuration schema
  • Hard-parsing and XmlDocument traversal work better in situations when the XML document and its contents are either
    1. Very simple and statically defined
    2. Totally unknown and dynamic
  • Serialized classes define a rigid standard and schema for the configuration, but also offer a significant amount of flexibility while simplifying the interpretation of the configuration

As a caveat, I should state that I’m extremely comfortable with XmlDocument use.  I’ve used it many times, and found it a natural fit for my long experience with JavaScript DOM programming.  However, I recently implemented a project that needed a highly structured and very robust configuration model, and decided to look for ways to reduce the amount of code I needed to write.  Additionally, I needed the process to be flexible, as I might change my mind about the operational needs from the configuration as the code evolved.

Starting with the concept of deserializing .Net classes from XML, I searched for the best method of implementation for CRM that I could identify.  I found that there are a few ways within the .Net framework to serialize and deserialize classes into and from XML.  Some are more flexible than others, but the CRM platform offers unique challenges that pretty much made my answer for me:  WCF Data Contracts.

Why WCF Data Contracts?  Well, I like to deploy my assemblies to the database.  This is a practice that helps assure the interoperability of my code with CRM Online.

The problem with traditional, old-school XmlSerializer methods is that .Net requires the original assembly to be file-system accessible in order to reference for compilation into a “[foo].XmlSerializers.dll” assembly.  If you really didn’t know what these assemblies were (as I didn’t) before now, allow me to explain.  The XmlSerializer routines require strongly-typed interpretive classes that are Reflected out of the original assembly and built “on-the-fly” by the .Net CLR.  It cannot achieve this with assemblies that exist only in memory, as database-deployed assemblies are.  Therefore when CRM attempts to deserialize by calling these interpretive classes, they don’t exist—causing an “Object Is Not An Instance” exception.

Experienced developers know that these XmlSerializer assemblies don’t have to be built “on-the-fly”, and they can certainly be assembled and distributed in conjunction with the original assembly.  However, CRM doesn’t support a shared-assembly model for database deployment.  The assemblies could be deployed to disk or GAC, but again this isn’t compatible with CRM Online.

If you’re thinking, as I did, that perhaps the assemblies can be merged together with ILmerge and deployed together, think again.  ILmerge generates a new AssemblyId attribute for the combined assembly metadata—which is only a problem for a the “[foo].XmlSerializers.dll” assembly because it strictly validates the AssemblyId of the calling code; and it imprints on the original assembly.

Before I discovered WCF Data Contracts, I was desperate enough to output the generated code for the XmlSerializer assembly and inject it into my project.  This is as ugly and highly ill-advised as it is time-consuming.  So, if you’re considering XML configuration for CRM Plug-ins, I strongly recommend using WCF Data Contracts.

For more reading, see the MSDN Article on WCF Data Contracts.

Some tricky “gotchas” with using WCF Data Contracts for newbies:

  • The order of XML nodes must be alphabetically ordered by their declared Data Contract name; this is a deserialization optimization requirement, and can be massaged to a certain extent by using advanced Data Contract directives.
  • There are no deserialized “attributes” for XML nodes.  The only supported XML-node attributes are namespace declarations.  Every Data Member is its own node, and its contents become the deserialized value.