When you use a WCF ServiceContract you will want to supply custom Types for your Operations.
This custom types need to be robust for versioning, should supply collections and .Net types like string, int or enums.

This post is going to show you, how and why you want to use the DataContractAttribute. It will also reveal some tricks on how to apply good versioning techniques for your DataObjects, and how you can utilize collections in WCF.

Serialize Data for transfer

When you recall that WCF’s servicecontract with its definition maps to a WSDL you can imagine that you need to specify how your custom types need to be serialized to achieve this.

The flow of serialization and message sending in WCF is:

Serialization input --> message transfer --> deserialize input params --> 
Execute --> 
serialize out params --> return message to client --> deserialize out.

As you can tell this is THE performance bottleneck of WCF, because you have to serialize and deserialize the sent message on each side. Also remember that you can only transfer state of an object and not its behavior, because that can not be depicted in XML, and would go against WCF’s premise of service orienation and technological agnosticity (is that a word? maybe I ahem you should look it up). That is why this is called marshal by value (as transfer state from one location to another) opposed to marshal by ref in the case of .Net Remoting (.Net Components).

In .Net you have build in Serializers with Formatters that implement the IFormatter interface. Two of this are the BinaryFormatter and the SoapFormatter. Both use Reflection to capture the object graphs state.

The .Net formatter implementations serializes the .Net specifics like assemblyversion, type and namespace with the graph. Also the SerializeAbleAttribute makes all Members on a .Net class Serializeable and you need an opt out approach to mark non serializeable values. So to support service orientation (like WCF does) you will want another approach.

This is why WCF supports its own serviceoriented Formatter (the non abstract class):

public abstract class XmlObjectSerializer
{
public virtual object ReadObject(Stream stream);
public virtual object ReadObject(XmlReader reader);
public virtual void WriteObject(XmlWriter writer, object graph);
public void WriteObject(Stream stream, object graph);
}
public sealed class DataContractSerializer : XmlObjectSerializer
{
public DataContractSerializer(Type type);
// more members
}

This captures only the state of an object and does not implement IFormatter. It is also used autotmatically by WCF.

To supply a Data for exchange the Types that are used to deserialize must not match exactly, they only need to have an equivalent schema. That is they have result in the same soap xml. This means in essence, that besides the same name and namespace, the data type has to match. So the following definition results in an equivalent data scheme.

[Serializeable] // service side 
public class TypeForClient
{
public string Name;
public string FirstName;
}
[Serializeable] // client side
public class TypeForService
{
public string Name;
public string FirstName;
[NonSerializable]
public int Age;
}

From opt out to opt in – the DataContractAttribute

With the SerializeableAttribute we can see the opt out approach. This can be exchanged for with the DataContractAttribute from WCF, which not only uses an opt in approach but also lets you specify a lot more service oriented approaches than the SerializeAbleAttribute, as we will see in this post. Features of this are versioning, specifying the ordering in serialization and others.

[AttributeUsageAttribute(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum, 
Inherited = false, AllowMultiple = false)]
public sealed class DataContractAttribute : Attribute
{
public string Name{get;set;}
public string Namespace {get;set;}
public bool IsNameSetExplicitly { get; }
public bool IsNamespaceSetExplicitly { get; }
public bool IsReference { get; set; }
public bool IsReferenceSetExplicitly { get; }
public virtual object TypeId { get; }
}

When the DataContract is applied to a Type, it will not make all the members of this type serializeable. You need to mark the members as such, as is wished for by an opt in approach. This is done with the DataMemberAttribute.

[AttributeUsageAttribute(AttributeTargets.Property | AttributeTargets.Field, 
Inherited = false, AllowMultiple = false)]
public sealed class DataMemberAttribute : Attribute
{
public bool EmitDefaultValue { get; set; }
public bool IsNameSetExplicitly { get; }
public bool IsRequired { get; set; }
public string Name { get; set; }
public int Order { get; set; }
public virtual object TypeId { get; }
}

Beceause the Type with a Datacontract is serialized into a SOAP message, CLR concepts do not matter. This is why you can mix private and public members, fields, autoProperties and so on and so forth for your decorated Datamembers, and all with this Attribute are being serialized for the message.

[DataContract]
public class MyData
{
[DataMember]
public string Data{get;set;}
[DataMember]
string NotPrivateData
[DataMember]
internal string NotInternalData{get;}
private int myField;
[DataMember]
public int MyField
{
get {return myField;}
set { myField = value;}
}
}

Yet the imported types in Visual studio will always be autoproperties where DataMembers where used.
When you use a DataContract with properites, get and set accessors are a must or an exception is thrown at service load time.
One side can still use a DataObject that only uses the SerializeableAttribute. Remember it is only important that the schema matches.

DataContractAttribute Properties

Name, IsNameSetExplicitly
Defaults to Membername. Can be set explicitly (see versioning). IsNameSetExplicitly is the flag that indicates for the formatter if the Name is set.

Namespace, IsNamespaceSetExplicitly
Defines the namespace that is set in the SOAP Message, and will be imported by the client. IsNamespaceSetExplicitly is the flag that indicates for the formatter if the Namespace is set explicitly.

IsReference, IsReferenceSetExplicitly
Advises the DataContractSerializer to format an XML that preserves the object reference information. IsReferenceSetExplicitly sets a flag for said formatter.

DataMember Properties

EmitDefaultValue
If this is set to false, and the Member has its default value (null for reference types, default(T) where T struct for structs) it will not be serialized. This can help to reduce messagesize. This is opt in, cause the default is set to true.

Name, IsNameSetExplicitly
See DataContractAttribute Properties.

IsRequired
Used for versioning. Indicates that a responsemessage with a missing member that has this flag set to true, will throw an NetDispatchFaultException on Deserialization.

Order
Used to specify the order in which the members are serialized/deserialized. Serialization order is alphabetical, but can be steered with the Order attribute. When the Order has recurring values (e.g. two or more members have the same number for Order, it defaults to alphabetical ordering). You can use this to your advantage, to order all alphabetically, except for the one where you want to opt in.

Inferring DataContracts

Also possible but not adviseable is, that any public type that is used in a service call or as Member of a Data object can be inferred. Meaning that WCF will figure it how to serialize this Type without the DataContractAttribute.
It is not adviseable because you loose the ability for versioning and control of namespace/name, ordering and such.

Better is to be explicit as (most) always in programming (except when you explicitly state otherwise 😉 ). In the following example you see how you can aggregate custom Datatypes to use in WCF with explicitly applied DataContracts.

[DataContract]
class Data
{
[DataMember]
public ComplexMember Member{get;set;}
}
[DataContract]
class ComplexMember
{
[DataMember]
public string Value{get;set;}
}

 

Serialization Events

.Net supports events during serialization and so does WCF. To designate methods for WCF serialization you again use attributes.
The signature must comply to:

    void <MethodName>(StreamingContext context);

See following type that uses all of the supplied Attributes.

[DataContract]
class MyData
{
[OnSerializing]
void OnSerializing(StreamingContext context);
[OnSerialized]
void OnSerialized(StreamingContext context);
[OnDeserializing]
void OnSerializing(StreamingContext context);
[OnDeserialized]
void OnSerialized(StreamingContext context);
}

StreamingContext tells you why the serialization takes place, but can usually be ignored for WCF contracts.
Note that each attribute can only be used once in a given class!

Following the order of events:

    For Serialization
OnSerializing, Serialization, OnSerialized
For Deserialization
OnDeserializing, Deserialization, OnDeserialized

Note that during this chain of events WCF never calls the default ctor.
To replace your constructor semantivally you can use the deserializing event. To do this in an effective and efficient way for service method as for use in .Net, your class should have a default ctor and the Deserializing event call the same helper method.

[DataContract]
class MyData
{
public MyData()
{
Initialize();
}
[OnDeserializing]
public void OnDeserializing()
{
Initialize();
}
private void Initialize()
{
/* initialize fields here */
}
}

The Deserialized event can be used to regain any values that can only be initialized with your data filled. E.g. Connectionstrings or such.

With this overview over serialization we will proceed with the DataContract specifics on the next page. This are the handling of class hierarchies and its resolution, how versioning of entities can be handled and the use of collections in WCF.

Categories: Contracts

0 Comments

Leave a Reply

Avatar placeholder