AdvancedDispatchByBody サンプルでは、受信メッセージを操作に割り当てるための代替アルゴリズムを実装する方法を示します。
既定では、サービス モデル ディスパッチャーは、メッセージの WS-Addressing "Action" ヘッダーまたは HTTP SOAP 要求内の同等の情報に基づいて、受信メッセージの適切な処理方法を選択します。
WS-I 基本プロファイル 1.1 のガイドラインに従っていない一部の SOAP 1.1 Web サービス スタックでは、アクション URI に基づいてメッセージをディスパッチするのではなく、SOAP 本文内の最初の要素の XML 修飾名に基づいてメッセージをディスパッチします。 同様に、これらのスタックのクライアント側では、SOAP 1.1 仕様で許可された空または任意の HTTP SoapAction ヘッダーを含むメッセージを送信できます。
メッセージをメソッドにディスパッチする方法を変更するために、サンプルでは、DispatchByBodyElementOperationSelector
にIDispatchOperationSelector機能拡張インターフェイスを実装します。 このクラスは、メッセージ本文の最初の要素に基づいて操作を選択します。
クラス コンストラクターは、 XmlQualifiedName
と文字列のペアが設定されたディクショナリを受け取ります。これにより、修飾名は SOAP 本文の最初の子の名前を示し、文字列は一致する操作名を示します。
defaultOperationName
は、このディクショナリと照合できないすべてのメッセージを受信する操作の名前です。
class DispatchByBodyElementOperationSelector : IDispatchOperationSelector
{
Dictionary<XmlQualifiedName, string> dispatchDictionary;
string defaultOperationName;
public DispatchByBodyElementOperationSelector(Dictionary<XmlQualifiedName,string> dispatchDictionary, string defaultOperationName)
{
this.dispatchDictionary = dispatchDictionary;
this.defaultOperationName = defaultOperationName;
}
}
IDispatchOperationSelector インターフェイスには 1 つのメソッド ( SelectOperation) しかないため、実装は非常に簡単に構築できます。 このメソッドのジョブは、受信メッセージを検査し、現在のエンドポイントのサービス コントラクト上のメソッドの名前と等しい文字列を返します。
このサンプルでは、操作セレクターは、GetReaderAtBodyContentsを使用して受信メッセージの本文のXmlDictionaryReaderを取得します。 このメソッドは既にメッセージの本文の最初の子にリーダーを配置しているため、現在の要素の名前と名前空間 URI を取得し、それらを組み合わせて XmlQualifiedName
にし、操作セレクターによって保持されているディクショナリから対応する操作を検索するために使用されます。
public string SelectOperation(ref System.ServiceModel.Channels.Message message)
{
XmlDictionaryReader bodyReader = message.GetReaderAtBodyContents();
XmlQualifiedName lookupQName = new
XmlQualifiedName(bodyReader.LocalName, bodyReader.NamespaceURI);
message = CreateMessageCopy(message,bodyReader);
if (dispatchDictionary.ContainsKey(lookupQName))
{
return dispatchDictionary[lookupQName];
}
else
{
return defaultOperationName;
}
}
GetReaderAtBodyContentsまたはメッセージの本文コンテンツへのアクセスを提供するその他のメソッドを使用してメッセージ本文にアクセスすると、メッセージは "読み取り" としてマークされます。これは、メッセージがそれ以降の処理に対して無効であることを意味します。 したがって、操作セレクターは、次のコードに示すメソッドを使用して、受信メッセージのコピーを作成します。 検査中にリーダーの位置が変更されていないため、メッセージのプロパティとメッセージ ヘッダーもコピーされる新しく作成されたメッセージで参照できるため、元のメッセージの正確な複製が作成されます。
private Message CreateMessageCopy(Message message,
XmlDictionaryReader body)
{
Message copy = Message.CreateMessage(message.Version,message.Headers.Action,body);
copy.Headers.CopyHeaderFrom(message,0);
copy.Properties.CopyProperties(message.Properties);
return copy;
}
サービスへの操作セレクターの追加
サービス ディスパッチ操作セレクターは、Windows Communication Foundation (WCF) ディスパッチャーの拡張機能です。 双方向コントラクトのコールバック チャネルでメソッドを選択する場合、クライアント操作セレクターもあります。これは、ここで説明するディスパッチ操作セレクターと非常に似ていますが、このサンプルでは明示的に説明されていません。
ほとんどのサービス モデル拡張機能と同様に、ディスパッチ操作セレクターは動作を使用してディスパッチャーに追加されます。 動作は、ディスパッチ ランタイム (またはクライアント ランタイム) に 1 つ以上の拡張機能を追加するか、その設定を変更する構成オブジェクトです。
操作セレクターにはコントラクト スコープがあるため、ここで実装する適切な動作は IContractBehaviorです。 インターフェイスは、次のコードに示すように Attribute 派生クラスに実装されているため、動作を任意のサービス コントラクトに宣言的に追加できます。 ServiceHostが開いてディスパッチ ランタイムがビルドされるたびに、コントラクト、操作、およびサービス実装の属性として、またはサービス構成の要素として検出されたすべての動作が自動的に追加され、その後、拡張機能の提供または既定の構成の変更が求められます。
簡潔にするために、次のコード抜粋はメソッド ApplyDispatchBehaviorの実装のみを示しています。これは、このサンプルのディスパッチャーの構成変更に影響を与えます。 他のメソッドは、何も処理せずに呼び出し元に戻るため、表示されません。
[AttributeUsage(AttributeTargets.Class|AttributeTargets.Interface)]
class DispatchByBodyElementBehaviorAttribute : Attribute, IContractBehavior
{
// public void AddBindingParameters(...)
// public void ApplyClientBehavior(...)
// public void Validate(...)
まず、ApplyDispatchBehavior実装は、サービス エンドポイントのContractDescription内のOperationDescription要素を反復処理することによって、操作セレクターのルックアップ ディクショナリを設定します。 次に、各操作の説明が、 DispatchBodyElementAttribute
動作の存在を調べ、このサンプルでも定義されている IOperationBehavior の実装を示します。 このクラスも動作ですが、パッシブであり、ディスパッチ ランタイムに構成の変更を積極的に反映しません。 すべてのメソッドは、アクションを実行せずに呼び出し元に戻ります。 操作の動作は、新しいディスパッチ メカニズムに必要なメタデータ (つまり、操作が選択されている本文要素の修飾名) をそれぞれの操作に関連付けることができるようにするためにのみ存在します。
このような動作が見つかった場合は、XML 修飾名 (QName
プロパティ) と操作名 (Name
プロパティ) から作成された値ペアがディクショナリに追加されます。
ディクショナリが設定されると、この情報を使用して新しい DispatchByBodyElementOperationSelector
が構築され、ディスパッチ ランタイムの操作セレクターとして設定されます。
public void ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.DispatchRuntime dispatchRuntime)
{
Dictionary<XmlQualifiedName,string> dispatchDictionary =
new Dictionary<XmlQualifiedName,string>();
foreach( OperationDescription operationDescription in
contractDescription.Operations )
{
DispatchBodyElementAttribute dispatchBodyElement =
operationDescription.Behaviors.Find<DispatchBodyElementAttribute>();
if ( dispatchBodyElement != null )
{
dispatchDictionary.Add(dispatchBodyElement.QName,
operationDescription.Name);
}
}
dispatchRuntime.OperationSelector =
new DispatchByBodyElementOperationSelector(
dispatchDictionary,
dispatchRuntime.UnhandledDispatchOperation.Name);
}
}
サービスの実装
このサンプルで実装される動作は、サービス コントラクトの関数であるワイヤからのメッセージの解釈とディスパッチの方法に直接影響します。 したがって、動作は、使用を選択するサービス実装でサービス コントラクト レベルで宣言する必要があります。
サンプル プロジェクト サービスは、 DispatchByBodyElementBehaviorAttribute
コントラクトの動作を IDispatchedByBody
サービス コントラクトに適用し、2 つの操作のそれぞれに OperationForBodyA()
と OperationForBodyB()
に DispatchBodyElementAttribute
操作動作のラベルを付けます。 このコントラクトを実装するサービスのサービス ホストを開くと、前述のようにディスパッチャー ビルダーによってこのメタデータが取得されます。
操作セレクターはメッセージ本文要素のみに基づいてディスパッチし、"Action" を無視するため、OperationContractAttributeのReplyAction
プロパティにワイルドカード "*" を割り当てることで、返された応答の "Action" ヘッダーをチェックしないようにランタイムに指示する必要があります。 さらに、"Action" プロパティがワイルドカード "*" に設定されている既定の操作が必要です。 既定の操作は、ディスパッチできず、 DispatchBodyElementAttribute
を持たないすべてのメッセージを受信します。
[ServiceContract(Namespace="http://Microsoft.ServiceModel.Samples"),
DispatchByBodyElementBehavior]
public interface IDispatchedByBody
{
[OperationContract(ReplyAction="*"),
DispatchBodyElement("bodyA","http://tempuri.org")]
Message OperationForBodyA(Message msg);
[OperationContract(ReplyAction = "*"),
DispatchBodyElement("bodyB", "http://tempuri.org")]
Message OperationForBodyB(Message msg);
[OperationContract(Action="*", ReplyAction="*")]
Message DefaultOperation(Message msg);
}
サンプル サービスの実装は簡単です。 すべてのメソッドは、受信したメッセージを応答メッセージにラップし、クライアントにエコーバックします。
サンプルの実行とビルド
サンプルを実行すると、次の (書式設定された) 出力と同様に、操作応答の本文の内容がクライアント コンソール ウィンドウに表示されます。
クライアントは、本文コンテンツ要素の名前が bodyA
、 bodyB
、および bodyX
である 3 つのメッセージをサービスに送信します。 前の説明と示されているサービス コントラクトから遅延できるように、 bodyA
要素を持つ受信メッセージは、 OperationForBodyA()
メソッドにディスパッチされます。
bodyX
本文要素を持つメッセージの明示的なディスパッチ ターゲットがないため、メッセージはDefaultOperation()
にディスパッチされます。 各サービス操作は、受信したメッセージ本文をメソッドに固有の要素にラップして返します。これは、このサンプルの入力メッセージと出力メッセージを明確に関連付けるために行われます。
<?xml version="1.0" encoding="IBM437"?>
<replyBodyA xmlns="http://tempuri.org">
<q:bodyA xmlns:q="http://tempuri.org">test</q:bodyA>
</replyBodyA>
<?xml version="1.0" encoding="IBM437"?>
<replyBodyB xmlns="http://tempuri.org">
<q:bodyB xmlns:q="http://tempuri.org">test</q:bodyB>
</replyBodyB>
<?xml version="1.0" encoding="IBM437"?>
<replyDefault xmlns="http://tempuri.org">
<q:bodyX xmlns:q="http://tempuri.org">test</q:bodyX>
</replyDefault>
サンプルを設定、ビルド、実行するには
Windows Communication Foundation サンプル のOne-Time セットアップ手順を実行していることを確認します。
ソリューションをビルドするには、「 Windows Communication Foundation サンプルのビルド」の手順に従います。
単一または複数のコンピューター間の構成でサンプルを実行するには、「Windows Communication Foundation Samplesの実行」の手順に従います。