Serializing WCF Contract with Object Property - TechNet Articles - United States (English) - TechNet Wiki

Table of Contents

Introduction

This sample project is posted in reply to the forum post: http://social.msdn.microsoft.com/Forums/en-US/554a7c1c-cea7-4743-ad4f-7845da2e9683/sending-xml-string-to-generated-webservice-proxy-stub.

WCF Service
The issue stems with how to handle contracts with object type properties:

[System.Runtime.Serialization.DataMemberAttribute(IsRequired=true)]       
       public object richPresenceField {
           get {
               return this.richPresenceFieldField;
           }
           set {
               if ((object.ReferenceEquals(this.richPresenceFieldField, value) != true)) {
                   this.richPresenceFieldField = value;
                   this.RaisePropertyChanged("richPresenceField");
               }
           }
       }

To illustrate, I created a WCF Service using the Visual Studio template and then modified the default service to return the contract provided in the forum post.  The class setPresence contains a property that has the richPresenceField shown above.

[ServiceContract]
public interface IService1
{
    [OperationContract]
    string GetData(int value);
[ServiceContract]
public     [OperationContract]
    setPresence GetPresence(setPresence composite);       
}

I wanted my implementation to be a little interesting so I added a couple of changes.  In general, the service just echos what it receives but I wanted to add a class that was included in the original contract and introduce a new contract that I manually added to the service.

public setPresence GetPresence(setPresence presence)
{
    if (presence == null)
    {
        throw new ArgumentNullException("setPresence");
    }
   
    if (presence.presenceInfo.richPresence is string)
    {
        // if a string is sent then try to parse and return as
        // an int to illustrate that the type can change
        int value;
        if (int.TryParse(((string) presence.presenceInfo.richPresence), out value))
            presence.presenceInfo.richPresence = value;
        // return out a new setPresence
        if(value==4)
            presence.presenceInfo.richPresence = new setPresence { expiration = 4, presenceType = "test presence" };
        // return out a new MyClass
        if (value == 5)
            presence.presenceInfo.richPresence = new MyOtherClass
                {
                    AnIntWithAnotherName = 3,
                    MyStringCollection = new string[] {"avalue", "and another"}
                };
    }
   
    return presence;
}


WCF Client

The proxy was initially generated in a normal console app using Add Service Reference.  This created the proxy as expected and added the following known types to the proxy (note the service was added as funnyservice so the namespace was created as FunnyClient.funnyservice):
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "4.0.0.0")]
[System.Runtime.Serialization.DataContractAttribute(Name="PresenceInfoType", Namespace="http://schemas.datacontract.org/2004/07/FunnySerialisation")]
[System.SerializableAttribute()]
[System.Runtime.Serialization.KnownTypeAttribute(typeof(FunnyClient.funnyservice.setPresence))]
[System.Runtime.Serialization.KnownTypeAttribute(typeof(System.MulticastDelegate))]
[System.Runtime.Serialization.KnownTypeAttribute(typeof(System.Delegate))]
[System.Runtime.Serialization.KnownTypeAttribute(typeof(System.ComponentModel.PropertyChangedEventHandler))]   
public partial class PresenceInfoType : object, System.Runtime.Serialization.IExtensibleDataan>
[System.Runtime.Serialization.KnownTypeAttribute(typeof(System.Delegate))]
[System.Runtime.Serialization.KnownTypeAttribute(

In my client I created a little helper method that returns an setPresence object that contains the object I am trying to verify:

private static setPresence BuildSimplePresence(object content)
{
    return new setPresence
        {
            expirationField = 100,
            presenceInfoField = new PresenceInfoType
                {
                    basicPresenceField = "basic",
                    richPresenceField = content
                },
            presenceTypeField = "a type of presence"
        };
}


Initial observations

Without making any changes to the proxy or service contracts, the datacontract serializer will allow us to send the richPresenceField as a base type (e.g., int, string, etc) or to a setPresence object.  This is shown and validated in the statements below (Note, I validated the result in the debugger):
var presence = client.GetPresence(BuildSimplePresence("some simple content that will be encoded <here> and <there>."));
presence = client.GetPresence(BuildSimplePresence("12202"));
presence = client.GetPresence(BuildSimplePresence(new setPresence { expirationField = 1 } ));
presence = client.GetPresence(BuildSimplePresence("4"));
presence = client.GetPresence(BuildSimplePresence("5"));


Adding a new class

I wanted to do something a little more interesting that hopefully illustrates the disconnect between client and server and provide some insight into serialization.  In my service I added a new class (Note how the class name and first member name do not match the contract name):
presence = client.GetPresence(BuildSimplePresence("4"));
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "4.0.30319.18046")]
[System.SerializableAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Runtime.Serialization.DataContractAttribute(Name = "PresenceInfoType", Namespace = "http://schemas.datacontract.org/2004/07/FunnySerialisation")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "urn:somecompany:presence:soap", TypeName = "MyClass")]   
public class MyOtherClass
{
    /// <remarks/>
    [System.Xml.Serialization.XmlElementAttribute(Order = 0, ElementName = "MyIntValue")]
    [System.Runtime.Serialization.DataMember()]
    public int AnIntWithAnotherName { get; set; }
    [System.Xml.Serialization.XmlElementAttribute(Order = 0)]
    [System.Runtime.Serialization.DataMember()]
    public string[] MyStringCollection { get; set; }
}

I also had to inform datacontractserializer to expect these type of objects by inserting the knowntypeattribute:

[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "4.0.30319.18046")]
[System.SerializableAttribute()]   
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(Namespace = "urn:somecompany:presence:soap")]
[System.Runtime.Serialization.KnownTypeAttribute(typeof(MyOtherClass))]
public partial class PresenceInfoType : object, System.ComponentModel.INotifyPropertyChanged

Without updating the proxy, I then manually added a class that has the same contract definition but different class structure:

[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "4.0.30319.18046")]
[System.SerializableAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "urn:somecompany:presence:soap")]
[System.Runtime.Serialization.DataContractAttribute(Name = "PresenceInfoType", Namespace = "http://schemas.datacontract.org/2004/07/FunnySerialisation")]
public class MyClass
{
    /// <remarks/>
    [System.Xml.Serialization.XmlElementAttribute(Order = 0, Namespace = "urn:somecompany:presence:soap")]
    [System.Runtime.Serialization.DataMember()]
    public int MyIntValue { get; set; }
    [System.Xml.Serialization.XmlElementAttribute(Order = 0, Namespace = "urn:somecompany:presence:soap")]
    [System.Runtime.Serialization.DataMember()]       
    public string[] MyStringCollection { get; set; }
}

And, I need to let the proxy (Reference.cs) know about this  new type:

[System.Diagnostics.DebuggerStepThroughAttribute()]e.Serialization.DataMember()]       
    public string[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "4.0.0.0")]
[System.Runtime.Serialization.DataContractAttribute(Name="PresenceInfoType", Namespace="http://schemas.datacontract.org/2004/07/FunnySerialisation")]
[System.SerializableAttribute()]
[System.Runtime.Serialization.KnownTypeAttribute(typeof(FunnyClient.funnyservice.setPresence))]
[System.Runtime.Serialization.KnownTypeAttribute(typeof(System.MulticastDelegate))]
[System.Runtime.Serialization.KnownTypeAttribute(typeof(System.Delegate))]
[System.Runtime.Serialization.KnownTypeAttribute(typeof(System.ComponentModel.PropertyChangedEventHandler))]
[System.Runtime.Serialization.KnownTypeAttribute(typeof(MyClass))]   
public partial class PresenceInfoType : object, System.Runtime.Serialization.IExtensibleDataObject, System.ComponentModel.INotifyPropertyChanged {

This then allows for the following message to be sent (and echoed):

presence = client.GetPresence(BuildSimplePresence(new MyClass { MyIntValue = 4, MyStringCollection = new string[] { "test1", "test2" }}));

Hope this is helpful.