Partager via


Substitution DataContract

L’exemple DataContract montre comment les processus tels que la sérialisation, la désérialisation, l’exportation de schéma et l’importation de schéma peuvent être personnalisés à l’aide d’une classe de substitution de contrat de données. Cet exemple montre comment utiliser un substitut dans un scénario client et serveur où les données sont sérialisées et transmises entre un client et un service Windows Communication Foundation (WCF).

Remarque

La procédure d’installation et les instructions de génération de cet exemple se trouvent à la fin de cette rubrique.

L’exemple utilise le contrat de service suivant :

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

    [OperationContract]
    Employee GetEmployee(string name);
}

L’opération AddEmployee permet aux utilisateurs d’ajouter des données sur les nouveaux employés et l’opération GetEmployee prend en charge la recherche d’employés en fonction du nom.

Ces opérations utilisent le type de données suivant :

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

    [DataMember]
    public Decimal salary;

    [DataMember]
    public Person person;
}

Dans le Employee, la classe Person (illustrée dans l'exemple de code suivant) ne peut pas être sérialisée par le DataContractSerializer, car elle n'est pas une classe de contrat de données valide.

public class Person
{
    public string firstName;

    public string lastName;

    public int age;

    public Person() { }
}

Vous pouvez appliquer l’attribut DataContractAttribute à la Person classe, mais cela n’est pas toujours possible. Par exemple, la Person classe peut être définie dans un assembly distinct sur lequel vous n’avez aucun contrôle.

Étant donné cette restriction, une façon de sérialiser la classe Person consiste à la remplacer par une autre classe marquée avec DataContractAttribute et à copier les données nécessaires dans la nouvelle classe. L’objectif est de faire apparaître la Person classe en tant que DataContract à l’objet DataContractSerializer. Notez que voici une manière de sérialiser des classes qui ne sont pas des contrats de données.

L’exemple remplace logiquement la Person classe par une autre classe nommée PersonSurrogated.

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

    [DataMember]
    public string LastName;

    [DataMember]
    public int Age;
}

Le substitut de contrat de données est utilisé pour effectuer ce remplacement. Un substitut de contrat de données est une classe qui implémente IDataContractSurrogate. Dans cet exemple, la AllowNonSerializableTypesSurrogate classe implémente cette interface.

Dans l’implémentation de l’interface, la première tâche consiste à établir un mappage de type à partir de Person vers PersonSurrogated. Cela est utilisé à la fois au moment de la sérialisation ainsi qu’au moment de l’exportation de schéma. Ce mappage est obtenu en implémentant la GetDataContractType(Type) méthode.

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

La GetObjectToSerialize(Object, Type) méthode mappe une Person instance à une PersonSurrogated instance pendant la sérialisation, comme illustré dans l’exemple de code suivant.

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

La GetDeserializedObject(Object, Type) méthode fournit le mappage inverse pour la désérialisation, comme indiqué dans l’exemple de code suivant.

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

Pour mapper le PersonSurrogated contrat de données à la classe existante Person pendant l’importation de schéma, l’exemple implémente la GetReferencedTypeOnImport(String, String, Object) méthode, comme indiqué dans l’exemple de code suivant.

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

L’exemple de code suivant termine l’implémentation de l’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();
}

Dans cet exemple, la substitution est activée dans ServiceModel par un attribut appelé AllowNonSerializableTypesAttribute. Les développeurs doivent appliquer cet attribut sur leur contrat de service, comme indiqué sur le IPersonnelDataService contrat de service ci-dessus. Cet attribut implémente IContractBehavior et définit le substitut sur les opérations dans les méthodes ApplyClientBehavior et ApplyDispatchBehavior.

L’attribut n’est pas nécessaire dans ce cas - il est utilisé à des fins de démonstration dans cet exemple. Les utilisateurs peuvent également activer un substitut en ajoutant manuellement un code similaire IContractBehavior, IEndpointBehavior ou IOperationBehavior en utilisant du code ou une configuration.

L’implémentation IContractBehavior recherche des opérations qui utilisent DataContract en vérifiant si elles ont un DataContractSerializerOperationBehavior enregistré. Si c'est le cas, cela applique la propriété DataContractSurrogate à ce comportement. L’exemple de code suivant montre comment procéder. La définition du substitut sur ce comportement d’opération permet pour ce dernier une sérialisation et une désérialisation.

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

D'autres étapes doivent être effectuées pour brancher le substitut afin de pouvoir être utilisé lors de la génération des métadonnées. Un mécanisme pour ce faire consiste à fournir un IWsdlExportExtension, illustré par cet exemple. Une autre façon est de modifier directement WsdlExporter.

L’attribut AllowNonSerializableTypesAttribute implémente IWsdlExportExtension et IContractBehavior. L’extension peut être une IContractBehavior ou IEndpointBehavior dans ce cas. Son implémentation de la méthode IWsdlExportExtension.ExportContract active le substitut en l'ajoutant au XsdDataContractExporter utilisé lors de la génération des schémas du DataContract. L’extrait de code suivant montre comment procéder.

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

Lorsque vous exécutez l’exemple, le client appelle AddEmployee suivi d’un appel GetEmployee pour vérifier si le premier appel a réussi. Le résultat de la demande d’opération GetEmployee s’affiche dans la fenêtre de console cliente. L’opération GetEmployee doit réussir à trouver l’employé et à afficher « trouvé ».

Remarque

Cet exemple montre comment intégrer un substitut pour sérialiser, désérialiser et générer des métadonnées. Il ne montre pas comment brancher une substitution pour la génération de code à partir de métadonnées. Pour savoir comment utiliser un substitut afin de générer du code client, consultez l’exemple Publication WSDL personnalisée.

Pour configurer, générer et exécuter l’exemple

  1. Assurez-vous d’avoir effectué la Procédure d’installation unique pour les exemples Windows Communication Foundation.

  2. Pour générer l’édition C# de la solution, suivez les instructions de Création des exemples Windows Communication Foundation.

  3. Pour exécuter l’exemple dans une configuration à un ou plusieurs ordinateurs, conformez-vous aux instructions figurant dans la rubrique Exécution des exemples Windows Communication Foundation.