次の方法で共有


データ転送のアーキテクチャの概要

Windows Communication Foundation (WCF) は、一種のメッセージング インフラストラクチャと考えることができます。WCF は、メッセージを受信し、それらのメッセージを処理し、さらにアクションを実行するためにユーザー コードにディスパッチすることができます。また、ユーザー コードで指定されたデータからメッセージを作成し、送信先に配布することもできます。ここでは、メッセージを処理するためのアーキテクチャと格納されるデータについて説明します。このトピックは、上級開発者を対象としています。データを送受信する方法のより簡単なタスク指向の概要については、「サービス コントラクトでのデータ転送の指定」を参照してください。

Aa347789.note(ja-jp,VS.90).gifメモ :
ここでは、WCF オブジェクト モデルを調べても明らかにはならない WCF 実装の詳細について説明します。文書化された実装の詳細について、2 つの注意事項があります。1 つは、説明が簡略化されているという点です。実際の実装は、最適化やその他の理由から、より複雑であることが考えられます。もう 1 つの注意事項として、特定の実装の詳細が文書化されていても、その詳細に依存しないようにしてください。これらの詳細は、バージョン間で予告なしに変更されることがあるからです。これは、サービス リリースにおいても同様です。

基本アーキテクチャ

WCF のメッセージ処理機能の中核となるのは、Message クラスです。このクラスの詳細については、「メッセージ クラスの使用」を参照してください。WCF のランタイム コンポーネントは、チャネル スタックとサービス フレームワークという 2 つの主要部分に分けることができ、Message クラスがコネクション ポイントになります。

チャネル スタックは、有効な Message インスタンスと、メッセージ データの送信または受信に対応するアクションとの間の変換を行います。送信側のチャネル スタックは、有効な Message インスタンスを取得し、何らかの処理を行った後、メッセージの送信に論理的に対応するアクションを実行します。このアクションには、TCP パケットまたは HTTP パケットの送信、メッセージ キューへのメッセージの配置、データベースへのメッセージの書き込み、ファイル共有へのメッセージの保存、実装によって異なるその他のアクションなどがあります。最も一般的なアクションは、ネットワーク プロトコル上でのメッセージの送信です。受信側では、この逆のことが行われます。つまり、アクション (TCP パケットまたは HTTP パケットの到着の場合もあれば、その他のアクションの場合もあります) が検出され、チャネル スタックが処理を行った後に、このアクションを有効な Message インスタンスに変換します。

Message クラスとチャネル スタックを直接使用して、WCF を使用できます。ただし、この作業は困難であり、時間もかかります。また、Message オブジェクトはメタデータをサポートしていないため、WCF をこのように使用した場合、厳密に型指定された WCF クライアントを生成することはできません。

そのため、WCF には、Message オブジェクトの構築と受信に使用できる使いやすいプログラミング モデルを提供するサービス フレームワークが用意されています。サービス フレームワークは、サービス コントラクトの概念によってサービスを .NET Framework 型にマップし、OperationContractAttribute 属性でマークされた .NET Framework メソッドであるユーザー操作にメッセージをディスパッチします (詳細については、「サービス コントラクトの設計」を参照してください)。これらのメソッドは、パラメータと戻り値を持つことができます。サービス側では、サービス フレームワークが受信 Message インスタンスをパラメータに変換し、戻り値を送信 Message インスタンスに変換します。クライアント側では、この逆のことが行われます。たとえば、次のような FindAirfare の操作について考えてみます。

クライアントで FindAirfare が呼び出されたとします。クライアントのサービス フレームワークは、FromCity パラメータと ToCity パラメータを送信 Message インスタンスに変換し、これをチャネル スタックに渡して送信します。

サービス側では、チャネル スタックから Message インスタンスが到着すると、サービス フレームワークがメッセージから関連データを抽出して FromCity パラメータと ToCity パラメータを設定し、サービス側の FindAirfare メソッドを呼び出します。メソッドから制御が戻ると、サービス フレームワークは、返された整数値と IsDirectFlight 出力パラメータを取得し、この情報を格納する Message オブジェクトのインスタンスを作成します。次に、この Message インスタンスをチャネル スタックに渡して、クライアントに返送します。

クライアント側では、応答メッセージが格納された Message インスタンスが、チャネル スタックから取り出されます。サービス フレームワークは、戻り値と IsDirectFlight 値を抽出し、これらをクライアントの呼び出し元に返します。

Message クラス

Message クラスはメッセージの抽象表現を意図したものですが、そのデザインは SOAP メッセージと密接に関連しています。Message には、情報の 3 つの主要部分であるメッセージ本文、メッセージ ヘッダー、およびメッセージ プロパティが含まれます。

メッセージ本文

メッセージ本文は、メッセージの実際のデータ ペイロードを表すためのものです。メッセージ本文は、常に XML Infoset として表されます。これは、WCF で作成または受信されるすべてのメッセージが XML 形式でなければならないということではありません。メッセージ本文を解釈する方法を決定するのはチャネル スタックです。チャネル スタックは、メッセージ本文を XML として出力する場合もあれば、他の形式に変換する場合もあります。また、メッセージ本文を完全に除外する場合もあります。WCF で提供されるほとんどのバインディングでは、メッセージ本文は SOAP エンベロープの body セクションに XML コンテンツとして表されます。

Message クラスは、本文を表す XML データを保持するバッファを必ずしも含むわけではないことを認識しておくことが重要です。Message には XML Infoset が論理的に含まれますが、この Infoset は動的に構築可能であると同時に、メモリ内に物理的に存在することはありません。

メッセージ本文へのデータの配置

メッセージ本文にデータを配置するための統一された機構はありません。Message クラスには、抽象メソッド OnWriteBodyContents があります。このメソッドは、XmlDictionaryWriter を取得します。Message クラスの各サブクラスは、このメソッドをオーバーライドし、独自のコンテンツを書き込む必要があります。メッセージ本文には、OnWriteBodyContent によって生成された XML Infoset が論理的に含まれます。たとえば、次のような Message サブクラスについて考えてみます。

物理的には、AirfareRequestMessage インスタンスには 2 つの文字列 ("fromCity" と "toCity") しか含まれていません。ただし、このメッセージには、次のような XML Infoset が論理的に含まれています。

<airfareRequest>
    <from>Tokyo</from>
    <to>London</to>
</airfareRequest>

もちろん、このようにメッセージを作成することは、通常はありません。上記のようなメッセージは、サービス フレームワークを使用して、操作コントラクト パラメータから作成できるためです。さらに、Message クラスには、静的 CreateMessage メソッドもあります。このメソッドを使用すると、一般的な種類の内容を含むメッセージ (空のメッセージ、DataContractSerializer によって XML にシリアル化されたオブジェクトを含むメッセージ、SOAP エラーを含むメッセージ、XmlReader によって表された XML を含むメッセージなど) を作成できます。

メッセージ本文からのデータの取得

メッセージ本文に格納されたデータは、主に次の 2 とおりの方法で抽出できます。

  • WriteBodyContents メソッドを呼び出し、XML ライタに渡すことにより、メッセージ本文全体を一度に取得できます。メッセージ本文全体がこのライタに書き込まれます。メッセージ本文全体を一度に取得することを、メッセージの書き込みとも呼びます。書き込みは、メッセージの送信時に主にチャネル スタックによって実行されます。チャネル スタックには、通常、メッセージ本文全体にアクセスし、本文全体をエンコードして送信する部分があります。
  • メッセージ本文から情報を取得するもう 1 つの方法は、GetReaderAtBodyContents を呼び出し、XML リーダーを取得する方法です。リーダーでメソッドを呼び出すことにより、必要に応じてメッセージ本文に順次アクセスできます。メッセージ本文を少しずつ取得することを、メッセージの読み取りとも呼びます。メッセージの読み取りは、メッセージの受信時にサービス フレームワークによって主に使用されます。たとえば、DataContractSerializer の使用時に、サービス フレームワークは本文で XML リーダーを取得し、逆シリアル化エンジンに渡します。逆シリアル化エンジンは、要素単位でメッセージを読み取り、対応するオブジェクト グラフの構築を開始します。

メッセージ本文を取得できるのは一度だけです。これにより、転送専用ストリームを使用することが可能になります。たとえば、FileStream から読み取り、XML Infoset として結果を返す OnWriteBodyContents のオーバーライドを作成できます。ファイルの先頭まで "巻き戻す" 必要はありません。

WriteBodyContents メソッドと GetReaderAtBodyContents メソッドは、メッセージ本文がこれまで一度も取得されていないことを簡単にチェックした後、それぞれ OnWriteBodyContentsOnGetReaderAtBodyContents を呼び出します。

WCF でのメッセージの使用

ほとんどのメッセージは、送信 (チャネル スタックによって送信するために、サービス フレームワークが作成するメッセージ) または受信 (チャネル スタックから到着し、サービス フレームワークが解釈するメッセージ) のいずれかに分類できます。さらに、チャネル スタックは、バッファ モードまたはストリーミング モードで動作できます。また、サービス フレームワークが、ストリーム プログラミング モデルを公開する場合もあれば、非ストリーム プログラミング モデルを公開する場合もあります。この結果として生じるケースと、その実装の簡略化した詳細を次の表に示します。

メッセージの種類 メッセージの本文データ 書き込み (OnWriteBodyContents) 実装 読み取り (OnGetReaderAtBodyContents) 実装

送信 (非ストリーム プログラミング モデルから作成)

メッセージの書き込みに必要なデータ (例 : オブジェクトとそのシリアル化に必要な DataContractSerializer インスタンス)*

格納されたデータに基づいてメッセージを書き込むためのカスタム ロジック (例 : DataContractSerializer を使用している場合に、このシリアライザで WriteObject を呼び出す)*

OnWriteBodyContents を呼び出し、結果をバッファに保持し、バッファで XML リーダーを返します。

送信 (ストリーム プログラミング モデルから作成)

書き込むデータを含む Stream*

IStreamProvider 機構を使用して、格納されたストリームからデータを書き込みます*。

OnWriteBodyContents を呼び出し、結果をバッファに保持し、バッファで XML リーダーを返します。

ストリーミング チャネル スタックからの受信

ネットワーク上で到着したデータを表す Stream オブジェクトと、このオブジェクトに配置された XmlReader

WriteNode を使用して、格納された XmlReader からコンテンツを書き込みます。

格納された XmlReader を返します。

非ストリーミング チャネル スタックからの受信

本文データを格納するバッファと、このバッファに配置された XmlReader

WriteNode を使用して、格納された XmlReader からコンテンツを書き込みます。

格納された lang を返します。

* これらの項目は、Message サブクラスに直接実装されるのではなく、BodyWriter クラスのサブクラスに実装されます。BodyWriter の詳細については、「メッセージ クラスの使用」を参照してください。

メッセージ ヘッダー

メッセージには、ヘッダーを含めることができます。ヘッダーは、名前、名前空間、および他の複数のプロパティに関連付けられた XML Infoset で論理的に構成されます。メッセージ ヘッダーには、MessageHeaders プロパティを使用してアクセスします。各ヘッダーは、MessageHeader クラスによって表されます。通常、SOAP メッセージを使用するように構成されたチャネル スタックを使用している場合、メッセージ ヘッダーは SOAP メッセージ ヘッダーにマップされます。

メッセージ ヘッダーへの情報の配置と、メッセージ ヘッダーからの情報の抽出は、メッセージ本文を使用する場合と似ています。ストリーミングがサポートされていないため、プロセスは若干簡略化されます。ヘッダーは常に強制的にバッファに保持されるため、同じヘッダーの内容に何度もアクセスすることが可能であり、各ヘッダーに任意の順序でアクセスできます。ヘッダーで XML リーダーを取得するために使用できる汎用の機構はありませんが、このような機能を備えた読み取り可能なヘッダーを表す MessageHeader サブクラスが WCF の内部に存在します。この種の MessageHeader は、カスタム アプリケーション ヘッダーを持つメッセージが到着したときにチャネル スタックによって作成されます。これにより、サービス フレームワークは、逆シリアル化エンジン (DataContractSerializer など) を使用してこれらのヘッダーを解釈できます。

詳細な情報については、次のページを参照してください。 「メッセージ クラスの使用」を参照してください。

メッセージ プロパティ

メッセージには、プロパティを含めることができます。"プロパティ" とは、文字列名に関連付けられた任意の .NET Framework オブジェクトです**。プロパティには、MessageProperties プロパティからアクセスします。

通常、メッセージ本文とメッセージ ヘッダーは、それぞれ SOAP 本文および SOAP ヘッダーにマップされますが、メッセージ プロパティがメッセージと共に送信または受信されることは通常ありません。メッセージ プロパティは、チャネル スタック内のさまざまなチャネル間、およびチャネル スタックとサービス モデルの間で、メッセージに関するデータを渡す通信機構として主に存在します。

たとえば、WCF の一部として含まれる HTTP トランスポート チャネルは、クライアントに応答を送信するときに、"404 (ページが見つかりません)" や "500 (内部サーバー エラー)" などのさまざまな HTTP ステータス コードを生成できます。HTTP トランスポート チャネルは、応答メッセージを送信する前に、MessagePropertiesHttpResponseMessageProperty 型のオブジェクトを格納する "httpResponse" というプロパティが含まれているかどうかをチェックします。このようなプロパティが見つかった場合、StatusCode プロパティを調べ、そのステータス コードを使用します。該当のプロパティが見つからなかった場合は、既定の "200 (OK)" コードが使用されます。

詳細な情報については、次のページを参照してください。 「メッセージ クラスの使用」を参照してください。

メッセージ全体

これまで、メッセージのさまざまな部分に個別にアクセスするためのメソッドについて説明してきましたが、Message クラスには、メッセージ全体を使用するためのメソッドも用意されています。たとえば、WriteMessage メソッドは、メッセージ全体を XML ライタに書き込みます。

これを可能にするには、Message インスタンス全体と XML Infoset 間のマッピングが定義されている必要があります。実際に、このようなマッピングは存在します。WCF では、SOAP 標準を使用して、このマッピングを定義します。Message インスタンスが XML Infoset として書き込まれると、書き込まれた Infoset はメッセージを含む有効な SOAP エンベロープになります。したがって、通常、WriteMessage は次の手順を実行します。

  1. SOAP エンベロープ要素の開始タグを書き込みます。
  2. SOAP ヘッダー要素の開始タグを書き込み、すべてのヘッダーを書き込んで、ヘッダー要素を閉じます。
  3. SOAP 本文要素の開始タグを書き込みます。
  4. WriteBodyContents または同等のメソッドを呼び出して、本文を書き込みます。
  5. 本文要素とエンベロープ要素を閉じます。

上記の手順は、SOAP 標準に密接に関連しています。これは、SOAP の複数のバージョンが存在することで複雑になります。たとえば、使用している SOAP のバージョンがわからなければ、SOAP エンベロープ要素を正しく書き込むことはできません。また、SOAP 固有のこの複雑なマッピングを完全に無効にすることが望ましい場合もあります。

このため、Message には Version プロパティが用意されています。このプロパティをメッセージの書き込み時に使用する SOAP バージョンに設定できます。また、None に設定することで、SOAP 固有のマッピングを使用しないようにすることもできます。Version プロパティを None に設定すると、メッセージ全体を使用するメソッドは、メッセージが本文だけで構成されている場合と同様に機能します。たとえば、WriteMessage は前述の複数の手順を実行するのではなく、WriteBodyContents を呼び出すだけです。受信メッセージでは、Version が自動検出され、適切に設定されることが求められます。

チャネル スタック

チャネル

既に説明したように、チャネル スタックは、送信 Message インスタンスをアクション (ネットワーク上でのパケットの送信など) に変換したり、アクション (ネットワーク パケットの受信など) を受信 Message インスタンスに変換したりする役割を担います。

チャネル スタックは、一連の順序付けられた 1 つ以上のチャネルで構成されます。送信 Message インスタンスは、スタック内の最初のチャネル ("最上位チャネル" とも呼ばれます) に渡され、このチャネルからスタック内の 1 つ下のチャネルに渡されます。以降、同様にスタック内の 1 つ下のチャネルに順次渡されていきます**。メッセージは、"トランスポート チャネル" と呼ばれる最後のチャネルで終了します**。受信メッセージはトランスポート チャネルから始まり、スタック内の下位のチャネルから上位のチャネルに順次渡されていきます。通常、メッセージは最上位チャネルからサービス フレームワークに渡されます。これは、アプリケーション メッセージの通常のパターンですが、若干動作が異なるチャネルもあります。たとえば、上のチャネルからメッセージが渡されることなく、独自のインフラストラクチャ メッセージを送信する場合があります。

メッセージがスタックを通過するときに、チャネルではさまざまな方法でメッセージを処理できます。最も一般的な処理は、送信メッセージにヘッダーを追加し、受信メッセージのヘッダーを読み取ることです。たとえば、チャネルでメッセージのデジタル署名を計算し、ヘッダーとして追加できます。また、受信メッセージのこのデジタル署名ヘッダーを検査し、有効な署名のないメッセージがチャネル スタック内の上位のチャネルに渡されないようにブロックすることもできます。多くの場合、チャネルはメッセージ プロパティの設定や検査も行います。通常、メッセージ本文は変更されませんが、変更は可能です。たとえば、WCF セキュリティ チャネルはメッセージ本文を暗号化できます。

トランスポート チャネルとメッセージ エンコーダ

他のチャネルによって変更された送信 Message を実際に何らかのアクションに変換するのは、スタック内の最下位チャネルです。受信側では、このチャネルがアクションを Message に変換して、他のチャネルが処理できるようにします。

既に説明したように、アクションはさまざまです。たとえば、各種プロトコル上でのネットワーク パケットの送信/受信、データベースでのメッセージの読み取り/書き込み、メッセージ キューへのメッセージの配置/キューからのメッセージの削除などがあります。これらのアクションには、共通点が 1 つあります。それは、すべてのアクションには、WCF の Message インスタンスと、送信、受信、読み取り、書き込み、キューへの配置、またはキューからの削除が可能な実際のバイト グループとの間の変換が必要であるということです。Message をバイト グループに変換するプロセスはエンコードと呼ばれ、バイト グループから Message を作成する逆のプロセスはデコードと呼ばれます。

ほとんどのトランスポート チャネルでは、メッセージ エンコーダと呼ばれるコンポーネントを使用して、エンコードとデコードの処理を行います。メッセージ エンコーダは、MessageEncoder クラスのサブクラスです。MessageEncoder には、Message とバイト グループとの間の変換を行う ReadMessage メソッドと WriteMessage メソッドのさまざまなオーバーロードが含まれます。

送信側では、バッファ トランスポート チャネルが上のチャネルから受け取った Message オブジェクトを WriteMessage に渡します。バッファ トランスポート チャネルはバイト配列を取得し、アクション (これらのバイトを有効な TCP パケットとしてパッケージングし、適切な送信先に送信するなど) を実行するために使用します。ストリーミング トランスポート チャネルは、(たとえば、送信 TCP 接続で) まず Stream を作成します。次に、この Stream と送信に必要な Message の両方を適切な WriteMessage オーバーロードに渡し、このオーバーロードによってメッセージが書き込まれます。

受信側では、バッファ トランスポート チャネルは、(たとえば、受信 TCP パケットから) 受信バイトを配列に抽出し、ReadMessage を呼び出して、チャネル スタックの上位に渡すことができる Message オブジェクトを取得します。ストリーミング トランスポート チャネルは、Stream オブジェクト (受信 TCP 接続のネットワーク ストリームなど) を作成し、ReadMessage に渡して Message オブジェクトを取得します。

トランスポート チャネルとメッセージ エンコーダの分離は必須ではありません。つまり、メッセージ エンコーダを使用しないトランスポート チャネルの作成が可能です。ただし、この分離には、構成しやすいという利点があります。トランスポート チャネルが基本 MessageEncoder だけを使用する限り、WCF のメッセージ エンコーダを使用することも、サードパーティのメッセージ エンコーダを使用することもできます。同様に、通常はどのトランスポート チャネルでも同じエンコーダを使用できます。

メッセージ エンコーダの動作

エンコーダの一般的な動作を記述する場合、次の 4 つのケースについて検討すると有益です。

動作 説明

エンコード (バッファ)

バッファ モードでは、通常、エンコーダは可変サイズのバッファを作成し、このバッファに XML ライタを作成します。エンコーダは、エンコードするメッセージに対して WriteMessage を呼び出してヘッダーを書き込みます。次に、このトピックの Message に関するセクションで説明したように、WriteBodyContents を使用して本文を書き込みます。その後、トランスポート チャネルで使用できるように、(バイト配列として表される) バッファの内容が返されます。

エンコード (ストリーミング)

ストリーミング モードでは、動作が上記に似ていますが、より単純です。バッファは必要ありません。通常、XML ライタがストリームに作成され、このライタに書き込むために Message に対して WriteMessage が呼び出されます。

デコード (バッファ)

バッファ モードでデコードする場合、通常、バッファされたデータを格納する特殊な Message サブクラスが作成されます。メッセージのヘッダーが読み取られ、メッセージ本文に配置する XML リーダーが作成されます。これは、GetReaderAtBodyContents で返されるリーダーです。

デコード (ストリーミング)

ストリーミング モードでデコードする場合、通常、特殊な Message サブクラスが作成されます。ストリームは、すべてのヘッダーを読み取り、メッセージ本文に配置できる位置まで進められます。次に、ストリームに XML リーダーが作成されます。これは、GetReaderAtBodyContents で返されるリーダーです。

エンコーダでは、他の機能も実行できます。たとえば、エンコーダは XML リーダーとライタをプールできます。新しい XML リーダーまたはライタが必要になるたびに作成すると負荷がかかります。したがって、通常、エンコーダは構成可能なサイズのリーダーのプールとライタのプールを保持しています。前述のエンコーダの動作の説明の中の、"XML リーダー/ライタを作成する" という表現は、"プールから取得し、プールされているものを使用できない場合に作成する" という意味です。エンコーダ (およびデコード時にエンコーダが作成する Message サブクラス) には、リーダーとライタが必要でなくなったら (Message を閉じたときなどに) プールに戻すためのロジックが含まれています。

WCF には、3 つのメッセージ エンコーダが用意されていますが、さらにカスタム エンコーダを作成することもできます。用意されているエンコーダは、Text、Binary、および MTOM (Message Transmission Optimization Mechanism) の 3 種類です。これらの詳細については、「メッセージ エンコーダを選択する」を参照してください。

IStreamProvider インターフェイス

ストリーミングされた本文を含む送信メッセージを XML ライタに書き込むときに、MessageOnWriteBodyContents 実装で次のような一連の呼び出しを使用します。

  • ストリームの前に必要な情報 (XML 開始タグなど) を書き込みます。
  • ストリームを書き込みます。
  • ストリームの後に情報 (XML 終了タグなど) を書き込みます。

これは、テキスト XML エンコーディングに類似するエンコードで有効に機能します。ただし、XML Infoset 情報 (XML 要素を開始および終了するためのタグなど) を、要素内に含まれるデータと共には配置しないエンコードもあります。たとえば、MTOM エンコーディングでは、メッセージは複数の部分に分割されます。ある部分に XML Infoset が含まれ、要素の実際のコンテンツについては他の部分への参照が含まれている場合があります。通常、XML Infoset は、ストリーミングされたコンテンツと比べてサイズが小さいため、Infoset をバッファに格納し、これを書き込んだ後に、ストリーミング方式でコンテンツを書き込むことには意味があります。これは、終了要素タグが書き込まれるまでは、ストリームを書き込むことができないことを意味します。

このために、IStreamProvider インターフェイスが使用されます。このインターフェイスには、書き込むストリームを返す GetStream メソッドがあります。OnWriteBodyContents でストリーミングされたメッセージ本文を書き込む適切な方法は次のとおりです。

  1. ストリームの前に必要な情報を書き込みます (XML 開始タグなど)。
  2. 書き込むストリームを返す IStreamProvider 実装で、IStreamProvider を受け取る XmlDictionaryWriter に対して WriteValue オーバーロードを呼び出します。
  3. ストリームの後に情報を書き込みます (XML 終了タグなど)。

この方法を使用すると、XML ライタは GetStream を呼び出し、ストリーミングされたデータを書き込む時期を選択できます。たとえば、テキスト XML ライタやバイナリ XML ライタは、このメソッドをすぐに呼び出し、開始タグと終了タグの間にストリーミングされたコンテンツを書き込むことができます。MTOM ライタは、メッセージの適切な部分を書き込む準備ができたときに、後で GetStream を呼び出すことができます。

サービス フレームワークでのデータの表現

このトピックの「基本アーキテクチャ」で説明したように、サービス フレームワークは WCF の一部であり、特に、メッセージ データのユーザー フレンドリなプログラミング モデルと実際の Message インスタンス間で変換を行う機能を果たします。通常、サービス フレームワークでは、メッセージ交換は OperationContractAttribute 属性でマークされた .NET Framework メソッドとして表されます。このメソッドは複数のパラメータを取得でき、戻り値または出力パラメータ (または両方) を返すことができます。サービス側では、入力パラメータは受信メッセージを表し、戻り値と出力パラメータは送信メッセージを表します。クライアント側では、この逆になります。パラメータと戻り値を使用してメッセージを記述するためのプログラミング モデルの詳細については、「サービス コントラクトでのデータ転送の指定」を参照してください。ここでは、概要を簡単に説明します。

プログラミング モデル

WCF サービス フレームワークでは、メッセージを記述するための次の 5 種類のプログラミング モデルをサポートしています。

1. 空のメッセージ

これは、最も簡単なケースです。空の受信メッセージを記述する場合は、次のように入力パラメータは使用しないでください。

空の送信メッセージを記述する場合は、次のように void 戻り値を使用し、出力パラメータは使用しないでください。

次のように、一方向の操作コントラクトとは異なることに注意してください。

SetDesiredTemperature の例では、双方向メッセージ交換パターンが記述されています。メッセージは操作から返されますが、このメッセージは空です。操作からエラーを返すことができます。"SetLightbulb" の例では、メッセージ交換パターンは一方向であるため、記述する送信メッセージはありません。この場合、サービスはクライアントにステータスを通知できません。

2. Message クラスの直接使用

操作コントラクトで Message クラス (またはサブクラスのいずれか) を直接使用できます。この場合、サービス フレームワークは、操作からチャネル スタックおよびチャネル スタックから操作に Message を渡すだけであり、それ以上の処理は行いません。

Message を直接使用するケースとして、主に 2 つのケースがあります。1 つは、他のプログラミング モデルでは、メッセージを記述できるだけの柔軟性を得ることができない高度なシナリオで使用します。たとえば、ディスク上のファイルを使用して、ファイルのプロパティがメッセージ ヘッダーになり、ファイルの内容がメッセージ本文になるようにメッセージを記述することが必要になる場合があります。これは、次のように作成できます。

操作コントラクトで Message を一般的に使用するもう 1 つのケースは、サービスがメッセージの特定の内容に留意しているわけではなく、ブラック ボックスと同様にメッセージを処理する場合です。たとえば、メッセージを他の複数の受信者に転送するサービスを使用することがあります。コントラクトは、次のように作成できます。

Action="*" 行により、メッセージのディスパッチが実質的に無効になり、IForwardingService コントラクトに送信されるすべてのメッセージが確実に ForwardMessage 操作に送られます (通常、ディスパッチャは、メッセージの "Action" ヘッダーを調べて、対象とする操作を特定します。Action="*" は、"Action ヘッダーに設定可能なすべての値" を意味します。)Action="*" とパラメータとしての Message の使用を組み合わせると、存在し得るすべてのメッセージを取得できるため、この組み合わせは "ユニバーサル コントラクト" と呼ばれます。存在し得るすべてのメッセージを送信できるようにするには、Message を戻り値として使用し、ReplyAction を "*" に設定します。これにより、サービス フレームワークは独自の Action ヘッダーを追加できなくなるため、開発者が返す Message オブジェクトを使用して、このヘッダーを制御できます。

3. メッセージ コントラクト

WCF には、"メッセージ コントラクト" と呼ばれるメッセージを記述するための宣言型プログラミング モデルが用意されています。このモデルの詳細については、「メッセージ コントラクトの使用」を参照してください。基本的に、単一の .NET Framework 型によってメッセージ全体が表されます。この型は、MessageBodyMemberAttributeMessageHeaderAttribute などの属性を使用して、メッセージ コントラクト クラスのどの部分をメッセージのどの部分にマップする必要があるかを示します。

メッセージ コントラクトは、結果として生成される Message インスタンスに対してさまざまな制御を行うことができます (ただし、Message クラスを直接使用した場合と同様に制御できるわけではありません)。たとえば、多くの場合、メッセージ本文は情報の複数の部分で構成され、各部分は独自の XML 要素によって表されます。これらの要素は、本文に直接出現することも (ベア モード)、XML 要素で囲んで ラップすることもできます。メッセージ コントラクト プログラミング モデルを使用すると、ベアとラップのどちらを使用するかを決定し、ラッパー名と名前空間の名前を制御できます。

前述の機能を示すメッセージ コントラクトのコード例を次に示します。

MessageBodyMemberAttributeMessageHeaderAttribute、または他の関連する属性を使用して、シリアル化対象としてマークされた項目は、メッセージ コントラクトに関与するためにシリアル化可能であることが必要です。詳細な情報については、次のページを参照してください。 このトピックの「シリアル化」で後述します。

4. パラメータ

多くの場合、データの複数の部分に作用する操作を記述する開発者は、メッセージ コントラクトによって実現される制御のレベルを必要としていません。たとえば、新しいサービスの作成時に、ベアとラップのどちらを使用するかを決定し、ラッパー要素名を決めることは通常望まれていません。多くの場合、これらを決定するには Web サービスと SOAP の深い知識が必要となります。

WCF サービス フレームワークは、こうした選択をユーザーに強制することなく、情報の複数の関連部分を送信または受信するのに最適で、相互運用性が最も高い SOAP 表現を自動的に選択できます。これは、情報のこのような部分を操作コントラクトのパラメータまたは戻り値として記述するだけで実現されます。たとえば、次のような操作コントラクトについて考えてみます。

サービス フレームワークは、情報の 3 つの部分 (customerIDitem、および quantity) をすべてメッセージ本文に配置し、SubmitOrderRequest というラッパー要素にラップすることを自動的に決定します。

より複雑なメッセージ コントラクトや Message ベースのプログラミング モデルに移行する特別な理由がない限り、操作コントラクト パラメータの簡単なリストとして送信または受信するように情報を記述することをお勧めします。

5. Stream

Stream またはそのサブクラスのいずれかを、操作コントラクトで使用したり、メッセージ コントラクトでメッセージ本文の単独の部分として使用したりすることは、これまでに説明したものとは別のプログラミング モデルと考えることができます。ストリーミングに対応する独自の Message サブクラスを作成する場合を除き、Stream をこのように使用することは、コントラクトをストリーミング方式で使用できることを保証する唯一の方法です。詳細な情報については、次のページを参照してください。 「大規模データとストリーミング」を参照してください。

Stream またはそのサブクラスのいずれかをこのように使用した場合、シリアライザは呼び出されません。送信メッセージの場合、IStreamProvider インターフェイスのセクションで説明したように、特殊なストリーミング Message サブクラスが作成され、ストリームが書き込まれます。受信メッセージの場合は、サービス フレームワークが受信メッセージに Stream サブクラスを作成し、操作に提供します。

プログラミング モデルの制限

前述のプログラミング モデルを任意に組み合わせることはできません。たとえば、ある操作でメッセージ コントラクトを受け入れる場合、そのメッセージ コントラクトは入力パラメータのみであることが必要です。さらに、操作では、空のメッセージ (戻り値の型が void) または別のメッセージ コントラクトを返す必要があります。プログラミング モデルのこれらの制限については、各プログラミング モデルに関するトピック (「メッセージ コントラクトの使用」、「メッセージ クラスの使用」、および「大規模データとストリーミング」) に記載されています。

メッセージ フォーマッタ

前述の各プログラミング モデルは、"メッセージ フォーマッタ" と呼ばれるコンポーネントをサービス フレームワークにプラグインすることによってサポートされます**。メッセージ フォーマッタは、IClientMessageFormatter インターフェイス (クライアントで使用)、または IDispatchMessageFormatter インターフェイス (WCF サービス クライアントで使用)、あるいはこの両方を実装する型です。

通常、メッセージ フォーマッタは動作によってプラグインされます。たとえば、DataContractSerializerOperationBehavior は、データ コントラクト メッセージ フォーマッタをプラグインします。これを行うには、サービス側では ApplyDispatchBehavior メソッドで Formatter を適切なフォーマッタに設定します。クライアント側では、ApplyClientBehavior メソッドで Formatter を適切なフォーマッタに設定します。

メッセージ フォーマッタが実装できるメソッドを次の表に示します。

インターフェイス メソッド アクション

IDispatchMessageFormatter

DeserializeRequest

受信 Message を操作パラメータに変換します。

IDispatchMessageFormatter

SerializeReply

操作の戻り値または出力パラメータから送信 Message を作成します。

IClientMessageFormatter

SerializeRequest

操作パラメータから送信 Message を作成します。

IClientMessageFormatter

DeserializeReply

受信 Message を戻り値または出力パラメータに変換します。

シリアル化

メッセージ コントラクトまたはパラメータを使用してメッセージの内容を記述する場合は、常にシリアル化を使用して .NET Framework 型と XML Infoset 表現の間の変換を行う必要があります。WCF では、他の場所でもシリアル化を使用します。たとえば、MessageGetBody ジェネリック メソッドを使用すると、オブジェクトに逆シリアル化されたメッセージの本文全体を読み取ることができます。

WCF は、パラメータおよびメッセージ各部のシリアル化と逆シリアル化を行うためにすぐに使用できるシリアル化技術として、DataContractSerializerXmlSerializer をサポートしています。また、カスタム シリアライザを作成することもできます。ただし、WCF のその他の部分 (GetBody ジェネリック メソッドや SOAP エラーのシリアル化など) では、XmlObjectSerializer のサブクラス (DataContractSerializer および NetDataContractSerializerXmlSerializer は対象外) だけを使用するように制限されている場合があります。また、DataContractSerializer だけを使用するようにハードコーディングされている場合もあります。

XmlSerializer は、ASP.NET Web サービスで使用されるシリアル化エンジンです。DataContractSerializer は、新しいデータ コントラクト プログラミング モデルを認識する新しいシリアル化エンジンです。DataContractSerializer が既定で選択されています。XmlSerializer を使用する場合は、DataContractFormatAttribute 属性を使用して操作ごとに選択できます。

DataContractSerializerOperationBehaviorXmlSerializerOperationBehavior は、それぞれ DataContractSerializer および XmlSerializer のメッセージ フォーマッタをプラグインする役割を担う操作の動作です。DataContractSerializerOperationBehavior の動作は、NetDataContractSerializer など、XmlObjectSerializer から派生した任意のシリアライザで実際に操作できます (詳細については、「スタンドアロンのシリアル化の使用」を参照してください)。この動作では、CreateSerializer 仮想メソッド オーバーロードのいずれかを呼び出して、シリアライザを取得します。別のシリアライザをプラグインするには、新しい DataContractSerializerOperationBehavior サブクラスを作成し、CreateSerializer の両方のオーバーロードをオーバーライドします。

関連項目

概念

サービス コントラクトでのデータ転送の指定