DataContract Surrogate
L’exemple DataContract illustre comment personnaliser des processus tels que la sérialisation, la désérialisation, l’exportation et l’importation de schémas à l’aide d’une classe de substitution d’un contrat de données. Cet exemple illustre comment utiliser un substitut dans un scénario de client et serveur où les données sont sérialisées et transmises entre un client et un service WCF (Windows Communication Foundation).
Notes
La procédure d'installation ainsi que les instructions de génération relatives à cet exemple figurent à 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 de nouveaux employés et l'opération GetEmployee
permet de lancer une recherche sur les employés en fonction de leur 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 type Employee
, la classe Person
(contenue dans l'exemple de code suivant) ne peut pas être sérialisée par le DataContractSerializer, celle-ci ne correspondant pas à une classe de contrat de données valable.
public class Person
{
public string firstName;
public string lastName;
public int age;
public Person() { }
}
Vous pouvez appliquer l'attribut DataContractAttribute à la classe Person
, mais cela n'est pas toujours possible. Par exemple, la classe Person
peut être définie dans un assembly distinct sur lequel vous n'exercez aucun contrôle.
L'une des solutions permettant alors de sérialiser la classe Person
consiste à la remplacer par une autre classe signalée par l'attribut DataContractAttribute, puis à copier les données requises dans cette nouvelle classe. L'objectif de l'opération ci-dessus est le suivant : présenter la classe Person
comme DataContract au DataContractSerializer. Remarque : il s'agit d'une solution parmi d'autres permettant de sérialiser les classes n'appartenant pas à un contrat de données.
L'exemple remplace logiquement la classe Person
par une classe différente 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 classe AllowNonSerializableTypesSurrogate
implémente cette interface.
Dans l’implémentation d’interface, la première tâche consiste à définir un type mappant la classe Person
à la classe PersonSurrogated
. Ce processus de mappage est utilisé à la fois pendant la sérialisation et l'exportation des schémas. Il s'effectue en implémentant la méthode GetDataContractType(Type).
public Type GetDataContractType(Type type)
{
if (typeof(Person).IsAssignableFrom(type))
{
return typeof(PersonSurrogated);
}
return type;
}
La méthode GetObjectToSerialize(Object, Type) mappe une instance de la classe Person
à une instance de la classe PersonSurrogated
pendant la sérialisation, tel qu'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 méthode GetDeserializedObject(Object, Type) permet d’effectuer le mappage dans le sens inverse lors de la désérialisation, tel qu’illustré 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 contrat de données PersonSurrogated
à la classe Person
existante lors de l'importation des schémas, l'exemple implémente la méthode GetReferencedTypeOnImport(String, String, Object), tel qu'illustré 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 achève 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, le substitut est activé dans ServiceModel par un attribut appelé AllowNonSerializableTypesAttribute
. Les développeurs doivent appliquer cet attribut dans leur contrat de service respectif, tel qu'illustré dans le contrat de service IPersonnelDataService
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 d'illustration dans cet exemple. Les utilisateurs peuvent également activer un substitut en ajoutant manuellement un IContractBehavior
, IEndpointBehavior
ou IOperationBehavior
similaire à l'aide du code ou de la configuration.
L'implémentation IContractBehavior
recherche les opérations qui utilisent DataContract en vérifiant si un comportement DataContractSerializerOperationBehavior
leur correspondant est enregistré. Si tel est le cas, elle définit la propriété DataContractSurrogate
sur ce comportement. L'exemple de code suivant illustre les modalités de ce processus. 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 sont nécessaires pour incorporer le substitut et ainsi pouvoir l'utiliser lors de la génération des métadonnées. Pour ce faire, l'une des solutions consiste à fournir une extension IWsdlExportExtension
, tel qu'illustré dans cet exemple. Une autre solution consiste à modifier directement le WsdlExporter
.
L’attribut AllowNonSerializableTypesAttribute
implémente IWsdlExportExtension
et IContractBehavior
. L’extension peut correspondre à IContractBehavior
ou à IEndpointBehavior
dans ce cas de figure. 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 la méthode AddEmployee, puis la méthode GetEmployee afin de s'assurer que le premier appel a réussi. Le résultat de la demande d'opération GetEmployee est affiché dans la fenêtre de console du client. L’opération GetEmployee doit réussir à trouver l’employé, puis afficher la mention « trouvé ».
Notes
Cet exemple illustre comment incorporer un substitut à des fins de sérialisation, désérialisation et génération de métadonnées. Il ne montre pas en revanche comment incorporer un substitut pour permettre la génération de code à partir des 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
Assurez-vous d’avoir effectué la Procédure d’installation unique pour les exemples Windows Communication Foundation.
Pour générer l’édition C# de la solution, suivez les instructions indiquées dans la rubrique Génération des exemples Windows Communication Foundation.
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.