Serialization issue when using CRM 2011 and WCF-generated proxies

(Note: I know this blog is ugly as sin at the moment. I’ll fix it all pretty when it gets enough traffic to warrant the time)

So for reasons known only to yourself you decide that you want to shun the CRM SDK and call the web services using proxies generated from the Organization WSDL. All rightey then!

Everything is great until you try retrieving all the attributes for a SystemUser entity when you receive the following error:
System.ServiceModel.Dispatcher.NetDispatcherFaultException : The formatter threw an exception while trying to deserialize the message: There was an error while trying to deserialize parameter http://schemas.microsoft.com/xrm/2011/Contracts/Services:RetrieveResult.
The InnerException message was 'Error in line 1 position 1879. Element 'http://schemas.datacontract.org/2004/07/System.Collections.Generic:value' contains data from a type that maps to the name 'http://schemas.microsoft.com/xrm/2011/Contracts:OptionSetValue'. The deserializer has no knowledge of any type that maps to this name.
Consider using a DataContractResolver or add the type corresponding to 'OptionSetValue' to the list of known types - for example, by using the KnownTypeAttribute attribute or by adding it to the list of known types passed to DataContractSerializer.'. Please see InnerException for more details. ----> System.Runtime.Serialization.SerializationException : Error in line 1 position 1879. Element 'http://schemas.datacontract.org/2004/07/System.Collections.Generic:value' contains data from a type that maps to the name 'http://schemas.microsoft.com/xrm/2011/Contracts:OptionSetValue'. The deserializer has no knowledge of any type that maps to this name. Consider using a DataContractResolver or add the type corresponding to 'OptionSetValue' to the list of known types - for example, by using the KnownTypeAttribute attribute or by adding it to the list of known types passed to DataContractSerializer.

What just happened?

A number of CRM entities use xsd:anyType to reference any type of object in the object graph. However WCF and specifically .Net’s serialization plubming needs to know of a type before it can serialize or deserialize it. For reasons I haven’t had time to investigate WCF does not generate the client-side Entity class with the necessary KnownType attributes to help the serializer/deserializer figure out which types to create.

You can solve this problem in a number of ways:

1. Edit the generated Entity class by hand and add the necessary attributes. This is not recommended since refreshing the service references will lose your changes. Months from now some poor maintenance developer will try to make things better by regenerating the classes but nothing will work. Hopefully he’ll read this blog and fix things but you never know.

2. You can whip up a custom DataContractResolver (http://msdn.microsoft.com/en-us/library/ee358759.aspx). This will make you feel like a rock star but no one reading your code afterwards will understand what you’re doing. You can solve the problem with much less hassle, see no 3.

3. Extend the partial class definition for Entity with the necessary attributes in a separate file. This is my recommendation. It is simple and easy and you can document the whole thing so that others can understand what you’re doing without the danger of someone trashing the required changes with a service update.

Here are my extra bits for the Entity class:

namespace CrmServices.CrmSdk
{
/// <summary>
/// This declaration extends the class declaration generated by SvcUtil.exe.
/// This is necessary since WCF does not generate the necessary attributes required to make serialization of the Entity class work.
/// </summary>
[System.Runtime.Serialization.KnownTypeAttribute(typeof(CrmServices.CrmSdk.AttributeCollection))]
[System.Runtime.Serialization.KnownTypeAttribute(typeof(CrmServices.CrmSdk.Money))]
[System.Runtime.Serialization.KnownTypeAttribute(typeof(CrmServices.CrmSdk.Money[]))]
[System.Runtime.Serialization.KnownTypeAttribute(typeof(CrmServices.CrmSdk.EntityReference))]
[System.Runtime.Serialization.KnownTypeAttribute(typeof(CrmServices.CrmSdk.EntityReference[]))]
[System.Runtime.Serialization.KnownTypeAttribute(typeof(CrmServices.CrmSdk.OptionSetValue))]
[System.Runtime.Serialization.KnownTypeAttribute(typeof(CrmServices.CrmSdk.OptionSetValue[]))]
// ...
// You get the idea
public partial class Entity
{
}
}

Hope this helps someone.
Chris

Posted in Uncategorized | 4 Comments