Table of Contents
- Introduction
- WCF Service The issue stems with how to handle contracts with object type properties:
- WCF Client
- Initialnt-6615">
Serializing WCF Contract with Object Property
observations- Adding a new class
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)]publicobjectrichPresenceField {get{returnthis.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]publicinterfaceIService1{[OperationContract]stringGetData(intvalue);[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.
publicsetPresence GetPresence(setPresence presence){if(presence ==null){thrownewArgumentNullException("setPresence");}if(presence.presenceInfo.richPresenceisstring){// if a string is sent then try to parse and return as// an int to illustrate that the type can changeintvalue;if(int.TryParse(((string) presence.presenceInfo.richPresence),outvalue))presence.presenceInfo.richPresence = value;// return out a new setPresenceif(value==4)presence.presenceInfo.richPresence =newsetPresence { expiration = 4, presenceType ="test presence"};// return out a new MyClassif(value == 5)presence.presenceInfo.richPresence =newMyOtherClass{AnIntWithAnotherName = 3,MyStringCollection =newstring[] {"avalue","and another"}};}returnpresence;}
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):
WCF Client[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))]publicpartialclassPresenceInfoType :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:
privatestaticsetPresence BuildSimplePresence(objectcontent){returnnewsetPresence{expirationField = 100,presenceInfoField =newPresenceInfoType{basicPresenceField ="basic",richPresenceField = content},presenceTypeField ="a type of presence"};}
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):
Initial observationsvar presence = client.GetPresence(BuildSimplePresence("some simple content that will be encoded <here> and <there>."));presence = client.GetPresence(BuildSimplePresence("12202"));presence = client.GetPresence(BuildSimplePresence(newsetPresence { expirationField = 1 } ));presence = client.GetPresence(BuildSimplePresence("4"));presence = client.GetPresence(BuildSimplePresence("5"));
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):
Adding a new classpresence = 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")]publicclassMyOtherClass{/// <remarks/>[System.Xml.Serialization.XmlElementAttribute(Order = 0, ElementName ="MyIntValue")][System.Runtime.Serialization.DataMember()]publicintAnIntWithAnotherName {get;set; }[System.Xml.Serialization.XmlElementAttribute(Order = 0)][System.Runtime.Serialization.DataMember()]publicstring[] 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))]publicpartialclassPresenceInfoType :object, System.ComponentModel.INotifyPropertyChangedWithout 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")]publicclassMyClass{/// <remarks/>[System.Xml.Serialization.XmlElementAttribute(Order = 0, Namespace ="urn:somecompany:presence:soap")][System.Runtime.Serialization.DataMember()]publicintMyIntValue {get;set; }[System.Xml.Serialization.XmlElementAttribute(Order = 0, Namespace ="urn:somecompany:presence:soap")][System.Runtime.Serialization.DataMember()]publicstring[] MyStringCollection {get;set; }}And, I need to let the proxy (Reference.cs) know about this new type:
[System.Diagnostics.DebuggerStepThroughAttribute()]e.Serialization.DataMember()]publicstring[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))]publicpartialclassPresenceInfoType :object, System.Runtime.Serialization.IExtensibleDataObject, System.ComponentModel.INotifyPropertyChanged {This then allows for the following message to be sent (and echoed):
presence = client.GetPresence(BuildSimplePresence(newMyClass { MyIntValue = 4, MyStringCollection =newstring[] {"test1","test2"}}));Hope this is helpful.