Partilhar via


Substituto DataContract

O exemplo DataContract demonstra como processos como serialização, desserialização, exportação de esquema e importação de esquema podem ser personalizados usando uma classe substituta de contrato de dados. Este exemplo mostra como usar um substituto em um cenário de cliente e servidor onde os dados são serializados e transmitidos entre um cliente e serviço do Windows Communication Foundation (WCF).

Observação

O procedimento de configuração e as instruções de compilação para este exemplo estão localizados no final deste tópico.

O exemplo usa o seguinte contrato de serviço:

[ServiceContract(Namespace = "http://Microsoft.ServiceModel.Samples")]
[AllowNonSerializableTypes]
public interface IPersonnelDataService
{
    [OperationContract]
    void AddEmployee(Employee employee);

    [OperationContract]
    Employee GetEmployee(string name);
}

A AddEmployee operação permite que os usuários adicionem dados sobre novos funcionários e a operação suporta a GetEmployee busca por funcionários com base no nome.

Essas operações usam o seguinte tipo de dados:

[DataContract(Namespace = "http://Microsoft.ServiceModel.Samples")]
class Employee
{
    [DataMember]
    public DateTime dateHired;

    [DataMember]
    public Decimal salary;

    [DataMember]
    public Person person;
}

No tipo Employee, a classe Person (mostrada no código de exemplo a seguir) não pode ser serializada por DataContractSerializer porque não é uma classe de contrato de dados válida.

public class Person
{
    public string firstName;

    public string lastName;

    public int age;

    public Person() { }
}

Você pode aplicar o DataContractAttribute atributo à Person classe, mas isso nem sempre é possível. Por exemplo, a Person classe pode ser definida em um assembly separado do qual você não tem controle.

Dada essa restrição, uma maneira de serializar a Person classe é substituí-la por outra classe marcada com DataContractAttribute e copiar os dados necessários para a nova classe. O objetivo é fazer com que a Person classe apareça como um DataContract para o DataContractSerializer. Observe que essa é uma maneira de serializar classes de contrato que não sejam de dados.

O exemplo substitui logicamente a Person classe por uma classe diferente chamada PersonSurrogated.

[DataContract(Name="Person", Namespace = "http://Microsoft.ServiceModel.Samples")]
public class PersonSurrogated
{
    [DataMember]
    public string FirstName;

    [DataMember]
    public string LastName;

    [DataMember]
    public int Age;
}

O substituto do contrato de dados é usado para realizar esta substituição. Um substituto de contrato de dados é uma classe que implementa IDataContractSurrogate. Neste exemplo, a AllowNonSerializableTypesSurrogate classe implementa essa interface.

Na implementação da interface, a primeira tarefa é estabelecer um mapeamento de tipo de Person para PersonSurrogated. Isso é usado tanto no momento da serialização quanto no momento da exportação do esquema. Este mapeamento é conseguido através da implementação do GetDataContractType(Type) método.

public Type GetDataContractType(Type type)
{
    if (typeof(Person).IsAssignableFrom(type))
    {
        return typeof(PersonSurrogated);
    }
    return type;
}

O GetObjectToSerialize(Object, Type) método mapeia uma Person instância para uma PersonSurrogated instância durante a serialização, conforme mostrado no código de exemplo a seguir.

public object GetObjectToSerialize(object obj, Type targetType)
{
    if (obj is Person)
    {
        Person person = (Person)obj;
        PersonSurrogated personSurrogated = new PersonSurrogated();
        personSurrogated.FirstName = person.firstName;
        personSurrogated.LastName = person.lastName;
        personSurrogated.Age = person.age;
        return personSurrogated;
    }
    return obj;
}

O GetDeserializedObject(Object, Type) método fornece o mapeamento reverso para desserialização, conforme mostrado no código de exemplo a seguir.

public object GetDeserializedObject(object obj,
Type targetType)
{
    if (obj is PersonSurrogated)
    {
        PersonSurrogated personSurrogated = (PersonSurrogated)obj;
        Person person = new Person();
        person.firstName = personSurrogated.FirstName;
        person.lastName = personSurrogated.LastName;
        person.age = personSurrogated.Age;
        return person;
    }
    return obj;
}

Para mapear o contrato de PersonSurrogated dados para a classe existente Person durante a importação do esquema, o exemplo implementa o GetReferencedTypeOnImport(String, String, Object) método, conforme mostrado no código de exemplo a seguir.

public Type GetReferencedTypeOnImport(string typeName,
               string typeNamespace, object customData)
{
if (
typeNamespace.Equals("http://schemas.datacontract.org/2004/07/DCSurrogateSample")
)
    {
         if (typeName.Equals("PersonSurrogated"))
        {
             return typeof(Person);
        }
     }
     return null;
}

O código de exemplo a seguir conclui a implementação da interface IDataContractSurrogate.

public System.CodeDom.CodeTypeDeclaration ProcessImportedType(
          System.CodeDom.CodeTypeDeclaration typeDeclaration,
          System.CodeDom.CodeCompileUnit compileUnit)
{
    return typeDeclaration;
}
public object GetCustomDataToExport(Type clrType,
                               Type dataContractType)
{
    return null;
}

public object GetCustomDataToExport(
System.Reflection.MemberInfo memberInfo, Type dataContractType)
{
    return null;
}
public void GetKnownCustomDataTypes(
        KnownTypeCollection customDataTypes)
{
    // It does not matter what we do here.
    throw new NotImplementedException();
}

Neste exemplo, o substituto é habilitado em ServiceModel por um atributo chamado AllowNonSerializableTypesAttribute. Os desenvolvedores precisariam aplicar esse atributo em seu contrato de serviço, IPersonnelDataService conforme mostrado no contrato de serviço acima. Este atributo implementa IContractBehavior e configura o substituto nas operações em seus ApplyClientBehavior e ApplyDispatchBehavior métodos.

O atributo não é necessário neste caso - é usado para fins de demonstração nesta amostra. Como alternativa, os utilizadores podem habilitar um substituto ao adicionar manualmente um IContractBehavior, IEndpointBehavior ou IOperationBehavior semelhante usando código ou configuração.

A IContractBehavior implementação procura operações que utilizam DataContract, verificando se têm um DataContractSerializerOperationBehavior registado. Se o fizerem, define a propriedade DataContractSurrogate nesse comportamento. O código de exemplo a seguir mostra como isso é feito. Definir o substituto nesse comportamento de operação permite que ele seja serializado e desserializado.

public void ApplyClientBehavior(ContractDescription description, ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime proxy)
{
    foreach (OperationDescription opDesc in description.Operations)
    {
        ApplyDataContractSurrogate(opDesc);
    }
}

public void ApplyDispatchBehavior(ContractDescription description, ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.DispatchRuntime dispatch)
{
    foreach (OperationDescription opDesc in description.Operations)
    {
        ApplyDataContractSurrogate(opDesc);
    }
}

private static void ApplyDataContractSurrogate(OperationDescription description)
{
    DataContractSerializerOperationBehavior dcsOperationBehavior = description.Behaviors.Find<DataContractSerializerOperationBehavior>();
    if (dcsOperationBehavior != null)
    {
        if (dcsOperationBehavior.DataContractSurrogate == null)
            dcsOperationBehavior.DataContractSurrogate = new AllowNonSerializableTypesSurrogate();
    }
}

Etapas adicionais precisam ser tomadas para conectar o substituto para uso durante a geração de metadados. Um mecanismo para fazer isso é fornecer um IWsdlExportExtension que é o que esta amostra demonstra. Outra maneira é modificar o WsdlExporter diretamente.

O AllowNonSerializableTypesAttribute atributo implementa IWsdlExportExtension e IContractBehavior. A extensão pode ser um IContractBehavior ou IEndpointBehavior neste caso. A implementação do método IWsdlExportExtension.ExportContract permite ao substituto ser adicionado ao XsdDataContractExporter utilizado durante a geração do esquema para o DataContract. O trecho de código a seguir mostra como fazer isso.

public void ExportContract(WsdlExporter exporter, WsdlContractConversionContext context)
{
    if (exporter == null)
        throw new ArgumentNullException("exporter");

    object dataContractExporter;
    XsdDataContractExporter xsdDCExporter;
    if (!exporter.State.TryGetValue(typeof(XsdDataContractExporter), out dataContractExporter))
    {
        xsdDCExporter = new XsdDataContractExporter(exporter.GeneratedXmlSchemas);
        exporter.State.Add(typeof(XsdDataContractExporter), xsdDCExporter);
    }
    else
    {
        xsdDCExporter = (XsdDataContractExporter)dataContractExporter;
    }
    if (xsdDCExporter.Options == null)
        xsdDCExporter.Options = new ExportOptions();

    if (xsdDCExporter.Options.DataContractSurrogate == null)
        xsdDCExporter.Options.DataContractSurrogate = new AllowNonSerializableTypesSurrogate();
}

Quando você executa o exemplo, o cliente chama AddEmployee seguido por uma chamada GetEmployee para verificar se a primeira chamada foi bem-sucedida. O resultado da solicitação de operação GetEmployee é exibido na janela do console do cliente. A operação GetEmployee deve conseguir encontrar o funcionário e imprimir "encontrado".

Observação

Este exemplo mostra como conectar um substituto para serializar, desserializar e gerar metadados. Ele não mostra como conectar um substituto para geração de código a partir de metadados. Para ver um exemplo de como um substituto pode ser usando para se integrar à geração de código do cliente, consulte o exemplo de Publicação WSDL Personalizada.

Para configurar, compilar e executar o exemplo

  1. Verifique se você executou o procedimento de instalação do One-Time para os exemplos do Windows Communication Foundation.

  2. Para criar a edição em C# da solução, siga as instruções em Criando os exemplos do Windows Communication Foundation.

  3. Para executar o exemplo em uma configuração de máquina única ou cruzada, siga as instruções em Executando os exemplos do Windows Communication Foundation.