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).

Nota

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;
}

Employee No tipo, a Person classe (mostrada no código de exemplo a seguir) não pode ser serializada DataContractSerializer pelo 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 classe pode ser definida em um assembly separado sobre o Person 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 conseguir essa substituição. Um substituto de contrato de dados é uma classe que implementa IDataContractSurrogateo . Neste exemplo, a AllowNonSerializableTypesSurrogate classe implementa essa interface.

Na implementação da interface, a primeira tarefa é estabelecer um mapeamento de Person tipo de 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 IDataContractSurrogate implementação da interface.

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 usuários podem habilitar um substituto adicionando manualmente um IContractBehaviorsemelhante, IEndpointBehavior usando IOperationBehavior código ou configuração.

A IContractBehavior implementação procura operações que usam DataContract verificando se eles têm um DataContractSerializerOperationBehavior registrado. Se o fizerem, ele define a DataContractSurrogate propriedade 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. Sua IWsdlExportExtension.ExportContract implementação de método permite o substituto, adicionando-o ao usado durante a geração de XsdDataContractExporter esquema para 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".

Nota

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 usado para se conectar à geração de código do cliente, consulte o Exemplo de publicação WSDL personalizada.

Para configurar, compilar e executar o exemplo

  1. Certifique-se de ter executado o procedimento de instalação única 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.