Importera schema för att generera klasser
Om du vill generera klasser från scheman som kan användas med Windows Communication Foundation (WCF) använder du XsdDataContractImporter klassen . Det här avsnittet beskriver processen och variationerna.
Importprocessen
Schemaimportprocessen börjar med en XmlSchemaSet och skapar en CodeCompileUnit.
XmlSchemaSet
är en del av .NET Frameworks schemaobjektmodell (SOM) som representerar en uppsättning schemadokument för XML-schemadefinitionsspråk (XSD). Om du vill skapa ett XmlSchemaSet
objekt från en uppsättning XSD-dokument deserialiserar du varje dokument till ett XmlSchema objekt (med hjälp av XmlSerializer) och lägger till dessa objekt i en ny XmlSchemaSet
.
CodeCompileUnit
är en del av .NET Frameworks Code Document Object Model (CodeDOM) som representerar .NET Framework-kod på ett abstrakt sätt. Om du vill generera den faktiska koden från en CodeCompileUnit
använder du en underklass av CodeDomProvider klassen, till exempel CSharpCodeProvider klassen eller VBCodeProvider .
Importera ett schema
Skapa en instans av XsdDataContractImporter.
Valfritt. Skicka en
CodeCompileUnit
i konstruktorn. De typer som genereras under schemaimport läggs till i den härCodeCompileUnit
instansen i stället för att börja med en tomCodeCompileUnit
.Valfritt. Anropa en av CanImport metoderna. Metoden avgör om det angivna schemat är ett giltigt datakontraktsschema och kan importeras. Metoden
CanImport
har samma överlagringar somImport
(nästa steg).Anropa en av de överlagrade
Import
metoderna, till exempel Import(XmlSchemaSet) metoden.Den enklaste överlagringen tar en
XmlSchemaSet
och importerar alla typer, inklusive anonyma typer, som finns i den schemauppsättningen. Med andra överlagringar kan du ange XSD-typen eller en lista med typer som ska importeras (i form av en XmlQualifiedName eller en samlingXmlQualifiedName
objekt). I det här fallet importeras endast de angivna typerna. En överlagring tar en XmlSchemaElement som importerar ett visst element frånXmlSchemaSet
, samt dess associerade typ (oavsett om den är anonym eller inte). Den här överlagringen returnerar ettXmlQualifiedName
, som representerar namnet på datakontraktet för den typ som genererats för det här elementet.Flera anrop av
Import
metoden resulterar i att flera objekt läggs till i sammaCodeCompileUnit
. En typ genereras inte tillCodeCompileUnit
om den redan finns där. AnropaImport
flera gånger på sammaXsdDataContractImporter
i stället för att använda fleraXsdDataContractImporter
objekt. Det här är det rekommenderade sättet att undvika att dubbletter genereras.Kommentar
Om det uppstår ett fel under importen är det
CodeCompileUnit
i ett oförutsägbart tillstånd. Om du använder ettCodeCompileUnit
resultat av en misslyckad import kan du utsätta dig för säkerhetsproblem.Få åtkomst till
CodeCompileUnit
via CodeCompileUnit -egenskapen.
Importalternativ: Anpassa de genererade typerna
Du kan ange Options egenskapen XsdDataContractImporter för till en instans av ImportOptions klassen för att kontrollera olika aspekter av importprocessen. Ett antal alternativ påverkar direkt de typer som genereras.
Styra åtkomstnivån (GenerateInternal eller /internal switch)
Detta motsvarar / internal-växeln på Verktyget för ServiceModel-metadata (Svcutil.exe).
Normalt genereras offentliga typer från schemat, med privata fält och matchande egenskaper för offentliga datamedlemmar. Om du vill generera interna typer i stället anger du GenerateInternal egenskapen till true
.
I följande exempel visas ett schema som omvandlats till en intern klass när egenskapen är inställd på GenerateInternaltrue.
[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
Kontrollera namnområden (namnområden eller växeln /namespace)
Detta motsvarar /namespace-växeln på Svcutil.exe
verktyget.
Normalt genereras typer som genereras från schemat till .NET Framework-namnområden, där varje XSD-namnområde motsvarar ett visst .NET Framework-namnområde enligt en mappning som beskrivs i Data Contract Schema Reference. Du kan anpassa den här mappningen efter egenskapen Namespaces till en Dictionary<TKey,TValue>. Om ett angivet XSD-namnområde hittas i ordlistan tas även det matchande .NET Framework-namnområdet från din ordlista.
Tänk till exempel på följande schema.
<xs:schema targetNamespace="http://schemas.contoso.com/carSchema">
<xs:complexType name="Vehicle">
<!-- details omitted... -->
</xs:complexType>
</xs:schema>
I följande exempel används Namespaces
egenskapen för att mappa http://schemas.contoso.com/carSchema
namnområdet till "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"))
Lägga till SerializableAttribute (GenerateSerializable eller växeln /serializable)
Detta motsvarar / serializable-växeln på Svcutil.exe
verktyget.
Ibland är det viktigt att de typer som genereras från schemat kan användas med .NET Framework-körningsserialiseringsmotorer. Detta är användbart när du använder typer för .NET Framework-fjärrkommunikation. Om du vill aktivera detta måste du tillämpa SerializableAttribute attributet på de genererade typerna utöver det vanliga DataContractAttribute attributet. Attributet genereras automatiskt om importalternativet GenerateSerializable
är inställt på true
.
I följande exempel visas klassen Vehicle
som genererats med importalternativet GenerateSerializable
inställt på 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
Lägga till stöd för databindning (EnableDataBinding eller växeln /enableDataBinding)
Detta motsvarar växeln /enableDataBinding på verktyget Svcutil.exe.
Ibland kanske du vill binda de typer som genereras från schemat till grafiska användargränssnittskomponenter så att alla uppdateringar av instanser av dessa typer automatiskt uppdaterar användargränssnittet. XsdDataContractImporter
Kan generera typer som implementerar INotifyPropertyChanged gränssnittet på ett sådant sätt att alla egenskapsändringar utlöser en händelse. Om du genererar typer för användning med en programmeringsmiljö för klientgränssnittet som stöder det här gränssnittet (till exempel Windows Presentation Foundation (WPF)) anger du EnableDataBinding egenskapen till true
för att aktivera den här funktionen.
I följande exempel visas klassen Vehicle
som genererats med EnableDataBinding värdet 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
Importalternativ: Välja samlingstyper
Två särskilda mönster i XML representerar samlingar av objekt: listor över objekt och associationer mellan ett objekt och ett annat. Följande är ett exempel på en lista med strängar.
<People>
<person>Alice</person>
<person>Bob</person>
<person>Charlie</person>
</People>
Följande är ett exempel på en association mellan en sträng och ett heltal (city name
och 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>
Kommentar
Alla associationer kan också betraktas som en lista. Du kan till exempel visa den föregående associationen som en lista över komplexa city
objekt som råkar ha två fält (ett strängfält och ett heltalsfält). Båda mönstren har en representation i XSD-schemat. Det finns inget sätt att skilja mellan en lista och en association, så sådana mönster behandlas alltid som listor om inte en särskild anteckning som är specifik för WCF finns i schemat. Kommentaren anger att ett givet mönster representerar en association. Mer information finns i Schemareferens för datakontrakt.
Normalt importeras en lista som ett samlingsdatakontrakt som härleds från en allmän lista eller som en .NET Framework-matris, beroende på om schemat följer standardnamnmönstret för samlingar eller inte. Detta beskrivs mer detaljerat i Samlingstyper i datakontrakt. Associationer importeras normalt som antingen ett Dictionary<TKey,TValue> eller ett samlingsdatakontrakt som härleds från ordlisteobjektet. Tänk till exempel på följande schema.
<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>
Detta importeras på följande sätt (fält visas i stället för egenskaper för läsbarhet).
[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
Det går att anpassa de samlingstyper som genereras för sådana schemamönster. Du kanske till exempel vill generera samlingar som härleds från BindingList<T> i stället för List<T> klassen för att binda typen till en listruta och få den automatiskt uppdaterad när innehållet i samlingen ändras. Det gör du genom att ange ReferencedCollectionTypes egenskapen ImportOptions för klassen till en lista över samlingstyper som ska användas (kallas därefter för de refererade typerna). När du importerar en samling genomsöks den här listan över refererade samlingstyper och den bäst matchande samlingen används om någon hittas. Associationer matchas endast mot typer som implementerar antingen det generiska gränssnittet eller det icke-generiska IDictionary gränssnittet, medan listor matchas mot alla typer av samlingar som stöds.
Om egenskapen ReferencedCollectionTypes till exempel är inställd på en BindingList<T>people
genereras typen i föregående exempel på följande sätt.
[CollectionDataContract(ItemName = "person")]
public class people : BindingList<string> { }
<CollectionDataContract(ItemName:="person")> _
Public Class people
Inherits BindingList(Of String)
En sluten generisk anses vara den bästa matchningen. Om till exempel typerna BindingList(Of Integer)
och ArrayList skickas till samlingen med refererade typer importeras alla listor med heltal som finns i schemat som en BindingList(Of Integer)
. Andra listor, till exempel en List(Of String)
, importeras som en ArrayList
.
Om en typ som implementerar det generiska IDictionary
gränssnittet läggs till i samlingen med refererade typer måste dess typparametrar antingen vara helt öppna eller helt stängda.
Dubbletter tillåts inte. Du kan till exempel inte lägga till både en List(Of Integer)
och en Collection(Of Integer)
till de refererade typerna. Det skulle göra det omöjligt att avgöra vilken som ska användas när en lista över heltal hittas i schemat. Dubbletter identifieras endast om det finns en typ i schemat som exponerar duplicerade problem. Om det importerade schemat till exempel inte innehåller listor över heltal, tillåts det att ha både List(Of Integer)
och Collection(Of Integer)
i samlingen med refererade typer, men ingen av dem har någon effekt.
Mekanismen för referenssamlingstyper fungerar lika bra för samlingar av komplexa typer (inklusive samlingar av andra samlingar) och inte bara för samlingar av primitiver.
Egenskapen ReferencedCollectionTypes
motsvarar växeln /collectionType på verktyget SvcUtil.exe. Observera att om du vill referera till flera samlingstyper måste /collectionType-växeln anges flera gånger. Om typen inte finns i MsCorLib.dll måste dess sammansättning också refereras med växeln /reference .
Importalternativ: Referera till befintliga typer
Ibland motsvarar typerna i schemat befintliga .NET Framework-typer, och det finns inget behov av att generera dessa typer från grunden. (Det här avsnittet gäller endast för icke-insamlingstyper. För samlingstyper, se föregående avsnitt.)
Du kan till exempel ha en standardföretagsomfattande "Person"-datakontraktstyp som du alltid vill använda när du representerar en person. När någon tjänst använder den här typen, och schemat visas i tjänstens metadata, kanske du vill återanvända den befintliga Person
typen när du importerar det här schemat i stället för att generera en ny för varje tjänst.
Det gör du genom att skicka en lista över .NET Framework-typer som du vill återanvända till samlingen som ReferencedTypes egenskapen returnerar i ImportOptions klassen. Om någon av dessa typer har ett datakontraktsnamn och ett namnområde som matchar namnet och namnområdet för en schematyp utförs en strukturell jämförelse. Om det fastställs att typerna har både matchande namn och matchande strukturer återanvänds den befintliga .NET Framework-typen i stället för att generera en ny. Om bara namnet matchar men inte strukturen genereras ett undantag. Observera att det inte finns någon ersättning för versionshantering när du refererar till typer (till exempel att lägga till nya valfria datamedlemmar). Strukturerna måste matcha exakt.
Det är lagligt att lägga till flera typer med samma namn och namnområde för datakontraktet i samlingen med refererade typer, så länge inga schematyper importeras med det namnet och namnområdet. På så sätt kan du enkelt lägga till alla typer i en sammansättning i samlingen utan att behöva oroa dig för dubbletter för typer som faktiskt inte förekommer i schemat.
Egenskapen ReferencedTypes
motsvarar /reference-växeln i vissa driftlägen för Svcutil.exe-verktyget.
Kommentar
När du använder Svcutil.exe eller (i Visual Studio) verktygen Lägg till tjänstreferens refereras alla typer i MsCorLib.dll automatiskt.
Importalternativ: Importera icke-DataContract-schema som IXmlSerializable-typer
Stöder XsdDataContractImporter en begränsad delmängd av schemat. Om schemakonstruktioner som inte stöds finns (till exempel XML-attribut) misslyckas importförsöket med ett undantag. Om du ställer in egenskapen ImportXmlType på true
utökas dock det schemaintervall som stöds. När värdet är inställt true
på genererar de XsdDataContractImporter typer som implementerar IXmlSerializable gränssnittet. Detta ger direkt åtkomst till XML-representationen av dessa typer.
Designöverväganden
Det kan vara svårt att arbeta med den svagt inskrivna XML-representationen direkt. Överväg att använda en alternativ serialiseringsmotor, till exempel XmlSerializer, för att arbeta med schema som inte är kompatibelt med datakontrakt på ett starkt skrivet sätt. Mer information finns i Använda XmlSerializer-klassen.
Vissa schemakonstruktioner kan inte importeras av XsdDataContractImporter även när egenskapen ImportXmlType är inställd på
true
. Överväg återigen att använda XmlSerializer för sådana fall.De exakta schemakonstruktioner som stöds både när ImportXmlType är
true
ellerfalse
beskrivs i Schemareferens för datakontrakt.Schemat för genererade IXmlSerializable typer behåller inte återgivning när det importeras och exporteras. Det innebär att export av schemat från de genererade typerna och import som klasser inte returnerar det ursprungliga schemat.
Det går att kombinera alternativet ImportXmlType med det alternativ som ReferencedTypes beskrevs tidigare. För typer som måste genereras som IXmlSerializable implementeringar hoppas den strukturella kontrollen över när funktionen används ReferencedTypes .
Alternativet ImportXmlType motsvarar växeln /importXmlTypes på verktyget Svcutil.exe.
Arbeta med genererade IXmlSerializable-typer
De genererade IXmlSerializable
typerna innehåller ett privat fält med namnet "nodesField", som returnerar en matris med XmlNode objekt. När du deserialiserar en instans av en sådan typ kan du komma åt XML-data direkt via det här fältet med hjälp av XML-dokumentobjektmodellen. När du serialiserar en instans av den här typen kan du ange det här fältet till önskade XML-data och det kommer att serialiseras.
Detta görs genom implementeringen IXmlSerializable
. I den genererade IXmlSerializable
typen anropar ReadNodes implementeringen ReadXml -metoden för XmlSerializableServices klassen. Metoden är en hjälpmetod som konverterar XML som tillhandahålls via en XmlReader till en matris med XmlNode objekt. Implementeringen WriteXml gör motsatsen och konverterar matrisen med XmlNode
objekt till en sekvens med XmlWriter anrop. Detta görs med hjälp av WriteNodes metoden .
Det går att köra schemaexportprocessen på de genererade IXmlSerializable
klasserna. Som tidigare nämnts får du inte tillbaka det ursprungliga schemat. I stället får du XSD-standardtypen "anyType", som är ett jokertecken för alla XSD-typer.
Detta uppnås genom att tillämpa XmlSchemaProviderAttribute attributet på de genererade IXmlSerializable
klasserna och ange en metod som anropar AddDefaultSchema metoden för att generera typen "anyType".
Kommentar
Typen XmlSerializableServices finns enbart för att stödja den här funktionen. Det rekommenderas inte för användning i något annat syfte.
Importalternativ: Avancerade alternativ
Följande är avancerade importalternativ:
CodeProvider Egenskapen. Ange vilken kod som CodeDomProvider ska användas för att generera koden för de genererade klasserna. Importmekanismen försöker undvika funktioner som CodeDomProvider inte stöds. Om inte CodeProvider har angetts används den fullständiga uppsättningen .NET Framework-funktioner utan begränsningar.
DataContractSurrogate Egenskapen. En IDataContractSurrogate implementering kan anges med den här egenskapen. Anpassar IDataContractSurrogate importprocessen. Mer information finns i Surrogater för datakontrakt. Som standard används ingen surrogat.