The Coding Hero You Deserve, Not The One You Need

Posted by on June 26, 2012  |  commentsComments (1)

Today's guest blogger is Rob Swafford, a Senior Developer at Sonoma Partners

Hero
We recently ran into an unusual situation on a client project for which it took a fair bit of research to ultimately track down the correct fix.  This particular client utilizes a number of internal web services with which different business units interact.  Most of these web services are standard SOAP-compatible services with exposed WSDLs that .NET is able to consume without a problem.  One in particular - that happened to be central to this project - was queue-based, and expected a packet of plain old XML (POX) to be dumped in its queue bucket as the request payload.  My first thought on tackling this service was to build up a series of C# objects that mirror the XML structure the service expects, decorate them appropriately with XmlAttribute and XmlElement attributes as needed, and then simply serialize the object to XML, passing the resulting string off to the service.

My team built up a console test client for the service at hand, worked through a few minor connection details, and soon we had XML packets being sent off to the queue for processing and were receiving the expected “yup, queue submission looks good” confirmation messages from the queue manager.  All should be good to go, let’s build this thing out into our CRM plugin and get ‘er done right?  Not so fast…

Where’s my Serializer?
When we deployed the plugin to fire off this web service call in CRM, we suddenly started getting a most unhelpful error message:

                “There was an error generating the XML Document.”

Doing a little debugging on the server turned up a stack trace that ended with a null reference exception:

XmlDocument error
at System.Xml.Serialization.XmlSerializer.Serialize(XmlWriter xmlWriter, Object o, XmlSerializerNamespaces namespaces, String encodingStyle, String id)
at System.Xml.Serialization.XmlSerializer.Serialize(TextWriter textWriter, Object o, XmlSerializerNamespaces namespaces)
at FooBarCo. Crm.Logic.Request.getXML() in C:\Users\jdoe\Desktop\FooBarCo\FooBarCo.Crm.Logic\Request.cs:line 31
at FooBarCo.Crm.Plugins.CreateRequestWebServicePlugin.Process(IServiceProvider serviceProvider, Dictionary`2 data, String rawData) in c:\Users\jdoe\Desktop\FooBarCo\FooBarCo.Crm.Plugins\CreateRequestWebServicePlugin.cs:line 100
at SonomaPartners.Xrm.Toolkit.Server.Plugins.Preconstructed.WebservicePlugin.Process(IServiceProvider serviceProvider, Entity entity, String entityNamespace, String data) in :line 0 

{"There was an error generating the XML document."} 

{"The type initializer for 'Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationWriterRequest' threw an exception."}

at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationWriterRequest..ctor()
at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializerContract.get_Writer()
at System.Xml.Serialization.TempAssembly.InvokeWriter(XmlMapping mapping, XmlWriter xmlWriter, Object o, XmlSerializerNamespaces namespaces, String encodingStyle, String id)
at System.Xml.Serialization.XmlSerializer.Serialize(XmlWriter xmlWriter, Object o, XmlSerializerNamespaces namespaces, String encodingStyle, String id) 
{"Object reference not set to an instance of an object."}

Ok…so the type initializer for our XmlSerializer object threw a null reference exception.  Seems a little strange, especially since I just tested this exact code in a console application and it works fine.  What gives?

A few internet searches later, I came across a post on StackOverflow talking about a similar error that occurs when you are doing dynamic code compilation and assembly loading, and the fix is to set a compiler flag to compile the assembly to disk instead of compiling to memory.  But we’re not doing dynamic compilation…or are we?  See that first line of the inner exception stack trace?  The one that starts “at Microsoft.Xml.Serialization.GeneratedAssembly…”  Yeah…that’s right, we’re generating an assembly at run-time, which means dynamic code compilation.  Under the covers it turns out that the XmlSerializer class does some pretty fancy reflection magic on whatever object(s) you’re serializing.  So how do we tell the compiler inside XmlSerializer to generate to disk?  We can’t.  But we can pre-generate a custom XmlSerializer for our class!

SGen to the Rescue!
I finally stumbled across this blog post by none other than Microsoft’s own CRM team that demonstrates how to improve your application’s performance with pre-generated XmlSerializers.  In a nutshell, you can use the sgen.exe tool provided with Visual Studio to pre-generate your XmlSerializer class and then ILMerge it into your plugin assembly.  The steps I followed were as follows:

  • Compile plugin assembly using generic XmlSerializer class
  • Run sgen.exe /t:FooBarCo.Crm.Logic.Request /a:FooBarCo.Crm.Plugins.dll from the Visual Studio Command Prompt
  • Copy the resulting FooBarCo.Crm.Logic.XmlSerializers.dll assembly into my shared Libraries folder and reference it in my Plugins project
  • Replace the reference to XmlSerializer with a reference to the new RequestSerializer object from the new assembly

Lastly, we added the new DLL assembly to our post-build ILMerge step, recompiled and merged everything together and deployed the newly updated Plugins assembly.  With the pre-compiled XmlSerializer assembly in place, CRM was finally able to send the XML request payloads required of our target web service.

Comments

  1. Nice! Thanks for taking the time to detail this. We are experiencing the EXACT same issue. I'll give it a shot.

    Posted by: Chris  |  Aug 22, 2013 1:47:43 PM

Post a Comment

  • *Required

Contact Us for a Quote, or Personalized Demonstrationof Microsoft Dynamics CRM for Your Business.

Contact Us