Sdílet prostřednictvím


Import schématu pro generování tříd

Chcete-li vygenerovat třídy ze schémat, která lze použít se službou Windows Communication Foundation (WCF), použijte XsdDataContractImporter třídu. Toto téma popisuje proces a varianty.

Proces importu

Proces importu schématu začíná XmlSchemaSet a vytvoří CodeCompileUnit.

XmlSchemaSet je součástí modelu SOM (Schema Object Model) rozhraní .NET Framework, který představuje sadu dokumentů schématu definičního jazyka XSD (XML Schema Definition Language). Chcete-li vytvořit XmlSchemaSet objekt ze sady dokumentů XSD, deserializovat každý dokument do objektu XmlSchema (pomocí XmlSerializer) a přidat tyto objekty do nového XmlSchemaSet.

Je CodeCompileUnit součástí modelu Code Document Object Model (CodeDOM) rozhraní .NET Framework, který představuje kód rozhraní .NET Framework abstraktním způsobem. Chcete-li vygenerovat skutečný kód z objektu CodeCompileUnit, použijte podtřídu CodeDomProvider třídy, například CSharpCodeProvider třídu nebo VBCodeProvider třídu.

Import schématu

  1. Vytvořte instanci XsdDataContractImporter.

  2. Volitelný. Předat CodeCompileUnit v konstruktoru. Typy vygenerované během importu schématu se přidají do této CodeCompileUnit instance místo toho, aby začínaly prázdnou CodeCompileUnit instancí.

  3. Volitelný. Volejte jednu z CanImport metod. Metoda určuje, zda dané schéma je platné schéma kontraktu dat a lze jej importovat. Metoda CanImport má stejné přetížení jako Import (další krok).

  4. Zavolejte jednu z přetížených Import metod, například metodu Import(XmlSchemaSet) .

    Nejjednodušší přetížení přebírá XmlSchemaSet a importuje všechny typy, včetně anonymních typů, nalezených v této kolekci schémat. Další přetížení umožňují určit typ XSD nebo seznam typů, které se mají importovat (ve formě XmlQualifiedName nebo kolekce XmlQualifiedName objektů). V tomto případě se importují pouze zadané typy. Přetížení používá XmlSchemaElement který importuje konkrétní prvek z XmlSchemaSet, stejně jako jeho související typ (ať už je anonymní či nikoli). Toto přetížení vrátí hodnotu XmlQualifiedName, která představuje název datového kontraktu typu generovaného pro tento prvek.

    Více volání Import metody vede k přidání více položek do stejného CodeCompileUnit. Typ není vygenerován v CodeCompileUnit případě, že již existuje. Volejte Import vícekrát na stejné XsdDataContractImporter místo použití více objektů XsdDataContractImporter. Toto je doporučený způsob, jak se vyhnout generování duplicitních typů.

    Poznámka:

    Pokud během importu dojde k selhání, CodeCompileUnit bude v nepředvídatelném stavu. Použití výsledku CodeCompileUnit z neúspěšného importu by vás mohlo vystavit ohrožením zabezpečení.

  5. Získáte přístup k CodeCompileUnit prostřednictvím vlastnosti CodeCompileUnit.

Možnosti importu: Přizpůsobení vygenerovaných typů

Vlastnost Options můžete nastavit na instanci třídy XsdDataContractImporter pro ImportOptions, což vám umožní řídit různé aspekty procesu importu. Řada možností přímo ovlivňuje typy, které se generují.

Řízení úrovně přístupu (GenerateInternal nebo /internal switch)

To odpovídá přepínači /internal na nástroji ServiceModel Metadata Utility Tool (Svcutil.exe).

Veřejné typy se obvykle generují ze schématu s privátními poli a odpovídajícími vlastnostmi veřejného datového členu. Chcete-li místo toho generovat interní typy, nastavte GenerateInternal vlastnost na true.

Následující příklad ukazuje schéma transformované na interní třídu, když GenerateInternal je vlastnost nastavena na 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

Správa jmenných prostorů (jmenné prostory nebo přepínač /namespace)

To odpovídá přepínači /namespace v nástroji Svcutil.exe .

Obvykle se typy generované ze schématu generují do oborů názvů rozhraní .NET Framework, přičemž každý obor názvů XSD odpovídá určitému oboru názvů rozhraní .NET Framework podle mapování popsaného v odkazu schématu kontraktu dat. Mapování můžete přizpůsobit pomocí vlastnosti Namespaces na Dictionary<TKey,TValue>. Pokud se daný obor názvů XSD nachází ve slovníku, odpovídající obor názvů rozhraní .NET Framework se přebírá také z vašeho slovníku.

Představte si například následující schéma.

<xs:schema targetNamespace="http://schemas.contoso.com/carSchema">
  <xs:complexType name="Vehicle">
    <!-- details omitted... -->
  </xs:complexType>
</xs:schema>

Následující příklad mapuje obor názvů Namespaces na "Contoso.Cars" pomocí vlastnosti http://schemas.contoso.com/carSchema.

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

Přidání SerializableAttribute (GenerateSerializable nebo přepínač "/serializable")

To odpovídá přepínači /serializable u nástroje Svcutil.exe.

Někdy je důležité, aby typy generované ze schématu byly použitelné se serializačními nástroji pro runtime rozhraní .NET Framework. To je užitečné při použití typů pro vzdálené volání v rámci .NET Framework. Musíte použít atribut SerializableAttribute na vygenerované typy kromě obvyklého atributu DataContractAttribute. Atribut se vygeneruje automaticky, pokud je možnost importu GenerateSerializable nastavena na true.

Následující příklad ukazuje Vehicle třídu vygenerovanou s možností importu nastavenou GenerateSerializable na 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

Přidání podpory datového vázání (EnableDataBinding nebo přepínač /enableDataBinding)

To odpovídá přepínači /enableDataBinding v nástroji Svcutil.exe.

Někdy můžete chtít svázat typy vygenerované ze schématu na grafické součásti uživatelského rozhraní, aby všechny aktualizace instancí těchto typů automaticky aktualizovaly uživatelské rozhraní. XsdDataContractImporter Může generovat typy, které implementují INotifyPropertyChanged rozhraní takovým způsobem, že jakákoli změna vlastnosti aktivuje událost. Pokud generujete typy pro použití s programovacím prostředím klientského uživatelského rozhraní, které podporuje toto rozhraní (například Windows Presentation Foundation (WPF)), nastavte EnableDataBinding vlastnost na true povolení této funkce.

Následující příklad ukazuje Vehicle třídu vygenerovanou s EnableDataBinding nastavenou na 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

Možnosti importu: Volba typů kolekcí

Dva speciální vzory v jazyce XML představují kolekce položek: seznamy položek a přidružení mezi jednou položkou a druhou. Následuje příklad seznamu řetězců.

<People>
  <person>Alice</person>
  <person>Bob</person>
  <person>Charlie</person>
</People>

Následuje příklad asociace mezi řetězcem a celým číslem (city name a 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>

Poznámka:

Jakékoliv přidružení by se také mohlo považovat za seznam. Předchozí přidružení můžete například zobrazit jako seznam složitých city objektů, u kterých se stane, že mají dvě pole (řetězcové pole a celé číslo). Oba vzory mají reprezentaci ve schématu XSD. Neexistuje způsob, jak rozlišovat mezi seznamem a přidružením, takže takové vzory jsou vždy považovány za seznamy, pokud není ve schématu přítomna zvláštní poznámka specifická pro WCF. Poznámka označuje, že daný vzor představuje přidružení. Další informace naleznete v tématu Referenční informace ke schématu kontraktu dat.

Obvykle se seznam importuje jako kontrakt dat kolekce, který je odvozený z obecného seznamu nebo jako pole rozhraní .NET Framework, v závislosti na tom, zda schéma dodržuje standardní vzor pojmenování pro kolekce. Toto je podrobněji popsáno v typech kolekcí v kontraktech dat. Přidružení se obvykle importují buď jako Dictionary<TKey,TValue> nebo jako kontrakt dat kolekce, který je odvozen z objektu slovníku. Představte si například následující schéma.

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

Tato možnost by se naimportovala následujícím způsobem (pole se místo vlastností pro čitelnost zobrazují).

[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

Typy kolekcí vygenerované pro takové vzory schématu je možné přizpůsobit. Můžete například chtít vygenerovat kolekce odvozené od BindingList<T> třídy místo List<T> třídy, aby bylo možné svázat typ se seznamem a nechat je automaticky aktualizovat při změně obsahu kolekce. Chcete-li to provést, nastavte ReferencedCollectionTypes vlastnost ImportOptions třídy na seznam typů kolekce, které se mají použít (dále označované jako referencované typy). Při importu jakékoli kolekce se tento seznam odkazovaných typů kolekcí zkontroluje a použije se nejvhodnější kolekce, pokud se najde. Přidružení se shodují pouze s typy, které implementují buď generické, nebo nespecifické IDictionary rozhraní, zatímco seznamy se shodují s jakýmkoli podporovaným typem kolekce.

Pokud ReferencedCollectionTypes je například vlastnost nastavena na BindingList<T>, people typ v předchozím příkladu je generován následujícím způsobem.

[CollectionDataContract(ItemName = "person")]
public class people : BindingList<string> { }
<CollectionDataContract(ItemName:="person")> _
Public Class people
    Inherits BindingList(Of String)

Uzavřený obecný termín se považuje za nejlepší shodu. Například pokud jsou typy BindingList(Of Integer) a ArrayList předány do kolekce odkazovaných typů, všechny seznamy celých čísel nalezených ve schématu jsou importovány jako BindingList(Of Integer). Všechny ostatní seznamy, například List(Of String), jsou importovány jako ArrayList.

Pokud je do kolekce odkazovaných typů přidán typ, který implementuje obecné IDictionary rozhraní, musí být parametry typu buď plně otevřené, nebo plně uzavřeny.

Duplicitní položky nejsou povoleny. Například do odkazovaných typů nemůžete přidat ani List(Of Integer) a Collection(Of Integer) . To by znemožnilo určit, které by se mělo použít, když se ve schématu nachází seznam celých čísel. Duplicity budou zjištěny pouze v případě, že existuje typ ve schématu, který zveřejňuje problém s duplicitami. Pokud například importované schéma neobsahuje seznamy celých čísel, je povoleno mít jak List(Of Integer), tak Collection(Of Integer) v kolekci odkazovaných typů, ale žádný z nich nebude mít žádný vliv.

Mechanismus odkazovaných typů kolekcí funguje stejně dobře pro kolekce komplexních typů (včetně kolekcí jiných kolekcí), a ne jen pro kolekce primitiv.

ReferencedCollectionTypes Vlastnost odpovídá přepínači /collectionType v nástroji SvcUtil.exe. Upozorňujeme, že pokud chcete odkazovat na více typů kolekcí, musí být přepínač /collectionType zadán vícekrát. Pokud typ není v MsCorLib.dll, musíte také odkázat na jeho sestavení pomocí přepínače /reference.

Možnosti importu: Odkazování na existující typy

Někdy typy ve schématu odpovídají existujícím typům rozhraní .NET Framework a není nutné tyto typy generovat úplně od začátku. (Tato část se vztahuje pouze na ne-kolekční typy. Pro kolekční typy, viz předchozí část.)

Můžete mít například standardní datový kontrakt typu "Osoba", který chcete vždy použít při reprezentaci osoby. Kdykoli některá služba používá tento typ a jeho schéma se zobrazí v metadatech služby, můžete při importu tohoto schématu místo generování nového pro každou službu použít existující Person typ.

Uděláte to tak, že předáte seznam typů rozhraní .NET Framework, které chcete znovu použít do kolekce ReferencedTypes, vracenou vlastností třídy ImportOptions. Pokud některý z těchto typů má název datového kontraktu a obor názvů, který odpovídá názvu a oboru názvů typu schématu, provede se strukturální porovnání. Pokud se zjistí, že typy mají odpovídající názvy i odpovídající struktury, stávající typ rozhraní .NET Framework se znovu použije místo generování nové. Pokud se shoduje pouze název, ale ne struktura, vyvolá se výjimka. Všimněte si, že při odkazování na typy (například přidání nových nepovinných datových členů) není povolená správa verzí. Struktury se musí přesně shodovat.

Do kolekce odkazovaných typů je možné přidat více typů se stejným názvem kontraktu dat a oborem názvů, pokud se s tímto názvem a oborem názvů neimportují žádné typy schématu. To vám umožní snadno přidat všechny typy v modulu do kolekce, aniž byste se museli starat o duplicity pro typy, které ve schématu ve skutečnosti neexistují.

ReferencedTypes Vlastnost odpovídá přepínači /reference v určitých režimech provozu nástroje Svcutil.exe.

Poznámka:

Při použití nástroje Svcutil.exe nebo nástroje Add Service Reference (v prostředí Visual Studio) jsou automaticky referencovány všechny typy v MsCorLib.dll.

Možnosti importu: Importování schématu, který není DataContract, jako typy IXmlSerializable

XsdDataContractImporter podporuje omezenou podmnožinu schématu. Pokud existují nepodporované konstrukce schématu (například atributy XML), pokus o import selže s výjimkou. Nastavením vlastnosti ImportXmlType na true se rozšiřuje rozsah podporovaného schématu. Při nastavení na true, generuje XsdDataContractImporter typy, které implementují IXmlSerializable rozhraní. To umožňuje přímý přístup k reprezentaci XML těchto typů.

Aspekty návrhu
  • Může být obtížné pracovat přímo se slabě napsanou reprezentací XML. Zvažte použití alternativního serializačního modulu, jako například XmlSerializer, pro práci se schématy, která nejsou kompatibilní s datovými kontrakty, způsobem silného typování. Další informace naleznete v tématu Použití Třídy XmlSerializer.

  • Některé konstrukce schématu nelze XsdDataContractImporter importovat, i když je vlastnost ImportXmlType nastavena na true. Opět zvažte použití XmlSerializer v těchto případech.

  • Přesné konstrukce schématu, které jsou podporovány, pokud ImportXmlType je true nebo false jsou popsány v odkazu schématu kontraktu dat.

  • Schéma vygenerovaných IXmlSerializable typů si při importu a exportu nezachovává věrnost. To znamená, že export schématu z vygenerovaných typů a importování jako třídy nevrací původní schéma.

Je možné zkombinovat možnost ImportXmlType s dříve popsanou možností ReferencedTypes. U typů, které je potřeba vygenerovat jako IXmlSerializable implementace, se strukturální kontrola při použití ReferencedTypes funkce přeskočí.

Možnost ImportXmlType odpovídá přepínači /importXmlTypes v nástroji Svcutil.exe.

Práce s vygenerovanými typy IXmlSerializable

Vygenerované IXmlSerializable typy obsahují privátní pole s názvem nodesField, které vrací pole XmlNode objektů. Při deserializaci instance takového typu můžete přistupovat k datům XML přímo prostřednictvím tohoto pole pomocí objektového modelu dokumentu XML. Při serializaci instance tohoto typu můžete toto pole nastavit na požadovaná data XML a toto pole pak bude serializováno.

Toho se dosahuje prostřednictvím IXmlSerializable implementace. Ve vygenerovaném typu IXmlSerializable volá implementace ReadXml metodu ReadNodes třídy XmlSerializableServices. Tato metoda je pomocná metoda, která převádí XML poskytované prostřednictvím XmlReader na pole objektů XmlNode. Implementace WriteXml provede opak a převede pole XmlNode objektů na posloupnost XmlWriter volání. Toho se dosahuje pomocí WriteNodes metody.

Proces exportu schématu je možné spustit ve vygenerovaných IXmlSerializable třídách. Jak už bylo uvedeno dříve, původní schéma se zpět nedostane. Místo toho získáte standardní typ XSD typu anyType, což je zástupný znak pro libovolný typ XSD.

Toho lze dosáhnout použitím atributu XmlSchemaProviderAttribute na vygenerované IXmlSerializable třídy a určení metody, která volá AddDefaultSchema metodu pro vygenerování typu anyType.

Poznámka:

Typ XmlSerializableServices existuje výhradně pro podporu této konkrétní funkce. Nedoporučuje se používat pro žádný jiný účel.

Možnosti importu: Upřesnit možnosti

Níže jsou uvedené pokročilé možnosti importu:

Viz také