Partilhar via


Data Contract Equivalence

For a client to successfully send data of a certain type to a service, or a service to successfully send data to a client, the sent type does not necessarily have to exist on the receiving end. The only requirement is that the data contracts of both types be equivalent. (Sometimes, strict equivalence is not required, as discussed in Data Contract Versioning.)

For data contracts to be equivalent, they must have the same namespace and name. Additionally, each data member on one side must have an equivalent data member on the other side.

For data members to be equivalent, they must have the same name. Additionally, they must represent the same type of data; that is, their data contracts must be equivalent.

Note

Note that data contract names and namespaces, as well as data member names, are case-sensitive.

For more information about data contract names and namespaces, as well as data member names, see Data Contract Names.

If two types exist on the same side (sender or receiver) and their data contracts are not equivalent (for example, they have different data members), you should not give them the same name and namespace. Doing so may cause exceptions to be thrown.

The data contracts for the following types are equivalent:

<DataContract()> _
Public Class Customer

    <DataMember()> _
    Public fullName As String

    <DataMember()> _
    Public telephoneNumber As String
End Class

<DataContract(Name:="Customer")> _
    Public Class Person

    <DataMember(Name:="fullName")> _
    Private nameOfPerson As String

    Private address As String

    <DataMember(Name:="telephoneNumber")> _
    Private phoneNumber As String
End Class
[DataContract]
public class Customer
{
    [DataMember]
    public string fullName;

    [DataMember]
    public string telephoneNumber;
}

[DataContract(Name = "Customer")]
public class Person
{
    [DataMember(Name = "fullName")]
    private string nameOfPerson;

    private string address;

    [DataMember(Name = "telephoneNumber")]
    private string phoneNumber;
}

Data Member Order and Data Contract equivalence

Using the Order property of the DataMemberAttribute class may affect data contract equivalence. The data contracts must have members that appear in the same order to be equivalent. The default order is alphabetical. For more information, see Data Member Order.

For example, the following code results in equivalent data contracts.

<DataContract(Name := "Coordinates")> _
Public Class Coords1
    <DataMember()> _
    Public X As Integer
    <DataMember()> _
    Public Y As Integer
    ' Order is alphabetical (X,Y).
End Class

<DataContract(Name := "Coordinates")> _
Public Class Coords2

    <DataMember()> _
    Public Y As Integer
    <DataMember()> _
    Public X As Integer
    ' Order is alphabetical (X,Y), equivalent 
    ' to the preceding code.
End Class

<DataContract(Name := "Coordinates")> _
Public Class Coords3
    <DataMember(Order := 2)> _
    Public Y As Integer
    <DataMember(Order := 1)> _
    Public X As Integer
    ' Order is according to the Order property (X,Y), 
    ' equivalent to the preceding code.
End Class
[DataContract(Name = "Coordinates")]
public class Coords1
{
    [DataMember]
    public int X;
    [DataMember]
    public int Y;
    // Order is alphabetical (X,Y).
}

[DataContract(Name = "Coordinates")]
public class Coords2
{
    [DataMember]
    public int Y;
    [DataMember]
    public int X;
    // Order is alphabetical (X,Y), equivalent 
    // to the preceding code.
}

[DataContract(Name = "Coordinates")]
public class Coords3
{
    [DataMember(Order = 2)]
    public int Y;
    [DataMember(Order = 1)]
    public int X;
    // Order is according to the Order property (X,Y), 
    // equivalent to the preceding code.
}

However, the following does not result in an equivalent data contract.

<DataContract(Name := "Coordinates")> _
Public Class Coords4

    <DataMember(Order := 1)> _
    Public Y As Integer
    <DataMember(Order := 2)> _
    Public X As Integer
    ' Order is according to the Order property (Y,X), 
    ' different from the preceding code.
End Class
[DataContract(Name = "Coordinates")]
public class Coords4
{
    [DataMember(Order = 1)]
    public int Y;
    [DataMember(Order = 2)]
    public int X;
    // Order is according to the Order property (Y,X), 
    // different from the preceding code.
}

Inheritance, Interfaces, and Data Contract Equivalence

When determining equivalence, a data contract that inherits from another data contract is treated as if it is just one data contract that includes all of the data members from the base type. Keep in mind that the order of the data members must match and that base type members precede derived type members in the order. Furthermore, if, as in the following code example, two data members have the same order value, the ordering for those data members is alphabetical. For more information, see Data Member Order.

In the following example, the data contract for type Employee is equivalent to the data contract for the type Worker.

<DataContract()> _
Public Class Person
    <DataMember()> Public name As String
End Class

<DataContract()> _
Public Class Employee
    Inherits Person
    <DataMember()> Public department As Integer
    <DataMember()> Public title As String
    <DataMember()> Public salary As Integer
End class 

' Order is "name", "department", "salary", "title" 
' (base class first, then alphabetical).

<DataContract(Name:="Employee")> _
Public Class Worker

    <DataMember(Order := 1)> _
    Public name As String
    <DataMember(Order := 2)> _
    Public department As Integer
    <DataMember(Order := 2)> _
    Public title As String
    <DataMember(Order := 2)> _
    Public salary As Integer
End Class
' Order is "name", "department", "salary", "title" 
' (Order=1 first, then Order=2 in alphabetical order), 
' which is equivalent to the Employee order}.
[DataContract]
public class Person
{
    [DataMember]
    public string name;
}
[DataContract]
public class Employee : Person
{
    [DataMember]
    public int department;
    [DataMember]
    public string title;
    [DataMember]
    public int salary;
}
// Order is "name", "department", "salary", "title" 
// (base class first, then alphabetical).

[DataContract(Name = "Employee")]
public class Worker
{
    [DataMember(Order = 1)]
    public string name;
    [DataMember(Order = 2)]
    public int department;
    [DataMember(Order = 2)]
    public string title;
    [DataMember(Order = 2)]
    public int salary;
}
// Order is "name", "department", "salary", "title" 
// (Order=1 first, then Order=2 in alphabetical order), 
// which is equivalent to the Employee order}.

When passing parameters and return values between a client and a service, a data contract from a base class cannot be sent when the receiving endpoint expects a data contract from a derived class. This is in accordance with object-oriented programming tenets. In the previous example, an object of type Person cannot be sent when an Employee is expected.

A data contract from a derived class can be sent when a data contract from a base class is expected, but only if the receiving endpoint "knows" of the derived type using the KnownTypeAttribute. For more information, see Data Contract Known Types. In the previous example, an object of type Employee can be sent when a Person is expected, but only if the receiver code employs the KnownTypeAttribute to include it in the list of known types.

When passing parameters and return values between applications, if the expected type is an interface, it is equivalent to the expected type being of type Object. Because every type ultimately derives from Object, every data contract ultimately derives from the data contract for Object. Thus, any data contract type can be passed when an interface is expected. Additional steps are required to successfully work with interfaces; for more information, see Data Contract Known Types.

See Also

Reference

DataContractAttribute
DataMemberAttribute

Concepts

Data Member Order
Data Contract Known Types
Data Contract Names