クラスを作成するためのスキーマのインポート

スキーマを読み込んで、Windows Communication Foundation (WCF) で使用可能なクラスを生成するには、XsdDataContractImporter クラスを使用します。ここでは、生成時に指定できる各種のオプションについて解説します。

インポート処理

スキーマのインポート処理は、XmlSchemaSet を用意し、CodeCompileUnit を生成することから始まります。

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

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

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

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

  2. 省略可能。CodeCompileUnit をコンストラクタに渡します。スキーマのインポート時に生成された型がこの CodeCompileUnit インスタンスに追加されるので、以降の処理は、CodeCompileUnit が空でない状態から始まります。

  3. 省略可能。CanImport メソッドのいずれかを呼び出します。指定されたスキーマが有効なデータ コントラクト スキーマであって、インポートしても問題ないかどうかを判断するメソッドです。CanImport メソッドには、次の手順に出てくる Import と同様に、オーバーロードがあります。

  4. オーバーロードされた Import メソッドのいずれか、たとえば Import を呼び出します。

    これは、指定しなければならない引数が XmlSchemaSet だけという最も簡潔なオーバーロードであり、匿名型を含め、一連のスキーマに記述されている型をすべてインポートできます。他に、XSD 型、あるいはインポートする型のリストを (XmlQualifiedName、または XmlQualifiedName オブジェクトのコレクションとして) 指定できるオーバーロードもあります。この場合は、指定した型だけがインポートされます。また、引数として XmlSchemaElement を受け取り、XmlSchemaSet のうち特定の要素を、対応する型 (匿名型を含む) と併せてインポートするオーバーロードもあります。このメソッドの戻り値は XmlQualifiedName であり、この要素に対応して生成された型のデータ コントラクト名を表します。

    Import メソッドを何度も呼び出すと、同じ CodeCompileUnit に複数の項目が追加されます。CodeCompileUnit に既に型が存在する場合は、その型は生成されません。したがって、XsdDataContractImporter オブジェクトをいくつも使うのではなく、同じ XsdDataContractImporterImport を必要な回数呼び出すようにしてください。同じ型が重複して生成されることがないので、この方法をお勧めします。

    Aa702680.note(ja-jp,VS.90).gifメモ :
    インポート中に障害が発生した場合の CodeCompileUnit の状態は不定です。このような CodeCompileUnit を使うと、セキュリティ上の脆弱性につながるおそれがあります。

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

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

XsdDataContractImporterOptions プロパティとして ImportOptions クラスのインスタンスを設定することにより、インポート処理の方法を制御できます。生成される型に直接影響するオプションが多数あります。

アクセス レベルの制御 (GenerateInternal または /internal スイッチ)

これは、ServiceModel Metadata Utility Tool (Svcutil.exe)/internal スイッチに相当します。

通常、スキーマから生成されるのはパブリック型です。ここにプライベート フィールドや対応するパブリック データ メンバ プロパティが定義されます。パブリック型ではなく内部型を生成したい場合は、GenerateInternal プロパティを true としてください。

次の例は、GenerateInternal プロパティが true の場合に、スキーマがどのように内部型に変換されるかを表します。

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

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

通常、スキーマから生成された型は .NET Framework 名前空間に属します。マッピングに基づく XSD 名前空間と .NET Framework 名前空間の対応については、「データ コントラクト スキーマの参照」に記載されています。この対応関係は、DictionaryNamespaces プロパティでカスタマイズできます。指定した XSD 名前空間がディクショナリに存在する場合、その XSD 名前空間に対応する .NET Framework 名前空間も、ディクショナリから取得されます。

たとえば、次のスキーマを考えます。

Namespaces プロパティを使って、"http://schemas.contoso.com/carSchema" という名前空間を "Contoso.Cars" に対応付ける例を示します。

SerializableAttribute の追加 (GenerateSerializable または /serializable スイッチ)

これは、Svcutil.exe ツールの /serializable スイッチに相当します。

スキーマから生成された型を、.NET Framework ランタイムのシリアル化エンジン (BinaryFormatter クラス、SoapFormatter クラスなど) でシリアル化しなければならないことがあります。たとえば、.NET Framework リモート処理に使うような場合がこれに当たります。そのためには、生成された型に対して、通常の DataContractAttribute 属性に加え、SerializableAttribute 属性も適用する必要があります。GenerateSerializable オプションを true に設定すると、この属性が自動的に生成されます。

GenerateSerializable オプションを true に設定して Vehicle クラスを生成する例を示します。

データ バインディング機能の追加 (EnableDataBinding または /enableDataBinding スイッチ)

これは、Svcutil.exe ツールの /enableDataBinding スイッチに相当します。

スキーマから生成された型を GUI コンポーネントにバインドして、この型のインスタンスを更新したとき、自動的に UI にも反映されるようにする場合があります。XsdDataContractImporter は、生成する型に INotifyPropertyChanged インターフェイスを実装して、プロパティが変更されるとイベントが発生するようにすることができます。このインターフェイスを実装した型 (たとえば Windows Presentation Foundation (WPF)) を生成して、クライアント側の UI プログラミング環境で使用できるようにする場合は、EnableDataBinding プロパティの値を true に設定してください。

EnableDataBinding プロパティを true に設定して生成した Vehicle クラスの例を以下に示します。

インポート オプション : コレクション型の選択

XML でアイテムのコレクションを表すパターンには、アイテムのリストと、アイテム間の関連付けの 2 種類があります。文字列のリストの例を次に示します。

文字列と整数 (city namepopulation) を関連付ける例を次に示します。

Aa702680.note(ja-jp,VS.90).gifメモ :
関連付けもリストと見なすことができます。たとえば、上記の関連付けは、文字列と整数の 2 つのフィールドから成る複雑な city オブジェクトのリストと考えることも可能です。どちらの方法であっても、XSD スキーマで表現できます。この 2 つを区別する手段はないので、WCF に特有の特別な注釈がスキーマ内になければ、このようなパターンは常にリストとして扱われます。注釈がある場合は、関連付けで表現されているものとして扱われます。詳細な情報については、次のページを参照してください。 「データ コントラクト スキーマの参照」を参照してください。

リストは通常、ジェネリック リストから派生したコレクション データ コントラクト、または .NET Framework の配列としてインポートされます。スキーマがコレクションの標準的な名前付けパターンに従っているかどうかによって切り分けます。詳細については、「データ コントラクトのコレクション型」を参照してください。関連付けは通常、Dictionary、または辞書オブジェクトから派生したコレクション データ コントラクトとしてインポートされます。たとえば、次のスキーマを考えます。

これをインポートすると次のようになります (読みやすいようプロパティの代わりにフィールドを記載)。

このようなスキーマ パターンに対し、生成されるコレクション型をカスタマイズすることも可能です。たとえば、List クラスではなく BindingList クラスからコレクションを生成することにより、型をリスト ボックスにバインドし、コレクションの内容が変わると自動的にリスト ボックスにも反映されるようにすることを考えてみましょう。これは、ImportOptions クラスの ReferencedCollectionTypes プロパティとして、実際に使うコレクション型 (以下、"参照される" 型) のリストを設定すると実現できます。コレクションのインポート時には、参照されるコレクション型のリストが順に調べられ、最も適したコレクションがあれば、それが使用されます。関連付けは IDictionary インターフェイス (ジェネリックか否かにかかわらず) を実装した型にしか照合されませんが、リストはサポートされているすべてのコレクション型に照合されます。

たとえば ReferencedCollectionTypes プロパティに BindingList を設定すると、上記の例にある people 型は次のように生成されます。

クローズ ジェネリック型 (型パラメータすべてに具体的な型が指定されているジェネリック型) は、合致度が最も高いと見なされます。したがって、たとえば BindingList(Of Integer)ArrayList の 2 つの型を "参照される" 型のコレクションに渡した場合、スキーマ内に整数のリストがあると、すべて 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 型が再利用されます。名前だけが一致して構造が一致しない場合は、例外が発生します。型の参照時に、バージョンによる (オプションのデータ メンバの追加などの) 差異は許容されません。構造は厳密に合致しなければなりません。

"参照される" 型のコレクションに、データ コントラクト名と名前空間が同一である型を重複して追加しても、その名前と名前空間でスキーマ型をインポートしない限り問題ありません。したがって、実際にはスキーマに現れることのない型については重複を気にすることなく、アセンブリ内の型をすべてコレクションに追加してしまうことができます。

ReferencedTypes プロパティは、特定のモードで実行する Svcutil.exe ツールの /reference スイッチに相当します。

Aa702680.note(ja-jp,VS.90).gifメモ :
Svcutil.exe ツールや (Visual Studio の) サービス参照の追加 ツールの使用時は、MsCorLib.dll のすべての型が自動的に参照されます。

インポート オプション : 非データ コントラクト スキーマを IXmlSerializable 型としてインポート

XsdDataContractImporter は、どのようなスキーマでもインポートできるわけではありません。未対応のスキーマ構造 (たとえば XML 属性) をインポートしようとすると例外が発生します。ただし ImportXmlType プロパティを true に設定すると、対応可能なスキーマの範囲が広がります。true に設定すると、XsdDataContractImporter は、IXmlSerializable インターフェイスを実装した型を生成します。そのため、これらの型の 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 データに直接アクセスできるようになります。この型のインスタンスをシリアル化する際、"nodesField" フィールドに XML データを設定しておくと、これもシリアル化の対象になります。

以上の処理は IXmlSerializable インターフェイスで実装されます。生成された IXmlSerializable 型では、ReadXml の実装により、XmlSerializableServices クラスの ReadNodes メソッドが呼び出されます。これは XmlReader を介して渡された XML を XmlNode オブジェクトに変換するヘルパー メソッドです。WriteXml を実装したコードは逆に、XmlNode オブジェクトの配列を、一連の XmlWriter の呼び出しに変換します。それには WriteNodes メソッドを使います。

スキーマのエクスポート処理は、生成された IXmlSerializable クラスに対して実行できます。ただし、先に述べたように、元どおりのスキーマは再現されません。任意の XSD 型を表す、"anyType" という標準型になります。

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

Aa702680.note(ja-jp,VS.90).gifメモ :
XmlSerializableServices 型は、この機能を提供するためだけに用意されています。他の目的で使用することはお勧めできません。

インポート オプション : 高度なオプション

他にも、次のようなオプションがあります。

  • CodeProvider プロパティ。生成されたクラスに組み込むコードを生成するために使用する、CodeDomProvider を指定します。インポートの際は、CodeDomProvider でサポートされていない機能が回避されます。たとえば J# にはジェネリック型の機能がありません。したがって、このプロパティに J# 用のコード プロバイダを指定した場合、インポート側の CodeCompileUnit では、ジェネリック型は生成されません。CodeProvider を設定しない場合は、.NET Framework のすべての機能が制限なく使用されます。
  • DataContractSurrogate プロパティ。IDataContractSurrogate の実装を指定するために使います。IDataContractSurrogate は、インポート処理のカスタマイズを行います。詳細な情報については、次のページを参照してください。 「データ コントラクト サロゲート」を参照してください。既定では、サロゲートは使用されません。

関連項目

リファレンス

DataContractSerializer
XsdDataContractImporter
XsdDataContractExporter
ImportOptions

概念

データ コントラクト スキーマの参照
データ コントラクト サロゲート
スキーマのインポートとエクスポート
クラスからのスキーマのエクスポート
データ コントラクト スキーマの参照