Nomes de contrato de dados

Às vezes, um cliente e um serviço não compartilham os mesmos tipos. Eles ainda podem passar dados uns para os outros, desde que os contratos de dados sejam equivalentes em ambos os lados. A Equivalência de Contrato de Dados é baseada em nomes de contrato de dados e de membro de dados e, portanto, um mecanismo é fornecido para mapear os tipos e membros para esses nomes. Este tópico explica as regras de nomenclatura de contratos de dados, bem como o comportamento padrão da infraestrutura WCF (Windows Communication Foundation) ao criar nomes.

Regras básicas

As regras básicas sobre a nomenclatura de contratos de dados incluem:

  • Um nome de contrato de dados totalmente qualificado consiste em um namespace e um nome.

  • Os membros de dados têm apenas nomes, mas não têm namespaces.

  • Ao processar contratos de dados, a infraestrutura WCF diferencia maiúsculas de minúsculas para os namespaces e os nomes de contratos de dados e de membros de dados.

Namespaces de Contrato de Dados

Um namespace de contrato de dados assume a forma de um URI (Uniform Resource Identifier). A URI pode ser absoluto ou relativo. Por padrão, os contratos de dados para um tipo específico recebem um namespace proveniente do namespace CLR (Common Language Runtime) desse tipo.

Por padrão, qualquer namespace CLR fornecido (no formato Clr.Namespace) é mapeado para o namespace http://schemas.datacontract.org/2004/07/Clr.Namespace. Para substituir esse padrão, aplique o atributo ContractNamespaceAttribute a todo o módulo ou assembly. Como alternativa, para controlar o namespace de contrato de dados para cada tipo, defina a propriedade Namespace do DataContractAttribute.

Observação

O namespace http://schemas.microsoft.com/2003/10/Serialization é reservado e não pode ser usado como namespace de contrato de dados.

Observação

Você não pode substituir o namespace padrão nos tipos de contrato de dados que contêm declarações delegate.

Nomes de contrato de dados

O nome padrão de um contrato de dados para determinado tipo é o nome desse tipo. Para substituir o padrão, defina a propriedade Name do DataContractAttribute como nome alternativo. As regras especiais para tipos genéricos são descritas em "Nomes de Contrato de Dados para Tipos Genéricos" mais adiante neste tópico.

Nomes de Membro de Dados

O nome padrão de um membro de dados para determinado campo ou propriedade é o nome desse campo ou propriedade. Para substituir o padrão, defina a propriedade Name do DataMemberAttribute como valor alternativo.

Exemplos

O exemplo a seguir mostra como você pode substituir o comportamento de nomenclatura padrão de contratos de dados e de membros de dados.

// This overrides the standard namespace mapping for all contracts
// in Contoso.CRM.
[assembly: ContractNamespace("http://schemas.example.com/crm",
   ClrNamespace = "Contoso.CRM")]
namespace Contoso.CRM
{
    // The namespace is overridden to become:
    // http://schemas.example.com/crm.
    // But the name is the default "Customer".
    [DataContract]
    public class Customer
    {
        // Code not shown.
    }
}
namespace Contoso.OrderProc
{
    [DataContract]
    public class PurchaseOrder
    {
        // This data member is named "Amount" by default.
        [DataMember]
        public double Amount;

        // The default is overridden to become "Address".
        [DataMember(Name = "Address")]
        public string Ship_to;
    }
    // The namespace is the default value:
    // http://schemas.datacontract.org/2004/07/Contoso.OrderProc
    // The name is "PurchaseOrder" instead of "MyInvoice".
    [DataContract(Name = "PurchaseOrder")]
    public class MyInvoice
    {
        // Code not shown.
    }

    // The contract name is "Payment" instead of "MyPayment"
    // and the Namespace is "http://schemas.example.com" instead
    // of the default.
    [DataContract(Name = "Payment",
        Namespace = "http://schemas.example.com")]
    public class MyPayment
    {
        // Code not shown.
    }
}
' This overrides the standard namespace mapping for all contracts 
' in Contoso.CRM. 
<Assembly: ContractNamespace("http://schemas.example.com/crm", _
ClrNamespace:="Contoso.CRM")>
Namespace Contoso.CRM
    ' The namespace is overridden to become: 
    ' http://schemas.example.com/crm.
    ' But the name is the default "Customer".
    <DataContract()> _
    Public Class Customer
        ' Code not shown.
    End Class
End Namespace

Namespace Contoso.OrderProc
    <DataContract()> _
    Public Class PurchaseOrder
        ' This data member is named "Amount" by default.
        <DataMember()> _
        Public Amount As Double

        ' The default is overridden to become "Address".
        <DataMember(Name:="Address")> _
        Public Ship_to As String
    End Class

    ' The namespace is the default value:
    ' http://schemas.datacontract.org/2004/07/Contoso.OrderProc
    ' The name is "PurchaseOrder" instead of "MyInvoice".
    <DataContract(Name:="PurchaseOrder")> _
    Public Class MyInvoice
        ' Code not shown.
    End Class

    ' The contract name is "Payment" instead of "MyPayment" 
    ' and the Namespace is "http://schemas.example.com" instead
    ' of the default.
    <DataContract(Name:="Payment", [Namespace]:="http://schemas.example.com")> _
    Public Class MyPayment
        ' Code not shown.
    End Class
End Namespace

Nomes de Contrato de Dados para Tipos Genéricos

Existem regras especiais para determinar nomes de contrato de dados para tipos genéricos. Essas regras ajudam a evitar colisões de nome de contrato de dados entre dois genéricos fechados do mesmo tipo genérico.

Por padrão, o nome de contrato de dados para um tipo genérico é o nome do tipo, seguido da cadeia de caracteres "Of", seguida dos nomes de contrato de dados dos parâmetros genéricos, seguidos de um hash calculado usando os namespaces de contrato de dados dos parâmetros genéricos. Um hash é o resultado de uma função matemática que atua como "impressão digital", que identifica exclusivamente uma parte dos dados. Quando todos os parâmetros genéricos são tipos primitivos, o hash é omitido.

Por exemplo, confira os tipos no exemplo a seguir.

[DataContract]
public class Drawing<Shape, Brush>
{
    // Code not shown.
}

[DataContract(Namespace = "urn:shapes")]
public class Square
{
    // Code not shown.
}

[DataContract(Name = "RedBrush", Namespace = "urn:default")]
public class RegularRedBrush
{
    // Code not shown.
}

[DataContract(Name = "RedBrush", Namespace = "urn:special")]
public class SpecialRedBrush
{
    // Code not shown.
}
<DataContract()> _
Public Class Drawing(Of Shape, Brush)

    <DataContract([Namespace]:="urn:shapes")> _
    Public Class Square
        ' Code not shown.
    End Class


    <DataContract(Name:="RedBrush", [Namespace]:="urn:default")> _
    Public Class RegularRedBrush
        ' Code not shown.
    End Class

    <DataContract(Name:="RedBrush", [Namespace]:="urn:special")> _
    Public Class SpecialRedBrush
        ' Code not shown.
    End Class
End Class

Neste exemplo, o tipo Drawing<Square,RegularRedBrush> tem o nome de contrato de dados "DrawingOfSquareRedBrush5HWGAU6h", onde "5HWGAU6h" é um hash dos namespaces "urn:shapes" e "urn:default". O tipo Drawing<Square,SpecialRedBrush> tem o nome de contrato de dados "DrawingOfSquareRedBrushjpB5LgQ_S", onde "jpB5LgQ_S" é um hash dos namespaces "urn:shapes" e "urn:special". Observe que, se o hash não for usado, os dois nomes serão idênticos e, portanto, ocorrerá uma colisão de nome.

Personalização de nomes de contrato de dados para tipos genéricos

Às vezes, os nomes de contrato de dados gerados para tipos genéricos, conforme descrito anteriormente, são inaceitáveis. Por exemplo, talvez você saiba com antecedência que não encontrará colisões de nome e queira remover o hash. Nesse caso, você pode usar a propriedade DataContractAttribute.Name para especificar uma maneira diferente de gerar nomes. Você pode usar números entre chaves dentro da propriedade Name, para se referir aos nomes de contrato de dados dos parâmetros genéricos. (0 refere-se ao primeiro parâmetro, 1 refere-se ao segundo e assim por diante.) Você pode usar um sinal de número (#) entre chaves para se referir ao hash. Você pode usar cada uma dessas referências várias vezes ou pode nem usar.

Por exemplo, o tipo genérico anterior Drawing poderia ter sido declarado conforme mostrado no exemplo a seguir.

[DataContract(Name = "Drawing_using_{1}_brush_and_{0}_shape")]
public class Drawing<Shape, Brush>
{
    // Code not shown.
}
<DataContract(Name:="Drawing_using_{1}_brush_and_{0}_shape")> _
Public Class Drawing(Of Shape, Brush)
    ' Code not shown.
End Class

Nesse caso, o tipo Drawing<Square,RegularRedBrush> tem o nome de contrato de dados "Drawing_using_RedBrush_brush_and_Square_shape". Observe que, como há um "{#}" na propriedade Name, o hash não faz parte do nome e, portanto, o tipo é suscetível a colisões de nomenclatura. Por exemplo, o tipo Drawing<Square,SpecialRedBrush> teria exatamente o mesmo nome de contrato de dados.

Confira também