Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Pour générer des classes à partir de schémas utilisables avec Windows Communication Foundation (WCF), utilisez la XsdDataContractImporter classe. Cette rubrique décrit le processus et les variantes.
Processus d’importation
Le processus d’importation de schéma commence par un XmlSchemaSet et produit un CodeCompileUnit.
Le XmlSchemaSet fait partie du modèle objet de schéma (SOM) du .NET Framework, qui représente un ensemble de documents de schéma du langage de définition de schéma XML (XSD). Pour créer un XmlSchemaSet objet à partir d’un ensemble de documents XSD, désérialisez chaque document dans un XmlSchema objet (à l’aide du XmlSerializer) et ajoutez ces objets à un nouveau XmlSchemaSet.
Il CodeCompileUnit fait partie du modèle objet de document de code (CodeDOM) du .NET Framework qui représente le code .NET Framework de manière abstraite. Pour générer le code réel à partir d’un CodeCompileUnit, utilisez une sous-classe de la classe CodeDomProvider, telle que la classe CSharpCodeProvider ou la classe VBCodeProvider.
Pour importer un schéma
Créez une instance du XsdDataContractImporter.
Optionnel. Passez un
CodeCompileUnitdans le constructeur. Les types générés lors de l’importation de schéma sont ajoutés à cetteCodeCompileUnitinstance au lieu de commencer par un videCodeCompileUnit.Optionnel. Appelez une des méthodes CanImport. La méthode détermine si le schéma donné est un schéma de contrat de données valide et peut être importé. La
CanImportméthode a les mêmes surcharges queImport(l’étape suivante).Appelez l’une des méthodes surchargées
Import, par exemple la Import(XmlSchemaSet) méthode.La surcharge la plus simple accepte
XmlSchemaSetet importe tous les types, y compris les types anonymes, trouvés dans ce jeu de schémas. D'autres surcharges vous permettent de spécifier le type XSD ou une liste de types à importer (sous la forme d'un XmlQualifiedName ou d'une collection d'objetsXmlQualifiedName). Dans ce cas, seuls les types spécifiés sont importés. Une surcharge prend un XmlSchemaElement qui importe un élément particulier hors duXmlSchemaSet, ainsi que son type associé (qu'il soit anonyme ou non). Cette surcharge retourne unXmlQualifiedName, qui représente le nom du contrat de données du type généré pour cet élément.Plusieurs appels de la méthode
Importentraînent l'ajout de plusieurs éléments au mêmeCodeCompileUnit. Un type n’est pas généré dans leCodeCompileUnits’il y existe déjà. AppelezImportplusieurs fois sur le mêmeXsdDataContractImporterréseau au lieu d’utiliser plusieursXsdDataContractImporterobjets. Il s’agit de la méthode recommandée pour éviter la génération de types en double.Remarque
En cas de défaillance lors de l’importation, l’état
CodeCompileUnitest imprévisible. L’utilisation d’uneCodeCompileUnitissue d’un import échoué peut vous exposer à des vulnérabilités.Accédez au
CodeCompileUnitvia la CodeCompileUnit propriété.
Options d’importation : personnalisation des types générés
Vous pouvez définir la Options propriété de la XsdDataContractImporter classe sur une instance de la ImportOptions classe pour contrôler différents aspects du processus d’importation. Un certain nombre d’options influencent directement les types générés.
Contrôle du niveau d’accès (GenerateInternal ou commutateur /internal)
Cela correspond au commutateur /internal sur l’outil utilitaire de métadonnées ServiceModel (Svcutil.exe).
Normalement, les types publics sont générés à partir du schéma, avec des champs privés et des propriétés de membre de données publiques correspondantes. Pour générer des types internes à la place, définissez la GenerateInternal propriété sur true.
L’exemple suivant montre un schéma transformé en classe interne lorsque la GenerateInternal propriété est définie sur true.
[DataContract]
internal partial class Vehicle : IExtensibleDataObject
{
private int yearField;
private string colorField;
[DataMember]
internal int year
{
get { return this.yearField; }
set { this.yearField = value; }
}
[DataMember]
internal string color
{
get { return this.colorField; }
set { this.colorField = value; }
}
private ExtensionDataObject extensionDataField;
public ExtensionDataObject ExtensionData
{
get { return this.extensionDataField; }
set { this.extensionDataField = value; }
}
}
Class Vehicle
Implements IExtensibleDataObject
Private yearField As Integer
Private colorField As String
<DataMember()> _
Friend Property year() As Integer
Get
Return Me.yearField
End Get
Set
Me.yearField = value
End Set
End Property
<DataMember()> _
Friend Property color() As String
Get
Return Me.colorField
End Get
Set
Me.colorField = value
End Set
End Property
Private extensionDataField As ExtensionDataObject
Public Property ExtensionData() As ExtensionDataObject _
Implements IExtensibleDataObject.ExtensionData
Get
Return Me.extensionDataField
End Get
Set(ByVal value As ExtensionDataObject)
Me.extensionDataField = value
End Set
End Property
End Class
Contrôle des espaces de noms (espaces de noms ou option /namespace)
Cela correspond au commutateur /namespace sur l’outil Svcutil.exe .
Normalement, les types générés à partir du schéma sont générés dans des espaces de noms .NET Framework, chaque espace de noms XSD correspondant à un espace de noms .NET Framework particulier en fonction d’un mappage décrit dans la référence de schéma du contrat de données. Vous pouvez personnaliser ce mappage en utilisant la propriété Namespaces vers Dictionary<TKey,TValue>. Si un espace de noms XSD donné se trouve dans le dictionnaire, l’espace de noms .NET Framework correspondant est également extrait de votre dictionnaire.
Par exemple, considérez le schéma suivant.
<xs:schema targetNamespace="http://schemas.contoso.com/carSchema">
<xs:complexType name="Vehicle">
<!-- details omitted... -->
</xs:complexType>
</xs:schema>
L’exemple suivant utilise la propriété Namespaces pour mapper l’espace de nommage http://schemas.contoso.com/carSchema à « Contoso.Cars ».
XsdDataContractImporter importer = new XsdDataContractImporter();
importer.Options.Namespaces.Add(new KeyValuePair<string, string>("http://schemas.contoso.com/carSchema", "Contoso.Cars"));
Dim importer As New XsdDataContractImporter
importer.Options.Namespaces.Add(New KeyValuePair(Of String, String)("http://schemas.contoso.com/carSchema", "Contoso.Cars"))
Ajout de l'attribut Serializable (GenerateSerializable ou de l'option /serializable)
Cela correspond au commutateur /serializable dans l’outil Svcutil.exe.
Il est parfois important que les types générés à partir du schéma soient utilisables avec les moteurs de sérialisation du runtime .NET Framework. Cela est utile si vous utilisez des types pour la communication à distance .NET Framework. Pour l’activer, vous devez appliquer l’attribut SerializableAttribute aux types générés en plus de l’attribut normal DataContractAttribute . L’attribut est généré automatiquement si l’option GenerateSerializable d’importation est définie sur true.
L’exemple suivant montre la Vehicle classe générée avec l’option d’importation GenerateSerializable définie sur true.
[DataContract]
[Serializable]
public partial class Vehicle : IExtensibleDataObject
{
// Code not shown.
public ExtensionDataObject ExtensionData
{
get
{
throw new Exception("The method or operation is not implemented.");
}
set
{
throw new Exception("The method or operation is not implemented.");
}
}
}
<DataContract(), Serializable()> _
Partial Class Vehicle
Implements IExtensibleDataObject
Private extensionDataField As ExtensionDataObject
' Code not shown.
Public Property ExtensionData() As ExtensionDataObject _
Implements IExtensibleDataObject.ExtensionData
Get
Return Me.extensionDataField
End Get
Set(ByVal value As ExtensionDataObject)
Me.extensionDataField = value
End Set
End Property
End Class
Ajout de la prise en charge de la liaisons de données (EnableDataBinding ou le commutateur /enableDataBinding)
Cela correspond à l'option /enableDataBinding dans l'outil Svcutil.exe.
Parfois, vous souhaiterez peut-être lier les types générés à partir du schéma aux composants d’interface utilisateur graphique afin que toute mise à jour de ces types met automatiquement à jour l’interface utilisateur. Le XsdDataContractImporter peut générer des types qui implémentent l’interface INotifyPropertyChanged afin que toute modification de propriété déclenche un événement. Si vous générez des types à utiliser avec un environnement de programmation de l'interface utilisateur client qui prend en charge cette interface (par exemple, Windows Presentation Foundation (WPF)), définissez la propriété EnableDataBinding sur true pour activer cette fonctionnalité.
L’exemple suivant montre la Vehicle classe générée avec le EnableDataBinding réglé sur true.
[DataContract]
public partial class Vehicle : IExtensibleDataObject, INotifyPropertyChanged
{
private int yearField;
private string colorField;
[DataMember]
public int year
{
get { return this.yearField; }
set
{
if (this.yearField.Equals(value) != true)
{
this.yearField = value;
this.RaisePropertyChanged("year");
}
}
}
[DataMember]
public string color
{
get { return this.colorField; }
set
{
if (this.colorField.Equals(value) != true)
{
this.colorField = value;
this.RaisePropertyChanged("color");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler propertyChanged =
this.PropertyChanged;
if (propertyChanged != null)
{
propertyChanged(this,
new PropertyChangedEventArgs(propertyName));
}
}
private ExtensionDataObject extensionDataField;
public ExtensionDataObject ExtensionData
{
get { return this.extensionDataField; }
set { this.extensionDataField = value; }
}
}
Partial Class Vehicle
Implements IExtensibleDataObject, INotifyPropertyChanged
Private yearField As Integer
Private colorField As String
<DataMember()> _
Public Property year() As Integer
Get
Return Me.yearField
End Get
Set
If Me.yearField.Equals(value) <> True Then
Me.yearField = value
Me.RaisePropertyChanged("year")
End If
End Set
End Property
<DataMember()> _
Public Property color() As String
Get
Return Me.colorField
End Get
Set
If Me.colorField.Equals(value) <> True Then
Me.colorField = value
Me.RaisePropertyChanged("color")
End If
End Set
End Property
Public Event PropertyChanged As PropertyChangedEventHandler _
Implements INotifyPropertyChanged.PropertyChanged
Private Sub RaisePropertyChanged(ByVal propertyName As String)
RaiseEvent PropertyChanged(Me, _
New PropertyChangedEventArgs(propertyName))
End Sub
Private extensionDataField As ExtensionDataObject
Public Property ExtensionData() As ExtensionDataObject _
Implements IExtensibleDataObject.ExtensionData
Get
Return Me.extensionDataField
End Get
Set(ByVal value As ExtensionDataObject)
Me.extensionDataField = value
End Set
End Property
End Class
Options d’importation : choix des types de collection
Deux modèles spéciaux dans XML représentent des collections d’éléments : des listes d’éléments et des associations entre un élément et un autre. Voici un exemple de liste de chaînes.
<People>
<person>Alice</person>
<person>Bob</person>
<person>Charlie</person>
</People>
Voici un exemple d’association entre une chaîne et un entier (city name et population).
<Cities>
<city>
<name>Auburn</name>
<population>40000</population>
</city>
<city>
<name>Bellevue</name>
<population>80000</population>
</city>
<city>
<name>Cedar Creek</name>
<population>10000</population>
</city>
</Cities>
Remarque
Toute association peut également être considérée comme une liste. Par exemple, vous pouvez afficher l’association précédente sous la forme d’une liste d’objets complexes city qui ont deux champs (un champ de chaîne et un champ entier). Les deux modèles ont une représentation dans le schéma XSD. Il n’existe aucun moyen de différencier une liste et une association. Ces modèles sont donc toujours traités comme des listes, sauf si une annotation spéciale spécifique à WCF est présente dans le schéma. L’annotation indique qu’un modèle donné représente une association. Pour plus d’informations, consultez Référence des schémas de contrats de données.
Normalement, une liste est importée en tant que contrat de données de collecte qui dérive d’une liste générique ou d’un tableau .NET Framework, selon que le schéma suit ou non le modèle d’affectation de noms standard pour les collections. Cette procédure est décrite plus en détail dans les types de collecte dans les contrats de données. Les associations sont importées normalement comme Dictionary<TKey,TValue> ou comme un contrat de données de collection qui dérive de l'objet dictionnaire. Par exemple, considérez le schéma suivant.
<xs:complexType name="Vehicle">
<xs:sequence>
<xs:element name="year" type="xs:int"/>
<xs:element name="color" type="xs:string"/>
<xs:element name="passengers" type="people"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="people">
<xs:sequence>
<xs:element name="person" type="xs:string" maxOccurs="unbounded" />
</xs:sequence>
</xs:complexType>
Cela serait importé comme suit (les champs sont affichés au lieu des propriétés pour la lisibilité).
[DataContract]
public partial class Vehicle : IExtensibleDataObject
{
[DataMember] public int yearField;
[DataMember] public string colorField;
[DataMember] public people passengers;
// Other code not shown.
public ExtensionDataObject ExtensionData
{
get
{
throw new Exception("The method or operation is not implemented.");
}
set
{
throw new Exception("The method or operation is not implemented.");
}
}
}
[CollectionDataContract(ItemName = "person")]
public class people : List<string> { }
Public Partial Class Vehicle
Implements IExtensibleDataObject
<DataMember()> _
Public yearField As Integer
<DataMember()> _
Public colorField As String
<DataMember()> _
Public passengers As people
' Other code not shown.
Public Property ExtensionData() As ExtensionDataObject _
Implements IExtensibleDataObject.ExtensionData
Get
Throw New Exception("The method or operation is not implemented.")
End Get
Set
Throw New Exception("The method or operation is not implemented.")
End Set
End Property
End Class
<CollectionDataContract(ItemName:="person")> _
Public Class people
Inherits List(Of String)
End Class
Il est possible de personnaliser les types de collection générés pour ces modèles de schéma. Par exemple, vous pouvez générer des collections dérivant de la BindingList<T> classe au lieu de la List<T> classe afin de lier le type à une zone de liste et de le mettre automatiquement à jour lorsque le contenu de la collection change. Pour ce faire, définissez la ReferencedCollectionTypes propriété de la ImportOptions classe sur une liste de types de collection à utiliser (appelées ci-après les types référencés). Lors de l’importation d’une collection, cette liste de types de collection référencés est analysée et la collection la plus adaptée est utilisée si une collection est trouvée. Les associations sont mises en correspondance uniquement par rapport aux types qui implémentent l’interface générique ou non IDictionary générique, tandis que les listes sont mises en correspondance avec n’importe quel type de collection pris en charge.
Par exemple, si la ReferencedCollectionTypes propriété est définie sur un BindingList<T>, le people type de l’exemple précédent est généré comme suit.
[CollectionDataContract(ItemName = "person")]
public class people : BindingList<string> { }
<CollectionDataContract(ItemName:="person")> _
Public Class people
Inherits BindingList(Of String)
Un type générique fermé est considéré comme la meilleure correspondance. Par exemple, si les types BindingList(Of Integer) et ArrayList sont passés à la collection de types référencés, toutes les listes d’entiers trouvés dans le schéma sont importées en tant que BindingList(Of Integer). Toutes les autres listes, par exemple, un List(Of String), sont importées en tant que ArrayList.
Si un type qui implémente l’interface générique IDictionary est ajouté à la collection de types référencés, ses paramètres de type doivent être entièrement ouverts ou entièrement fermés.
Les doublons ne sont pas autorisés. Par exemple, vous ne pouvez pas ajouter à la fois un List(Of Integer) et un Collection(Of Integer) aux types référencés. Cela rend impossible de déterminer qui doit être utilisé lorsqu’une liste d’entiers est trouvée dans le schéma. Les doublons ne sont détectés que s’il existe un type dans le schéma qui expose le problème des doublons. Par exemple, si le schéma importé ne contient pas de listes d’entiers, il est permis d'inclure à la fois List(Of Integer) et Collection(Of Integer) dans la collection des types référencés, mais aucun des deux n’aura d’effet.
Le mécanisme de types de collections référencés fonctionne également bien pour les collections de types complexes (y compris les collections d’autres collections), et non seulement pour celles de primitives.
La ReferencedCollectionTypes propriété correspond au commutateur /collectionType sur l’outil SvcUtil.exe. Notez que pour référencer plusieurs types de collection, le commutateur /collectionType doit être spécifié plusieurs fois. Si le type ne figure pas dans le fichier MsCorLib.dll, son assembly doit être aussi référencé à l’aide du commutateur /reference.
Options d’importation : référencement de types existants
Parfois, les types dans le schéma correspondent aux types .NET Framework existants et il n’est pas nécessaire de générer ces types à partir de zéro. (Cette section s’applique uniquement aux types non-collection. Pour les types de collection, consultez la section précédente.)
Par exemple, vous pouvez avoir un type standard de contrat de données « Personne » à l’échelle de l’entreprise que vous souhaitez toujours utiliser lors de la représentation d’une personne. Chaque fois que certains services utilisent ce type et que son schéma apparaît dans les métadonnées du service, vous pouvez réutiliser le type existant Person lors de l’importation de ce schéma au lieu de générer un nouveau pour chaque service.
Pour ce faire, transmettez une liste de types .NET Framework que vous souhaitez réutiliser dans la collection que la propriété ReferencedTypes de la classe ImportOptions retourne. Si l’un de ces types a un nom de contrat de données et un espace de noms correspondant au nom et à l’espace de noms d’un type de schéma, une comparaison structurelle est effectuée. S’il est déterminé que les types ont à la fois des noms correspondants et des structures correspondantes, le type .NET Framework existant est réutilisé au lieu de générer un nouveau. Si seul le nom correspond mais pas la structure, une exception est levée. Notez qu’il n’existe aucune autorisation pour le contrôle de version lors du référencement de types (par exemple, l’ajout de nouveaux membres de données facultatifs). Les structures doivent correspondre exactement.
Il est légal d’ajouter plusieurs types avec le même nom de contrat de données et l’espace de noms à la collection de types référencés, tant qu’aucun type de schéma n’est importé avec ce nom et cet espace de noms. Cela vous permet d’ajouter facilement tous les types d’un assembly à la collection sans vous soucier des doublons pour les types qui ne se produisent pas réellement dans le schéma.
La ReferencedTypes propriété correspond au commutateur /reference dans certains modes d’opération de l’outil Svcutil.exe.
Remarque
Lors de l’utilisation de l'Svcutil.exe ou (dans Visual Studio) des outils Add Service Reference , tous les types de MsCorLib.dll sont automatiquement référencés.
Options d'importation : importer le schéma non DataContract comme types IXmlSerializable
Le XsdDataContractImporter prend en charge un sous-ensemble limité du schéma. Si des constructions de schéma non prises en charge sont présentes (par exemple, des attributs XML), la tentative d’importation échoue avec une exception. Toutefois, définir la propriété ImportXmlType sur true étend la gamme de schémas pris en charge. Lorsqu'il est défini à true, les types XsdDataContractImporter générés implémentent l'interface IXmlSerializable. Cela permet un accès direct à la représentation XML de ces types.
Considérations relatives à la conception
Il peut être difficile de travailler directement avec la représentation XML faiblement typée. Envisagez d’utiliser un autre moteur de sérialisation, tel que le XmlSerializer, pour travailler avec des schémas qui ne sont pas compatibles avec les contrats de données, de manière fortement typée. Pour plus d’informations, consultez Utilisation de la classe XmlSerializer.
Certaines constructions de schéma ne peuvent pas être importées par XsdDataContractImporter même lorsque la propriété ImportXmlType est définie sur
true. Là encore, envisagez d’utiliser le XmlSerializer pour ces cas.Les constructions de schéma exactes qui sont prises en charge lorsque ImportXmlType a la valeur
trueoufalsesont décrites dans Référence des schémas de contrats de données.Le schéma pour les types générés IXmlSerializable ne conserve pas son exactitude lors de l'importation et de l'exportation. Autrement dit, l’exportation du schéma à partir des types générés et de l’importation en tant que classes ne retourne pas le schéma d’origine.
Il est possible de combiner l’option ImportXmlType avec l’option ReferencedTypes décrite précédemment. Pour les types qui doivent être générés en tant qu’implémentations IXmlSerializable , la vérification structurelle est ignorée lors de l’utilisation de la ReferencedTypes fonctionnalité.
L’option ImportXmlType correspond au commutateur /importXmlTypes sur l’outil Svcutil.exe.
Utilisation des types IXmlSerializable générés
Les types générés IXmlSerializable contiennent un champ privé nommé « nodeField », qui retourne un tableau d’objets XmlNode . Lors de la désérialisation d’une instance de ce type, vous pouvez accéder aux données XML directement via ce champ à l’aide du modèle objet document XML. Lors de la sérialisation d’une instance de ce type, vous pouvez définir ce champ sur les données XML souhaitées et il sera sérialisé.
Cela s’effectue par le biais de l’implémentation IXmlSerializable . Dans le type généré IXmlSerializable , l’implémentation ReadXml appelle la ReadNodes méthode de la XmlSerializableServices classe. La méthode est une fonction d'assistance qui convertit le XML fourni via un XmlReader en un tableau d'objets XmlNode. L’implémentation WriteXml effectue l’inverse et convertit le tableau d’objets XmlNode en une séquence d’appels XmlWriter . Cette opération est effectuée à l’aide de la WriteNodes méthode.
Il est possible d’exécuter le processus d’exportation de schéma sur les classes générées IXmlSerializable . Comme indiqué précédemment, vous n’obtiendrez pas le schéma d’origine. Au lieu de cela, vous obtiendrez le type XSD standard « anyType », qui est un caractère générique pour n’importe quel type XSD.
Pour ce faire, appliquez l’attribut XmlSchemaProviderAttribute aux classes générées IXmlSerializable et spécifiez une méthode qui appelle la AddDefaultSchema méthode pour générer le type « anyType ».
Remarque
Le XmlSerializableServices type existe uniquement pour prendre en charge cette fonctionnalité particulière. Il n’est pas recommandé d’utiliser à d’autres fins.
Options d’importation : Options avancées
Les options d’importation avancées suivantes sont les suivantes :
Propriété CodeProvider. Spécifiez la CodeDomProvider valeur à utiliser pour générer le code des classes générées. Le mécanisme d’importation tente d’éviter les fonctionnalités que le CodeDomProvider ne prend pas en charge. Si la CodeProvider valeur n’est pas définie, l’ensemble complet de fonctionnalités .NET Framework est utilisé sans restrictions.
Propriété DataContractSurrogate. Une IDataContractSurrogate implémentation peut être spécifiée avec cette propriété. Le IDataContractSurrogate personnalise le processus d’importation. Pour plus d’informations, consultez Les substituts de contrat de données. Par défaut, aucune substitution n’est utilisée.
Voir aussi
- DataContractSerializer
- XsdDataContractImporter
- XsdDataContractExporter
- ImportOptions
- Informations de référence sur le schéma du contrat de données
- Substituts de contrats de données
- Importation et exportation de schémas
- Exportation de schémas à partir de classes
- Informations de référence sur le schéma du contrat de données