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 IContractBehavior
semelhante, 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
Certifique-se de ter executado o procedimento de instalação única para os exemplos do Windows Communication Foundation.
Para criar a edição em C# da solução, siga as instruções em Criando os exemplos do Windows Communication Foundation.
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.