匯入結構描述以產生類別

如果要從可在 Windows Communication Foundation (WCF) 中使用的結構描述產生類別,請使用 XsdDataContractImporter 類別。 這個主題將說明處理程序和變化。

匯入程序

結構描述匯入程序是從 XmlSchemaSet 開始,並產生 CodeCompileUnit

XmlSchemaSet 是 .NET Framework 的結構描述物件模型 (SOM) 的一部分,代表一組 XML 結構描述定義語言 (XSD) 結構描述文件。 如果要從一組 XSD 文件建立 XmlSchemaSet 物件,請還原序列化各文件至 XmlSchema 物件中 (使用 XmlSerializer),然後新增這些物件至新的 XmlSchemaSet

CodeCompileUnit 是 .NET Framework 的程式碼文件物件模型 (CodeDOM) 的一部分,會以抽象方式表示 .NET Framework 程式碼。 如果要從 CodeCompileUnit 產生實際的程式碼,請使用 CodeDomProvider 類別的子類別,例如 CSharpCodeProviderVBCodeProvider 類別。

匯入結構描述

  1. 建立 XsdDataContractImporter的執行個體。

  2. 選擇性。 傳遞建構函式中的 CodeCompileUnit。 在結構描述匯入期間產生的型別會新增至這個 CodeCompileUnit 執行個體,而不是從空白的 CodeCompileUnit 開始。

  3. 選擇性。 呼叫其中一個 CanImport 方法。 此方法會判斷指定的結構描述是否為有效資料合約結構描述,且可以匯入。 CanImport 方法有和 Import 相同的多載 (下一個步驟)。

  4. 呼叫其中一個多載 Import 方法,例如 Import(XmlSchemaSet) 方法。

    最簡單的多載是採用 XmlSchemaSet,並匯入在該結構描述集中找到的所有型別,包括匿名型別。 其他的多載可讓您指定要匯入的 XSD 型別或型別清單 (以 XmlQualifiedNameXmlQualifiedName 物件集合的格式)。 在這個案例中,只會匯入指定的型別。 多載會採用匯入 XmlSchemaElement 之特定項目的 XmlSchemaSet,以及其相關型別 (無論是否為匿名)。 這個多載會傳回 XmlQualifiedName,表示針對這個項目所產生之型別的資料合約名稱。

    Import 方法的多個呼叫會造成將多個項目新增至相同的 CodeCompileUnit。 如果型別已經存在,便不會產生至 CodeCompileUnit 中。 請在相同的 Import 上多次呼叫 XsdDataContractImporter,而不要使用多個 XsdDataContractImporter 物件。 這是避免產生重複型別的建議方式。

    注意

    如果在匯入期間發生失敗,CodeCompileUnit 將會處於無法預測的狀態。 使用源自失敗之匯入的 CodeCompileUnit 可能會讓您暴露在安全性弱點中。

  5. 請透過 CodeCompileUnit 屬性存取 CodeCompileUnit

匯入選項:自訂產生的型別

您可以將 OptionsXsdDataContractImporter 屬性設定為 ImportOptions 類別的執行個體,以控制匯入處理程序的各方面。 選項的數目會直接影響到所產生的型別。

控制存取層級 (GenerateInternal 或 /internal 參數)

這對應於 ServiceModel 中繼資料公用程式工具 (Svcutil.exe) 上的 /internal 參數。

一般來說,公用型別是從結構描述產生的,其中包含私用欄位和相符的公用資料成員屬性。 如果要改為產生內部型別,請將 GenerateInternal 屬性設定為 true

下列範例會顯示當 GenerateInternal 屬性設定為 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

控制命名空間 (Namespaces 或 /namespace 參數)

這對應於 Svcutil.exe 工具上的 /namespace 參數。

一般來說,從結構描述產生的型別會產生至 .NET Framework 命名空間中,其中包含根據資料合約結構描述參考中所述的對應,對應至特定 .NET Framework 命名空間的各個 XSD 命名空間。 您可以將 Namespaces 屬性設定為 Dictionary<TKey,TValue>,以自訂這個對應。 如果在目錄中找到指定的 XSD 命名空間,相符的 .NET Framework 命名空間也是取自您的目錄。

例如,請試想下列結構描述。

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

下列範例會使用 Namespaces 屬性將 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"))

新增 SerializableAttribute (GenerateSerializable 或 /serializable 參數)

這對應於 Svcutil.exe 工具上的 /serializable 參數。

有時對於從結構描述產生的型別來說,可在 .NET Framework 執行階段序列化引擎中使用,是很重要的。 這在對 .NET Framework 遠端使用型別時有其效用。 如果要啟用,除了一般的 SerializableAttribute 屬性之外,您還必須將 DataContractAttribute 屬性套用至所產生的型別。 如果 GenerateSerializable 匯入選項設定為 true,便會自動產生此屬性。

下列範例會顯示由 Vehicle 匯入選項設定為 GenerateSerializable 所產生的 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

新增資料繫結支援 (EnableDataBinding 或 /enableDataBinding 參數)

這對應於 Svcutil.exe 工具上的 /enableDataBinding 參數。

有時,您可能會想要將從結構描述產生的型別,繫結至圖形使用者介面元件,讓這些型別之執行個體的更新可以自動更新 UI。 XsdDataContractImporter 可以產生以任何屬性變更都會觸發事件的方式實作 INotifyPropertyChanged 介面的型別。 如果產生的型別是用於支援此介面 (例如 Windows Presentation Foundation (WPF)) 的用戶端 UI 程式設計環境,請將 EnableDataBinding 屬性設定為 true,以啟用此功能。

下列範例會顯示由 Vehicle 設定為 EnableDataBinding 所產生的 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

匯入選項:選擇集合型別

XML 中有兩種特殊模式表示項目的集合:項目的清單以及兩個項目之間的關聯。 以下是字串清單的範例。

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

以下是字串和整數之間關聯的範例 (city namepopulation)。

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

注意

任何關聯也都可以視為是清單。 例如,您可以將前述關聯視為是剛好有兩個欄位 (字串欄位和整數欄位) 之複雜 city 物件的清單。 這兩種模式在 XSD 結構描述中都有表示法。 由於並沒有方法可以區分清單和關聯,因此這類模式永遠會被視為是清單,除非結構描述中有 WCF 特定的特別附註。 附註會指出指定的模式表示關聯。 如需詳細資訊,請參閱資料合約結構描述參考

一般來說,會匯入清單作為衍生自泛型清單的集合資料合約或作為 .NET Framework 陣列,視結構描述是否遵循集合的標準命名樣式而定。 在資料合約中的集合型別中會更詳細地加以說明。 關聯通常會被匯入做為 Dictionary<TKey,TValue> 或衍生自目錄物件的集合資料合約。 例如,請試想下列結構描述。

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

其匯入如下所示 (顯示欄位而非屬性以具可讀性)。

[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

您可以自訂為此類結構描述模式所產生的集合型別。 例如,您可能會想要產生衍生自 BindingList<T> 而非 List<T> 類別的集合,以便將型別繫結至清單方塊,並讓它在集合的內容變更時自動更新。 如果要執行這項操作,請將 ReferencedCollectionTypes 類別的 ImportOptions 屬性設定為要使用之集合型別的清單 (之後稱為參照型別)。 當匯入集合時,便會掃描這份參照集合型別的清單,並使用最符合的集合 (如果找得到的話)。 關聯只會針對實作一般性或非一般性 IDictionary 介面的型別進行對應,而清單會針對任何支援的集合型別進行對應。

例如,如果 ReferencedCollectionTypes 屬性設定為 BindingList<T>,前例中的 people 型別便會產生如下。

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

封閉式泛型被視為最符合的型別。 例如,如果型別 BindingList(Of Integer)ArrayList 傳遞至參照型別的集合,在結構描述中找到的任何整數清單都會匯入做為 BindingList(Of Integer)。 任何其他清單 (例如 List(Of String)) 都會匯入做為 ArrayList

如果將實作泛型 IDictionary 介面的型別新增至參照型別的集合,其型別參數必須是完全開啟或完全封閉的。

不允許重複。 例如,您無法同時將 List(Of Integer)Collection(Of Integer) 新增至參照型別。 這會讓它無法判斷當在結構描述中找到整數清單時,應使用何者。 只有當結構描述中有公開重複問題的型別時,才會偵測到重複。 例如,如果匯入的結構描述不包含整數的清單,便會允許它在參照型別集合中有 List(Of Integer)Collection(Of Integer),但是這兩者不會造成任何影響。

所參照集合型別機制對於複雜型別的集合 (包括其他集合的集合),運作效果都一樣好,而不只是適合基本型別的集合。

ReferencedCollectionTypes 屬性對應於 SvcUtil.exe 工具上的 /collectionType 參數。 請注意,如果要參考多個集合型別,必須指定多次 /collectionType 參數。 如果型別不在 MsCorLib.dll 中,則其組件也必須使用 /reference 參數來參考。

匯入選項:參照現有型別

結構描述中的型別偶而會對應至現有的 .NET Framework 型別,而不需要從頭開始產生這些型別。 (本節只適用於非集合型別。對於集合型別,請參閱上一節)。

例如,您可能會有標準的全公司「人員」資料合約類型,固定用於表示人員。 只要有服務使用這個型別,且其結構描述出現在服務中繼資料中,當匯入這個結構描述時,您可能會想要重複使用現有的 Person 型別,而非為每個服務產生新的型別。

如果要執行這項操作,請將您要重複使用的 .NET Framework 型別的清單傳至 ReferencedTypes 屬性針對 ImportOptions 類別傳回的集合中。 如果其中任何型別的資料合約名稱和命名空間符合結構描述型別的名稱和命名空間,便會執行結構比較。 如果判斷出型別同時具有相符的名稱和結構,便會重複使用現有的 .NET Framework 型別,而非產生新的型別。 如果只有名稱符合,但結構不符合,則會擲回例外狀況。 請注意,當參照型別時,不允許進行版本控制 (例如,新增選擇性資料成員)。 結構必須完全相符。

將多個有相同資料合約名稱和命名空間的型別新增至參照型別集合是合法的,只要沒有結構描述型別使用該名稱和命名空間匯入即可。 這可讓您輕鬆地將組件中的所有型別新增至集合,而不需擔心並未實際發生在結構描述中的型別有重複。

ReferencedTypes 屬性對應於 Svcutil.exe 工具的特定作業模式中的 /reference 參數。

注意

使用 Svcutil.exe 或 (在 Visual Studio 中) 新增服務參考工具時,會自動參考 MsCorLib.dll 中的所有型別。

匯入選項:匯入非 DataContract 結構描述做為 IXmlSerializable 型別

XsdDataContractImporter 支援結構描述的有限子集。 如果有不支援的結構描述建構存在 (例如,XML 屬性),匯入嘗試便會失敗並有例外狀況。 然而,將 ImportXmlType 屬性設定為 true 可延伸受支援的結構描述範圍。 當設定為 true 時,XsdDataContractImporter 會產生實作 IXmlSerializable 介面的型別。 如此可直接存取這些型別的 XML 表示法。

設計考量
  • 直接使用較弱型別的 XML 表示法可能會很困難。 請考慮使用另一種序列化引擎 (例如 XmlSerializer) 以強型別方式來使用與資料合約不相容的結構描述。 如需詳細資訊,請參閱使用 XmlSerializer 類別

  • 即使 XsdDataContractImporter 屬性設定為 ImportXmlType,有些結構描述建構仍然無法由 true 匯入。 請再次考慮針對此類案例使用 XmlSerializer

  • ImportXmlTypetruefalse 時所支援的確切結構描述建構,說明於資料合約結構描述參考中。

  • 所產生的 IXmlSerializable 型別的結構描述在匯入和匯出時不會保留逼真度。 也就是說,從產生的型別匯出結構描述然後匯入做為類別並不會傳回原始的結構描述。

您或許可以結合 ImportXmlType 選項與之前所述的 ReferencedTypes 選項。 對於必須產生做為 IXmlSerializable 實作的型別,會在使用 ReferencedTypes 功能時跳過結構檢查。

ImportXmlType 選項對應於 Svcutil.exe 工具上的 /importXmlTypes 參數。

使用所產生的 IXmlSerializable 型別

所產生的 IXmlSerializable 型別包含名為 "nodesField" 的私用欄位,它會傳回 XmlNode 物件的陣列。 當還原序列化此類型別的執行個體時,您可以使用 XML 文件物件模型,透過這個欄位直接存取 XML 資料。 當序列化這個型別的執行個體時,您可以將這個欄位設定為需要的 XML 資料,且它將會序列化。

這是透過 IXmlSerializable 實作完成的。 在所產生的 IXmlSerializable 型別中,ReadXml 實作會呼叫 ReadNodes 類別的 XmlSerializableServices 方法。 此方法是 Helper 方法,會將透過 XmlReader 提供的 XML 轉換為 XmlNode 物件的陣列。 WriteXml 實作會執行相反的操作,並將 XmlNode 物件的陣列轉換成 XmlWriter 呼叫的序列。 這是使用 WriteNodes 方法完成的。

您或許可以在所產生的 IXmlSerializable 類別上執行結構描述匯出處理程序。 如前所述,將不會傳回原始的結構描述。 您會收到 "anyType" 標準 XSD 型別,這是任何 XSD 型別的萬用字元。

完成這項工作的方法,是將 XmlSchemaProviderAttribute 屬性套用至產生的 IXmlSerializable 類別,並指定呼叫 AddDefaultSchema 方法以產生 "anyType" 型別的方法。

注意

XmlSerializableServices 型別是為了支援這項特定功能而單獨存在的。 並不建議用於其他方面。

匯入選項:進階選項

以下是進階匯入選項:

另請參閱