Two data contracts are considered
equivalent if they have the same wire representation—that is, if they have the
same schema. This can be the case if they define the same type (but not
necessarily the same version of the type) or if the two data contracts
refer to two different types with the same data contract and data member
names. Equivalent data contracts are interchangeable: WCF will let any
service that was defined with one data contract operate with an equivalent
data contract.The most common way of defining an equivalent data contract is to
use the DataContract and DataMember attributes’
Name properties to map one data
contract to another. In the case of the DataContract attribute, the Name property defaults to the type’s name, so
these two definitions are identical:
[DataContract]
struct Contact
{...}
[DataContract(Name = "Contact")]
struct Contact
{...}
In fact, the full name of the data contract always includes its
namespace as well, but as you have seen, you can assign a different
namespace.
In the case of the DataMember
attribute, the Name property defaults
to the member name, so these two definitions are identical:
[DataMember]
string FirstName;
[DataMember(Name = "FirstName")]
string FirstName;
By assigning different names to the data contract and data members,
you can generate an equivalent data contract from a different type. For
example, these two data contracts are equivalent:
[DataContract]
struct Contact
{
[DataMember]
public string FirstName;
[DataMember]
public string LastName;
}
[DataContract(Name = "Contact")]
struct Person
{
[DataMember(Name = "FirstName")]
public string Name;
[DataMember(Name = "LastName")]
public string Surname;
}
In addition to having identical names, the types of the data members
have to match.
Note: A class and a structure that support the same data contract are
interchangeable.
Serialization Order
In classic .NET, a subclass can define a member of the
same name and type as a private member of its base class and, in turn,
its own subclass can do the same:
class A
{
string Name;
}
class B : A
{
string Name;
}
class C : B
{
string Name;
}
If the class hierarchy is also a data contract, this presents a
problem when serializing an instance of the subclass into a message,
since the message will contain multiple copies of a data member with the
same name and type. To distinguish between them, WCF places the data
members in the message in a particular order.
The default serialization order inside a type is simply
alphabetical, and across a class hierarchy the order is top-down. In
case of a mismatch in the serialization order, the members will be
initialized to their default values. For example, when serializing a
Customer instance defined as:
[DataContract]
class Contact
{
[DataMember]
public string FirstName;
[DataMember]
public string LastName;
}
[DataContract]
class Customer : Contact
{
[DataMember]
public int CustomerNumber;
}
the members will be serialized in the following order: FirstName, LastName, CustomerNumber.
Of course, equivalent data contracts must serialize and
deserialize their members in the same order. The problem now is that
combining a data contract hierarchy with aliasing contracts and members
might break the serialization order. For example, the following data
contract is not equivalent to the Customer data contract:
[DataContract(Name = "Customer")]
public class Person
{
[DataMember(Name = "FirstName")]
public string Name;
[DataMember(Name = "LastName")]
public string Surname;
[DataMember]
public int CustomerNumber;
}
because the serialization order is CustomerNumber, FirstName, LastName.
To resolve this conflict, you need to provide WCF with the order
of serialization by setting the Order
property of the DataMember attribute.
The value of the Order−1, meaning the default
WCF ordering, but you can assign it values indicating the required
order: property
defaults to
[DataContract(Name = "Customer")]
public class Person
{
[DataMember(Name = "FirstName",Order = 1)]
public string Name;
[DataMember(Name = "LastName",Order = 2)]
public string Surname;
[DataMember(Order = 3)]
public int CustomerNumber;
}
When renaming data members, you must take care to manually change
their order. Even without renaming, the sorting can quickly get out of
hand with a large number of data members. Fortunately, if another member
has the same value for its Order
property, WCF will order them alphabetically. You can take advantage of
this behavior by assigning the same number to all members coming from
the same level in the original class hierarchy or, better yet, simply
assigning them their levels in that hierarchy:
[DataContract(Name = "Customer")]
public class Person
{
[DataMember(Name = "FirstName",Order = 1)]
public string Name;
[DataMember(Name = "LastName",Order = 1)]
public string Surname;
[DataMember(Order = 2)]
public int CustomerNumber;
}