次の方法で共有


スキーマをインポートしてクラスを生成する

Windows Communication Foundation (WCF) で使用できるスキーマからクラスを生成するには、 XsdDataContractImporter クラスを使用します。 このトピックでは、プロセスとバリエーションについて説明します。

インポート プロセス

スキーマのインポート プロセスは、 XmlSchemaSet から始まり、 CodeCompileUnitを生成します。

XmlSchemaSetは、XML スキーマ定義言語 (XSD) スキーマ ドキュメントのセットを表す .NET Framework のスキーマ オブジェクト モデル (SOM) の一部です。 一連の XSD ドキュメントからXmlSchemaSet オブジェクトを作成するには、各ドキュメントを (XmlSerializer を使用して) XmlSchema オブジェクトに逆シリアル化し、これらのオブジェクトを新しいXmlSchemaSetに追加します。

CodeCompileUnitは、.NET Framework コードを抽象的な方法で表す .NET Framework のコード ドキュメント オブジェクト モデル (CodeDOM) の一部です。 CodeCompileUnitから実際のコードを生成するには、CSharpCodeProviderVBCodeProvider クラスなどのCodeDomProvider クラスのサブクラスを使用します。

スキーマをインポートするには

  1. XsdDataContractImporterのインスタンスを作成します。

  2. 任意。 コンストラクターに CodeCompileUnit を渡します。 スキーマのインポート中に生成された型は、空白のCodeCompileUnitで始まるのではなく、このCodeCompileUnitインスタンスに追加されます。

  3. 任意。 CanImport メソッドのいずれかを呼び出します。 このメソッドは、指定されたスキーマが有効なデータ コントラクト スキーマであり、インポートできるかどうかを判断します。 CanImport メソッドには、Importと同じオーバーロードがあります (次の手順)。

  4. Import(XmlSchemaSet) メソッドなど、オーバーロードされた Import メソッドのいずれかを呼び出します。

    最も単純なオーバーロードは、 XmlSchemaSet を受け取り、そのスキーマ セット内にある匿名型を含むすべての型をインポートします。 その他のオーバーロードを使用すると、XSD 型またはインポートする型の一覧を指定できます ( XmlQualifiedName または XmlQualifiedName オブジェクトのコレクションの形式)。 この場合、指定した型のみがインポートされます。 オーバーロードは、特定の要素をXmlSchemaSetからインポートするXmlSchemaElementと、関連付けられた型 (匿名かどうか) を受け取ります。 このオーバーロードは、この要素に対して生成された型のデータ コントラクト名を表す XmlQualifiedNameを返します。

    Import メソッドを複数回呼び出すと、同じCodeCompileUnitに複数の項目が追加されます。 型が既に存在する場合、型は CodeCompileUnit に生成されません。 複数のXsdDataContractImporter オブジェクトを使用する代わりに、同じXsdDataContractImporterImportを複数回呼び出します。 これは、重複する型が生成されないようにするための推奨される方法です。

    インポート中にエラーが発生した場合、 CodeCompileUnit は予測できない状態になります。 インポートの失敗に起因する CodeCompileUnit を使用すると、セキュリティの脆弱性が発生する可能性があります。

  5. CodeCompileUnit プロパティを使用してCodeCompileUnitにアクセスします。

インポート オプション: 生成された型のカスタマイズ

XsdDataContractImporterOptions プロパティを 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

名前空間の制御 (名前空間または /namespace スイッチ)

これは、Svcutil.exe ツールの /namespace スイッチに対応します。

通常、スキーマから生成された型は .NET Framework 名前空間に生成されます。各 XSD 名前空間は、 データ コントラクト スキーマ リファレンスで説明されているマッピングに従って、特定の .NET Framework 名前空間に対応します。 このマッピングは、 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 リモート処理に型を使用する場合に便利です。 これを有効にするには、通常のDataContractAttribute属性に加えて、生成された型にSerializableAttribute属性を適用する必要があります。 GenerateSerializableインポート オプションが true に設定されている場合、属性は自動的に生成されます。

次の例は、GenerateSerializable インポート オプションを true に設定して生成されたVehicle クラスを示しています。

[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 に設定してこの機能を有効にします。

次の例は、EnableDataBindingtrue に設定された状態で生成されたVehicle クラスを示しています。

[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 の 2 つの特殊なパターンは、項目のコレクションを表します。項目のリストと、1 つの項目と別の項目の間の関連付け。 文字列の一覧の例を次に示します。

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

関連付けはすべてリストと見なすこともできます。 たとえば、上記の関連付けを、2 つのフィールド (文字列フィールドと整数フィールド) がある複合 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

このようなスキーマ パターンに対して生成されるコレクション型をカスタマイズできます。 たとえば、List<T> クラスではなくBindingList<T>から派生したコレクションを生成して、型をリスト ボックスにバインドし、コレクションの内容が変更されたときに自動的に更新されるようにすることができます。 これを行うには、ImportOptions クラスのReferencedCollectionTypes プロパティを使用するコレクション型の一覧 (以下、参照される型と呼ばれます) に設定します。 コレクションをインポートするときに、参照されるコレクションの種類のこの一覧がスキャンされ、最適に一致するコレクションが見つかった場合に使用されます。 関連付けは、ジェネリックまたは非ジェネリック 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" データ コントラクトの種類があり、ユーザーを表すときに常に使用する必要があるとします。 一部のサービスがこの型を使用し、そのスキーマがサービス メタデータに表示される場合は常に、すべてのサービスに対して新しいスキーマを生成するのではなく、このスキーマをインポートするときに既存の Person 型を再利用できます。

これを行うには、再利用する .NET Framework 型の一覧を、ImportOptions クラスで返されるReferencedTypes プロパティのコレクションに渡します。 これらの型のいずれかに、スキーマ型の名前と名前空間と一致するデータ コントラクト名と名前空間がある場合は、構造比較が実行されます。 型に一致する名前と一致する構造体の両方が含まれていると判断された場合は、新しい .NET Framework 型を生成する代わりに、既存の .NET Framework 型が再利用されます。 名前だけが構造体と一致するが、構造体に一致しない場合は、例外がスローされます。 型を参照する場合 (たとえば、新しい省略可能なデータ メンバーを追加するなど) は、バージョン管理の許容量がないことに注意してください。 構造体は正確に一致する必要があります。

同じデータ コントラクト名と名前空間を持つ複数の型を参照先の型コレクションに追加することは、その名前と名前空間でスキーマ型がインポートされない限り有効です。 これにより、実際にスキーマで発生しない型の重複を気にすることなく、アセンブリ内のすべての型をコレクションに簡単に追加できます。

ReferencedTypes プロパティは、Svcutil.exe ツールの特定の動作モードでの /reference スイッチに対応します。

Svcutil.exe または (Visual Studio で) サービス参照の追加 ツールを使用すると、MsCorLib.dll 内のすべての型が自動的に参照されます。

インポート オプション: 非 DataContract スキーマを IXmlSerializable 型としてインポートする

XsdDataContractImporterでは、スキーマの限られたサブセットがサポートされます。 サポートされていないスキーマコンストラクト (XML 属性など) が存在する場合、インポートの試行は例外で失敗します。 ただし、 ImportXmlType プロパティを true に設定すると、サポートされるスキーマの範囲が拡張されます。 trueに設定すると、XsdDataContractImporterIXmlSerializable インターフェイスを実装する型を生成します。 これにより、これらの型の XML 表現に直接アクセスできます。

設計に関する考慮事項
  • 弱く型指定された XML 表現を直接操作するのは困難な場合があります。 XmlSerializerなどの代替シリアル化エンジンを使用して、厳密に型指定された方法でデータ コントラクトと互換性のないスキーマを操作することを検討してください。 詳細については、「 XmlSerializer クラスの使用」を参照してください。

  • ImportXmlType プロパティが true に設定されている場合でも、一部のスキーマ コンストラクトはXsdDataContractImporterによってインポートできません。 ここでも、このような場合は XmlSerializer の使用を検討してください。

  • ImportXmlTypetrueまたはfalseの両方でサポートされる正確なスキーマ構造については、「データ コントラクト スキーマ リファレンス」を参照してください

  • 生成された IXmlSerializable 型のスキーマは、インポートおよびエクスポート時に忠実性を保持しません。 つまり、生成された型からスキーマをエクスポートし、クラスとしてインポートしても元のスキーマは返されません。

ImportXmlType オプションと、前述の ReferencedTypes オプションを組み合わせることができます。 IXmlSerializable実装として生成する必要がある型の場合、ReferencedTypes機能を使用する場合、構造チェックはスキップされます。

ImportXmlType オプションは、Svcutil.exe ツールの /importXmlTypes スイッチに対応します。

生成された IXmlSerializable 型の操作

生成された IXmlSerializable 型には、 XmlNode オブジェクトの配列を返す "nodesField" という名前のプライベート フィールドが含まれています。 このような型のインスタンスを逆シリアル化する場合は、XML ドキュメント オブジェクト モデルを使用して、このフィールドを介して XML データに直接アクセスできます。 この型のインスタンスをシリアル化するときに、このフィールドを目的の XML データに設定すると、シリアル化されます。

これは、 IXmlSerializable 実装によって実現されます。 生成されたIXmlSerializable型では、ReadXml実装は、XmlSerializableServices クラスのReadNodes メソッドを呼び出します。 このメソッドは、 XmlReader を介して提供された XML を XmlNode オブジェクトの配列に変換するヘルパー メソッドです。 WriteXml実装では逆の処理が行われ、XmlNode オブジェクトの配列が一連のXmlWriter呼び出しに変換されます。 これは、 WriteNodes メソッドを使用して行います。

生成された IXmlSerializable クラスでスキーマ エクスポート プロセスを実行できます。 前述のように、元のスキーマは返されません。 代わりに、"anyType" 標準 XSD 型が取得されます。これは、任意の XSD 型のワイルドカードです。

これを実現するには、生成されたIXmlSerializable クラスにXmlSchemaProviderAttribute属性を適用し、AddDefaultSchema メソッドを呼び出して "anyType" 型を生成するメソッドを指定します。

XmlSerializableServices型は、この特定の機能をサポートするためだけに存在します。 他の目的で使用することはお勧めしません。

インポート オプション: 詳細オプション

高度なインポート オプションを次に示します。

こちらも参照ください