Wednesday, August 18, 2010

WCF Client performance improvement

Recently I had to try various methods to overcome performance problems due to a large schema which represented the canonical model that is to be used in our SOA layer.

The biggest bottleneck was the initial call made to the service which used the model in its service contract. WCF generates serialization code on the fly when the first service request is sent – and due to the size and structure of the schema – the amount of generated code was large – 20mb!

The reason why the structure was an issue is because the model dictates that you use a generic message wrapper type which contains an element which allows you to specify the request and response type (basically in the XSD definition, this element contains several hundred choices that it could be – so when it’s deserialized – this is of type object – with several hundred XmlElementAttribute attributes above it as it could be any of these types specified in the XmlElementAttribute attributes. A truncated example of the deserialized code is below.

[System.Xml.Serialization.XmlElementAttribute("exampleRequest1", typeof(ExampleRequest1))] [System.Xml.Serialization.XmlElementAttribute("exampleResponse1", typeof(ExampleResponse1))]

[System.Xml.Serialization.XmlElementAttribute("exampleRequest200", typeof(ExampleRequest200))] [System.Xml.Serialization.XmlElementAttribute("exampleResponse200", typeof(ExampleResponse200))]
… you get the idea
public object Item {…


So the generated serialization code needs to handle every possible combination that could possibly be thrown at it (so it can handle the all the possible types that are declared in the XmlElement attributes). So in other words, a lot of if statements

To overcome the cost of generating the serialization code, I used the steps described in this MSDN article - http://msdn.microsoft.com/en-us/library/aa751883.aspx. I went with option 3: “Compile the generated serialization code into your application assembly and add the XmlSerializerAssemblyAttribute to the service contract that uses the XmlSerializerFormatAttribute. Do not set the AssemblyName or CodeBase properties. The default serialization assembly is assumed to be the current assembly”.

I used the following method to generate my assembly with the serialization code:

  • Add the XmlSerializerAssembly attribute and XmlSerializerFormat to my service contract (example below).

[ServiceContract(Name = "ITest", Namespace = "http://testnamespace"), XmlSerializerFormat]
[XmlSerializerAssemblyAttribute()]
public interface ITest
{

  • Compile my assembly
  • Run svcutil against my assembly – an example of the command line: svcutil C:\Source\Project\TestServiceContract \bin\TestServiceContract.dll /t:xmlserializer.
  • Copy the generated file that was produced in step 3 to the project folder of the assembly and include the generated file in the project.
  • Rebuild the assembly.
  • Done.
In the above example TestServiceContract.dll only contains the service contract definition, not the implementation. Using this approach to resolve the serialization cost means you have to share the service contract assembly with the client and service (meaning, the client must use the ChannelFactory) – so the service definition should live in its own assembly. Sharing the assembly means both the client and the service has access to the serialization code.

One option I looked at was the ServiceKnownType attribute – which is specific to the DataContractSerializer. The positives with the DataContractSerializer is it really is fast. So even with the below

[System.Xml.Serialization.XmlElementAttribute("exampleRequest1", typeof(ExampleRequest1))] [System.Xml.Serialization.XmlElementAttribute("exampleResponse1", typeof(ExampleResponse1))]

[System.Xml.Serialization.XmlElementAttribute("exampleRequest200", typeof(ExampleRequest200))] [System.Xml.Serialization.XmlElementAttribute("exampleResponse200", typeof(ExampleResponse200))]
you get the idea
public object Item {…


If I update my service contract to the following:
[ServiceContract(Name = "ITest", Namespace = "http://testnamespace"), DataContractFormat]
[ServiceKnownType(typeof(ExampleRequest1))]
[ServiceKnownType(typeof(ExampleResponse1))]
public interface ITest
{


The service performance is amazingly faster. In fact, even though I am still using the ChannelFactory, so the channel isn’t cached (I’ll get onto that later) – the cost of generating the channel is very cheap (I’m assuming since the amount of types that need to be reflected when creating the channel is considerably low since we have specified the types the Service is interested in by using ServiceKnownType).

However, since we build our services using the Contract First pattern – the DataContract serializer is not an option since the payload that is produced across the wire doesn’t resemble at all the original XSD – in other words you have no control on how the XML will look.

So the next bottleneck in performance was the generation of the channel. Since the ChannelFactory is being used, the channel is not cached; however if you are using a generated proxy class by svcutil, ClientBase caches its internal ChannelFactory. More details are here: http://blogs.msdn.com/b/wenlong/archive/2007/10/27/performance-improvement-of-wcf-client-proxy-creation-and-best-practices.aspx.

Again an assumption on my part for why the channel generation is costly in this example: in the client improvement article by Wenlong Dong, when the channel is created, all of the required CLR types are reflected, and because of the Item property in the example and all of the possible types it could be – there are a lot of the types that are reflected (which is where I believe ServiceKnownType mention before resolves this issue since the channel generation is fast, ServiceKnownType must supress the unneeded types that are not used by the service). I needed a solution like this using the XmlSerializer. And I worked it out using the below process

  1. Generate the XmlSerializer file against the service dll with all the required XmlElement attributes (i.e. many XmlElement attributes above the Item property).
  2. Copy the generated serialization file to the service folder; add it to the project etc…
  3. Remove all the XmlElement attributes for the Item property – and add [System.Xml.Serialization.XmlElementAttribute(Order = 0)]
  4. Rebuild the project that contains the CLR schema definitions.
  5. Latency is very low.
So the key with the above is the XmlSerializer code must be generated with the expected types for the Item property (Item property decorated with multiple XmlElement attributes). Doing so means you can remove all the expected types since the XmlSerializer can still handle serializing since it’s an expected type in the serialization code (since we generated the code with the required types in step 1).

So using the assumption for why the channel generation is so costly - the cost of generating the channel is now reduced as it doesn’t have to worry about reflecting as many types (which according to Wenlong, is the biggest cost) – however serialization is successful as I said before since we generated the serialization code is aware of these types.

Monday, August 9, 2010

Serializing with CDATA as the content of an element

Recently I came across the problem where I needed to serialize a property of type string whose content contained a CDATA element section - the content of the property/element was HTML markup. If you serialize the property using the XmlSerializer out of the box, you get the following example:

&lt ;![CDATA[&lt ;h3&gt ;Service Job 1561245&lt ;/h3&gt ;

The solution is to update the property type from string to XmlCDataSection. You can leave the field type to string (so that's descriptionField in the below example), but the get and set of the property needs to look something like this:


get
{
XmlDocument xmlDocument = new XmlDocument();
return xmlDocument.CreateCDataSection(descriptionField);
}
set
{
descriptionField = value.Value;
}

Sunday, August 8, 2010

Building blocks of SOA article

The following SOA Mag article uses a great analogy (Lego blocks) to describe the principles of SOA - http://www.soamag.com/I36/0210-1.php

Thursday, August 5, 2010

Great Provider model article

Had to implement the Provider model recently, this is a great article on how to do so using the .Net framework - http://dotnetslackers.com/articles/designpatterns/HowToWriteAProviderModel.aspx