Usando contratos de dados

Um contrato de dados é um contrato formal entre um serviço e um cliente que descreve abstratamente os dados a serem trocados. Ou seja, para se comunicar, o cliente e o serviço não precisam compartilhar os mesmos tipos, apenas os mesmos contratos de dados. Um contrato de dados define precisamente, para cada parâmetro ou tipo de retorno, quais dados são serializados (transformados em XML) a serem trocados.

Noções básicas do contrato de dados

O WCF (Windows Communication Foundation) usa um mecanismo de serialização chamado Serializador de Contrato de Dados por padrão para serializar e desserializar dados (convertê-los de e para XML). Todos os tipos primitivos do .NET Framework, como inteiros e cadeias de caracteres, bem como determinados tipos tratados como primitivos, como DateTime e XmlElement, podem ser serializados sem nenhuma outra preparação e são considerados como tendo contratos de dados padrão. Muitos tipos de .NET Framework também têm contratos de dados existentes. Para obter uma lista completa de tipos serializáveis, consulte Tipos compatíveis com o Serializador de Contrato de Dados.

Novos tipos complexos que você cria precisam ter um contrato de dados definido para que sejam serializáveis. Por padrão, o DataContractSerializer infere o contrato de dados e serializa todos os tipos publicamente visíveis. Todas as propriedades públicas de leitura/gravação e campos do tipo são serializadas. Você pode recusar membros da serialização usando o IgnoreDataMemberAttribute. Você também pode criar explicitamente um contrato de dados usando atributos DataContractAttribute e DataMemberAttribute. Normalmente, isso é feito aplicando o atributo DataContractAttribute ao tipo. Esse atributo pode ser aplicado a classes, estruturas e enumerações. O atributo DataMemberAttribute precisa ser aplicado a cada membro do tipo de contrato de dados para indicar que ele é um membro de dados, ou seja, ele deve ser serializado. Para obter mais informações, consulte Tipos serializáveis.

Exemplo

O exemplo a seguir mostra um contrato de serviço (uma interface) ao qual os atributos ServiceContractAttribute e OperationContractAttribute foram aplicados explicitamente. O exemplo mostra que os tipos primitivos não exigem um contrato de dados, enquanto um tipo complexo exige.

[ServiceContract]
public interface ISampleInterface
{
    // No data contract is required since both the parameter
    // and return types are primitive types.
    [OperationContract]
    double SquareRoot(int root);

    // No Data Contract required because both parameter and return
    // types are marked with the SerializableAttribute attribute.
    [OperationContract]
    System.Drawing.Bitmap GetPicture(System.Uri pictureUri);

    // The MyTypes.PurchaseOrder is a complex type, and thus
    // requires a data contract.
    [OperationContract]
    bool ApprovePurchaseOrder(MyTypes.PurchaseOrder po);
}
<ServiceContract()> _
Public Interface ISampleInterface
    ' No data contract is required since both the parameter and return 
    ' types are both primitive types.
    <OperationContract()> _
    Function SquareRoot(ByVal root As Integer) As Double

    ' No Data Contract required because both parameter and return 
    ' types are marked with the SerializableAttribute attribute.
    <OperationContract()> _
    Function GetPicture(ByVal pictureUri As System.Uri) As System.Drawing.Bitmap

    ' The MyTypes.PurchaseOrder is a complex type, and thus 
    ' requires a data contract.
    <OperationContract()> _
    Function ApprovePurchaseOrder(ByVal po As MyTypes.PurchaseOrder) As Boolean
End Interface

O exemplo a seguir mostra como um contrato de dados para o tipo MyTypes.PurchaseOrder é criado aplicando os atributos DataContractAttribute e DataMemberAttribute à classe e aos seus membros.

namespace MyTypes
{
    [DataContract]
    public class PurchaseOrder
    {
        private int poId_value;

        // Apply the DataMemberAttribute to the property.
        [DataMember]
        public int PurchaseOrderId
        {

            get { return poId_value; }
            set { poId_value = value; }
        }
    }
}
Namespace MyTypes
    <System.Runtime.Serialization.DataContractAttribute()> _
    Public Class PurchaseOrder
        Private poId_value As Integer

        ' Apply the DataMemberAttribute to the property.

        <DataMember()> _
        Public Property PurchaseOrderId() As Integer

            Get
                Return poId_value
            End Get
            Set
                poId_value = value
            End Set
        End Property
    End Class
End Namespace

Observações

As anotações a seguir fornecem itens a serem considerados ao criar contratos de dados:

  • O atributo IgnoreDataMemberAttribute só é honrado quando usado com tipos não marcados. Isso inclui tipos que não são marcados com um dos atributos DataContractAttribute, SerializableAttribute, CollectionDataContractAttribute ou EnumMemberAttribute marcados como serializáveis por qualquer outro meio (como IXmlSerializable).

  • Você pode aplicar o atributo DataMemberAttribute a campos e propriedades.

  • Os níveis de acessibilidade do membro (interno, privado, protegido ou público) não afetam o contrato de dados de forma alguma.

  • O atributo DataMemberAttribute será ignorado se for aplicado a membros estáticos.

  • Durante a serialização, o código de obtenção de propriedade é chamado para que os membros de dados de propriedade obtenham o valor das propriedades a serem serializadas.

  • Durante a desserialização, um objeto não inicializado é criado pela primeira vez, sem chamar construtores no tipo. Em seguida, todos os membros de dados são desserializados.

  • Durante a desserialização, o código do conjunto de propriedades é chamado para que os membros de dados de propriedade definam as propriedades como o valor sendo desserializado.

  • Para que um contrato de dados seja válido, a serialização de todos os seus membros de dados precisa ser possível. Para obter uma lista completa de tipos serializáveis, consulte Tipos compatíveis com o Serializador de Contrato de Dados.

    Tipos genéricos são tratados exatamente da mesma forma que tipos não genéricos. Não há requisitos especiais para parâmetros genéricos. Por exemplo, considere o seguinte XML.

[DataContract]
public class MyGenericType1<T>
{
    // Code not shown.
}
<DataContract()> _
Public Class MyGenericType1(Of T)
    ' Code not shown.
End Class

Esse tipo é serializável se o tipo usado para o parâmetro de tipo genérico (T) é serializável ou não. Como precisa ser possível serializar todos os membros de dados, o tipo a seguir só será serializável se o parâmetro de tipo genérico também for serializável, conforme mostrado no código a seguir.

[DataContract]
public class MyGenericType2<T>
{
    [DataMember]
    T theData;
}
<DataContract()> _
Public Class MyGenericType2(Of T)
    <DataMember()> _
    Dim theData As T
End Class

Para obter um exemplo de código completo de um serviço WCF que define um contrato de dados, consulte o exemplo de Contrato de Dados Básico.

Confira também