(Nearly) complete MS-CRM entity serialization sample

[I’m finally getting around to formatting this thing so it’s readable and changing the broken links]

This example assumes that you've created correct XSD from the MS-CRM metadata (see https://blogs.msdn.com/mikemill/archive/2004/12/01/273254.aspx) and that you've run that XSD through the XSD Object Generator. The one change you should making prior to creating classes is to change the tns:dateTimeType to derive from xsd:string instead of xsd:dateTime, otherwise you'll end up serializing dates that the platform can't really cope with.

With a few simple tweaks to the XSD generator you can create XSD that the VS XSD.EXE tool can use. The way I've done this is to change the generated xsd:complexType to be the entity name followed by 'Type' and to create xsd:elements that reference the complexType.

(Apologies in advance if the formatting here is off, I'm still working my way around the .Text editor).

using System;

using System.Text;

using System.Web.Services;

using System.Runtime.Serialization;

using System.Xml.Serialization;

using System.IO;

using System.Globalization;

using Microsoft.Crm.Entities;

using Microsoft.Crm.Platform.Proxy;

namespace Cheerios

{

    class SerializerSample

    {

        /// <summary>

        /// Returns a string (XML) representation of a serializable

        /// entity. The namespaces are removed because the CRM

        /// platform deserialization code won't cope welll with them.

        /// </summary>

        /// <param name="o">The instance to serialize</param>

        /// <returns></returns>

        public static string ToString(object o)

        {

            StringBuilder sb = new StringBuilder();

            XmlSerializer serializer = new XmlSerializer(o.GetType());

            XmlSerializerNamespaces ns = new XmlSerializerNamespaces();

            ns.Add("", ""); TextWriter writer = new StringWriter(sb);

            serializer.Serialize(writer, o, ns); return sb.ToString();

        }

        /// <summary>

        /// Returns an instance of a object created from the supplied

        /// XML. You must cast the return value to the correct class.

        /// </summary>

        /// <param name="t"></param>

        /// <param name="s"></param>

        /// <returns></returns>

        public static object ToObject(Type t, string s)

        {

            XmlSerializer serializer = new XmlSerializer(t);

            TextReader reader = new StringReader(s);

            return serializer.Deserialize(reader);

        }

        /// <summary> /// The main entry point for the application.

        /// </summary>

        [STAThread]

        static void Main(string[] args)

        {

            // create the service proxies

            BizUser userService = new BizUser();

            userService.Url = "https://paint/mscrmservices/bizuser.srf";

            userService.Credentials = System.Net.CredentialCache.DefaultCredentials;

           

            CRMContact contactService = new CRMContact();

            contactService.Url = "https://paint/mscrmservices/crmcontact.srf";

            contactService.Credentials = System.Net.CredentialCache.DefaultCredentials;

            // get our credentials from the platform

            CUserAuth userAuth = userService.WhoAmI();

            // create an contact object in "client-space" and set some properties

            contact theContact = new contact();

            // the CRM platform wants to see dates like YYYY-MM-DDTHH:MM:SS

            theContact.birthdate.Value =

                DateTime.UtcNow.ToString(CultureInfo.InvariantCulture.DateTimeFormat.SortableDateTimePattern);

            // set some other properties on the object

            theContact.customertypecode.Value = 6;

            theContact.creditlimit.Value = 123456.0F;

            theContact.creditonhold.Value = true;

            theContact.firstname = "Bob";

           theContact.lastname = "Jones";

            theContact.description = "This is my sample contact....";

            // set up the ownership information because the platform won't

            // automatically do that for you

            theContact.ownerid.Value = userAuth.UserId;

            theContact.ownerid.type = 8;

            // turn it into XML

            string initialContactXml = SerializerSample.ToString(theContact);

            Console.WriteLine("Created contact XML\n{0}\n", initialContactXml);

            // stuff in into the platform and get the new one back

            string updatedContactXml =

                contactService.CreateAndRetrieve(userAuth, initialContactXml);

            Console.WriteLine("Retrieved contact XML\n{0}\n", updatedContactXml);

            // turn the new one into an object

            theContact = (contact)ToObject(typeof(contact), updatedContactXml);

            Console.WriteLine("Serialized new contact to\n{0}\n", SerializerSample.ToString(theContact));

            Console.WriteLine("Hit <return> to continue"); Console.ReadLine();

        }

    }

}

The XML generated from the first serialization (from theContact to initialContactXml) looks like the following. This string can be passed directly to the MS-CRM platform as the ContactXml parameter.

<?xml version="1.0" encoding="utf-16"?>

<contact>

    <birthdate>2004-09-10T16:40:54</birthdate>

    <creditlimit>123456</creditlimit>

    <creditonhold>true</creditonhold>

    <customertypecode>6</customertypecode>

    <description>This is my sample contact....</description>

    <firstname>Bob</firstname>

    <lastname>Jones</lastname>

    <ownerid type="8">{9CBA0E1D-882F-4A39-9441-B4FA3BA14724}</ownerid>

</contact>

The call to CreateAndRetrieve returns the following XML (note, I've formatted this a bit so that it's readable). Notice the date, boolean, and lookup elements have extra XML attributes automatically. These will be mapped to corresponding values in the supporting C# types. One thing to notice here is that the platform XML doesn't include the processing instruction. You can assume that the platform will return UTF-8 formatted XML.

<?xml version="1.0" encoding="utf-16"?>

<contact>

    <contactid>{CD303B40-B8E5-4B84-ABFF-A2DA4C6D9E35}</contactid>

    <customertypecode>6</customertypecode>

    <owningbusinessunit>{7DE28647-59CA-4C95-B7A3-FA7E31CD6DA8}</owningbusinessunit>

    <participatesinworkflow>0</participatesinworkflow>

    <firstname>Bob</firstname>

    <lastname>Jones</lastname>

    <fullname>Jones, Bob</fullname>

    <birthdate date="09/10/2004" time="4:40 PM">2004-09-10T16:40:54-07:00</birthdate>

    <description>This is my sample contact....</description>

    <creditlimit>123456</creditlimit>

    <createdon date="09/10/2004" time="9:40 AM">2004-09-10T09:40:32-07:00</createdon>

    <creditonhold name="Yes">1</creditonhold>

    <createdby name="Miller, Michaeljon" dsc="0">{9CBA0E1D-882F-4A39-9441-B4FA3BA14724}</createdby>

    <modifiedon date="09/10/2004" time="9:40 AM">2004-09-10T09:40:32-07:00</modifiedon>

    <modifiedby name="Miller, Michaeljon" dsc="0">{9CBA0E1D-882F-4A39-9441-B4FA3BA14724}</modifiedby>

    <statecode name="Active">0</statecode>

    <statuscode name="Active">1</statuscode>

    <address1_addressid>{B315C1B1-ACFF-4438-97B5-FF9EA34A681F}</address1_addressid>

    <address2_addressid>{80FB9B47-4F68-44B1-A619-402F4AD94061}</address2_addressid>

    <ownerid name="Miller, Michaeljon" dsc="0" type="8">{9CBA0E1D-882F-4A39-9441-B4FA3BA14724}</ownerid>

</contact>

Finally we can take the returned platform XML and convert it into a class. For this example I'm simply turning the class back into an XML string, but you can use that resulting object like you would any object. It's also quite possible to create collection classes and use the bulk retrieve operations to hold collections of CRM "objects".

<?xml version="1.0" encoding="utf-16"?>

<contact>

    <address1_addressid>{2025B5AA-BAD7-4816-9406-BEF62BA89358}</address1_addressid>

    <address2_addressid>{5135ECBE-56E3-41A7-99F8-7ACC3C3B9FDA}</address2_addressid>

    <birthdate date="09/10/2004" time="4:50 PM">2004-09-10T16:50:08-07:00</birthdate>

    <contactid>{012014CF-350C-42CE-90D1-533A222CEE0A}</contactid>

    <createdby name="Miller, Michaeljon" dsc="0">{9CBA0E1D-882F-4A39-9441-B4FA3BA14724}</createdby>

    <createdon date="09/10/2004" time="9:49 AM">2004-09-10T09:49:55-07:00</createdon>

    <creditlimit>123456</creditlimit>

    <creditonhold name="Yes">true</creditonhold>

    <customertypecode>6</customertypecode>

    <description>This is my sample contact....</description>

    <firstname>Bob</firstname>

    <fullname>Jones, Bob</fullname>

    <lastname>Jones</lastname>

    <modifiedby name="Miller, Michaeljon" dsc="0">{9CBA0E1D-882F-4A39-9441-B4FA3BA14724}</modifiedby>

    <modifiedon date="09/10/2004" time="9:49 AM">2004-09-10T09:49:55-07:00</modifiedon>

    <ownerid name="Miller, Michaeljon" type="8" dsc="0">{9CBA0E1D-882F-4A39-9441-B4FA3BA14724}</ownerid>

    <owningbusinessunit>{7DE28647-59CA-4C95-B7A3-FA7E31CD6DA8}</owningbusinessunit>

    <participatesinworkflow>false</participatesinworkflow>

    <statecode name="Active">0</statecode>

    <statuscode name="Active">1</statuscode>

</contact>

I hope this helps developers better understand the relationship between the MS-CRM XML and .Net classes.