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
)]
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;
}
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))]
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"
};
}
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(
new
setPresence { 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"
)]
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.