本文要素別のディスパッチDispatch by Body Element

このサンプルでは、入力メッセージを操作に割り当てるための代替アルゴリズムを実装する方法を示します。This sample demonstrates how to implement an alternate algorithm for assigning incoming messages to operations.

既定では、サービス モデル ディスパッチャは、メッセージの WS-Addressing の "Action" ヘッダーまたは HTTP SOAP 要求でのこれと同等の情報に基づいて、入力メッセージの適切な処理メソッドを選択します。By default, the service model dispatcher selects the appropriate handling method for an incoming message based on the message's WS-Addressing "Action" header or the equivalent information in the HTTP SOAP request.

WS-I Basic Profile 1.1 のガイドラインに従っていない SOAP 1.1 Web サービス スタックの一部では、Action URI に基づくのではなく、SOAP 本文内にある最初の要素の XML 修飾名に基づいてメッセージをディスパッチします。Some SOAP 1.1 Web services stacks that do not follow the WS-I Basic Profile 1.1 guidelines do not dispatch messages based on the Action URI, but rather based on the XML qualified name of the first element inside the SOAP body. 同様に、クライアント側のこうしたスタックでは、空または任意の HTTP SoapAction ヘッダーが含まれるメッセージを送信する場合があります。これは SOAP 1.1 の仕様で許可されていました。Likewise, the client side of these stacks might send messages with an empty or arbitrary HTTP SoapAction header, which was permitted by the SOAP 1.1 specification.

メッセージをメソッドにディスパッチする方法を変更するために、このサンプルでは IDispatchOperationSelector 拡張インターフェイスを DispatchByBodyElementOperationSelector に実装します。To change the way messages are dispatched to methods, the sample implements the IDispatchOperationSelector extensibility interface on the DispatchByBodyElementOperationSelector. このクラスは、メッセージ本文の最初の要素に基づいて操作を選択します。This class selects operations based on the first element of the message body.

クラス コンストラクタには、XmlQualifiedName と文字列のペアが記録されたディクショナリが必要です。この修飾名は SOAP 本文の最初の子の名前を示し、文字列は該当する操作名を示します。The class constructor expects a dictionary populated with pairs of XmlQualifiedName and strings, whereby the qualified names indicate the name of the first child of the SOAP body and the strings indicate the matching operation name. defaultOperationName は、このディクショナリで照合できないすべてのメッセージを受信する操作の名前です。The defaultOperationName is the name of the operation that receives all messages that cannot be matched against this dictionary:

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 を実装すると、ビルドが非常に簡単です。IDispatchOperationSelector implementations are very straightforward to build as there is only one method on the interface: SelectOperation. このメソッドでの処理は、入力メッセージを検査し、現在のエンドポイントのサービス コントラクトでのメソッド名と同じ文字列を返すことです。The job of this method is to inspect an incoming message and to return a string that equals the name of a method on the service contract for the current endpoint.

このサンプルでは、操作セレクタは XmlDictionaryReader を通じて、入力メッセージの本文の GetReaderAtBodyContents を取得します。In this sample, the operation selector acquires an XmlDictionaryReader for the incoming message's body using GetReaderAtBodyContents. このメソッドは既にリーダーをメッセージ本文の最初の子に配置しています。そのため、現在の要素の名前と名前空間の URI を取得して、それらを XmlQualifiedName に結合することは簡単です。その後、これは操作セレクタによって保持されているディクショナリから対応する操作を検索する際に使用されます。This method already positions the reader on the first child of the message's body so that it is sufficient to get the current element's name and namespace URI and combine them into an XmlQualifiedName that is then used for looking up the corresponding operation from the dictionary held by the operation selector.

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、またはメッセージ本文のコンテンツへのアクセスを提供する他のメソッドを使用してメッセージ本文にアクセスすると、そのメッセージは "read" とマークされます。つまり、メッセージは以降の処理に対して無効になります。Accessing the message body with GetReaderAtBodyContents or any of the other methods that provide access to the message's body content causes the message to be marked as "read", which means that the message is invalid for any further processing. したがって、操作セレクタは次のコードに示すメソッドを使用して入力メッセージのコピーを作成します。Therefore, the operation selector creates a copy of the incoming message with the method shown in the following code. リーダーの位置は検査中には変更されていないので、新しく作成されたメッセージはこれを参照できます。入力メッセージのプロパティとヘッダーもこの新しいメッセージにコピーされるので、その結果、元のメッセージの完全な複製ができあがります。Because the reader's position has not been changed during the inspection, it can be referenced by the newly created message to which the message properties and the message headers are also copied, which results in an exact clone of the original message:

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

サービスへの操作セレクタの追加Adding an Operation Selector to a Service

サービスディスパッチ操作セレクターは、Windows Communication Foundation (WCF) ディスパッチャーの拡張機能です。Service dispatch operation selectors are extensions to the Windows Communication Foundation (WCF) dispatcher. 二重のコントラクトのコールバック チャネルでメソッドを選択する場合、クライアントの操作セレクタも存在します。この操作セレクタはここで説明するディスパッチ操作セレクタと非常によく似ていますが、このサンプルでは明示的には説明しません。For selecting methods on the callback channel of duplex contracts, there are also client operation selectors, which work very much like the dispatch operation selectors described here, but which are not explicitly covered in this sample.

ディスパッチ操作セレクタはほとんどのサービス モデル拡張と同様、動作を使用してディスパッチャに追加されます。Like most service model extensions, dispatch operation selectors are added to the dispatcher using behaviors. 動作は、ディスパッチランタイム (またはクライアントランタイム) に1つ以上の拡張機能を追加する構成オブジェクトであり、それ以外の場合はその設定を変更します。A behavior is a configuration object, which either adds one or more extensions to the dispatch runtime (or to the client runtime) or otherwise changes its settings.

操作セレクタにはコントラクトのスコープがあるので、ここで実装する適切な動作は IContractBehavior です。Because operation selectors have contract scope, the appropriate behavior to implement here is the IContractBehavior. このインターフェイスは、次のコードに示すように Attribute 派生クラスに実装されているため、この動作は任意のサービス コントラクトに宣言として追加できます。Because the interface is implemented on a Attribute derived class as shown in the following code, the behavior can be declaratively added to any service contract. ServiceHost が開いてディスパッチ ランタイムがビルドされるたびに、コントラクト、操作、およびサービス実装の属性の形をとるか、またはサービス構成内の要素の形をとるすべての動作が自動的に追加され、その後拡張機能の支援または既定の構成の変更を求められます。Whenever a ServiceHost is opened and the dispatch runtime is built, all behaviors found either as attributes on contracts, operations, and service implementations or as element in the service configuration are automatically added and subsequently asked to contribute extensions or modify the default configuration.

次のコードの抜粋では、簡略化のため、メソッド ApplyDispatchBehavior の実装のみを示します。これは、このサンプルのディスパッチャの構成変更に影響します。For brevity, the following code excerpt only shows the implementation of the method ApplyDispatchBehavior, which effects the configuration changes for the dispatcher in this sample. 他のメソッドが表示されていないのは、何の処理も行わずに呼び出し元に返されるためです。The other methods are not shown because they return to the caller without doing any work.

[AttributeUsage(AttributeTargets.Class|AttributeTargets.Interface)]  
class DispatchByBodyElementBehaviorAttribute : Attribute, IContractBehavior  
{  
    // public void AddBindingParameters(...)
    // public void ApplyClientBehavior(...)  
    // public void Validate(...)  

最初に、ApplyDispatchBehavior を実装し、サービス エンドポイントの OperationDescriptionContractDescription 要素を反復処理することによって、操作セレクタの検索ディクショナリを設定します。First, the ApplyDispatchBehavior implementation sets up the lookup dictionary for the operation selector by iterating over the OperationDescription elements in the service endpoint's ContractDescription. 次に、各操作の記述に DispatchBodyElementAttribute 動作が存在するかどうかが検査されます。この動作は、このサンプルでも定義されている IOperationBehavior の実装です。Then, each operation description is inspected for the presence of the DispatchBodyElementAttribute behavior, an implementation of IOperationBehavior that is also defined in this sample. このクラスも動作の 1 つですが、パッシブな動作です。ディスパッチ ランタイムに対して構成変更をアクティブに支援することはありません。While this class is also a behavior, it is passive and does not actively contribute any configuration changes to the dispatch runtime. このすべてのメソッドは、アクションを起こすことなく呼び出し元に返されます。All of its methods return to the caller without taking any actions. この操作の動作は、新しいディスパッチ機構に必要なメタデータ、つまり見つかった場合に操作で選択される本文要素の修飾名を、該当する操作に関連付ける目的でのみ存在します。The operation behavior only exists so that the metadata required for the new dispatch mechanism, namely the qualified name of the body element on whose occurrence an operation is selected, can be associated with the respective operations.

このような動作が見つかると、XML 修飾名 (QName プロパティ) と操作名 (Name プロパティ) から作成された値のペアがディクショナリに追加されます。If such a behavior is found, a value pair created from the XML qualified name (QName property) and the operation name (Name property) is added to the dictionary.

ディクショナリが設定されると、新しい DispatchByBodyElementOperationSelector がこの情報で構築され、ディスパッチ ランタイムの操作セレクタとして設定されます。Once the dictionary is populated, a new DispatchByBodyElementOperationSelector is constructed with this information and set as the operation selector of the dispatch runtime:

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);  
    }  
}  

サービスの実装Implementing the Service

このサンプルに実装されている動作は、通信回線からのメッセージの解釈方法とディスパッチ方法に直接影響します。これはサービス コントラクトの機能です。The behavior implemented in this sample directly affects how messages from the wire are interpreted and dispatched, which is a function of the service contract. そのため、この動作を使用することを選択したサービス実装では、この動作をサービス コントラクト レベルで宣言する必要があります。Consequently, the behavior should be declared on the service contract level in any service implementation that chooses to use it.

サンプルプロジェクトサービスは、 DispatchByBodyElementBehaviorAttribute コントラクトの動作を IDispatchedByBody サービスコントラクトに適用し、2つの操作のそれぞれに操作の動作をラベル OperationForBodyA() 付けし OperationForBodyB() DispatchBodyElementAttribute ます。The sample project service applies the DispatchByBodyElementBehaviorAttribute contract behavior to the IDispatchedByBody service contract and labels each of the two operations OperationForBodyA() and OperationForBodyB() with a DispatchBodyElementAttribute operation behavior. このコントラクトを実装しているサービスのサービス ホストが開かれると、このメタデータは前で説明したようにディスパッチャ ビルダによって取得されます。When a service host for a service that implements this contract is opened, this metadata is picked up by the dispatcher builder as previously described.

操作セレクタはメッセージ本文の要素のみに基づいてディスパッチし、"Action" を無視するので、返された応答の "Action" ヘッダーをチェックしないようランタイムに通知する必要があります。これを行うには、ワイルドカード "*" を ReplyActionOperationContractAttribute プロパティに割り当てます。Because the operation selector dispatches solely based on the message body element and ignores the "Action", it is required to tell the runtime not to check the "Action" header on the returned replies by assigning the wildcard "*" to the ReplyAction property of OperationContractAttribute. さらに、"Action" プロパティがワイルドカード "" に設定されている既定の操作が必要です * 。Furthermore, it is required to have a default operation that has the "Action" property set to the wildcard "*". 既定の操作は、ディスパッチできず、DispatchBodyElementAttribute を持たないすべてのメッセージを受信します。The default operation receives all messages which cannot be dispatched and does not have a 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);  
}  

このサンプルのサービス実装は簡単です。The sample service implementation is straightforward. どのメソッドも受信メッセージを応答メッセージにラップし、クライアントに再度エコーします。Every method wraps the received message into a reply message and echoes it back to the client.

サンプルの実行とビルドRunning and Building the Sample

このサンプルを実行すると、次に示す (書式設定された) 出力と同様の、操作応答の内容がクライアントのコンソール ウィンドウに表示されます。When you run the sample, the body content of the operation responses are displayed in the client console window similar to the following (formatted) output.

クライアントは 3 つのメッセージをサービスに送信します。このメッセージ本文のコンテンツ要素の名前は、それぞれ bodyAbodyB、および bodyX です。The client sends three messages to the service whose body content element is named bodyA, bodyB, and bodyX, respectively. 以上の説明とサービス コントラクトからわかるように、bodyA 要素が含まれる受信メッセージは、OperationForBodyA() メソッドにディスパッチされます。As can be deferred from the previous description and the service contract shown, the incoming message with the bodyA element is dispatched to the OperationForBodyA() method. bodyX 本文要素が含まれるメッセージについては、明示的なディスパッチ対象がないので、このメッセージは DefaultOperation() にディスパッチされます。Because there is no explicit dispatch target for the message with the bodyX body element, the message is dispatched to the DefaultOperation(). 各サービス操作は、受信されたメッセージ本文をメソッド固有の要素にラップして返します。このサンプルでは、入力メッセージと出力メッセージを明確に関連付けるためにこの操作を行います。Each of the service operations wraps the received message body into an element specific to the method and returns it, which is done to correlate input and output messages clearly for this sample:

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

サンプルをセットアップ、ビルド、および実行するにはTo set up, build, and run the sample

  1. Windows Communication Foundation サンプルの1回限りのセットアップ手順を実行したことを確認します。Ensure that you have performed the One-Time Setup Procedure for the Windows Communication Foundation Samples.

  2. ソリューションをビルドするには、「 Windows Communication Foundation サンプルのビルド」の手順に従います。To build the solution, follow the instructions in Building the Windows Communication Foundation Samples.

  3. サンプルを単一コンピューター構成または複数コンピューター構成で実行するには、「 Windows Communication Foundation サンプルの実行」の手順に従います。To run the sample in a single- or cross-machine configuration, follow the instructions in Running the Windows Communication Foundation Samples.

重要

サンプルは、既にコンピューターにインストールされている場合があります。The samples may already be installed on your machine. 続行する前に、次の (既定の) ディレクトリを確認してください。Check for the following (default) directory before continuing.

<InstallDrive>:\WF_WCF_Samples

このディレクトリが存在しない場合は、 Windows Communication Foundation (wcf) および Windows Workflow Foundation (WF) のサンプルの .NET Framework 4にアクセスして、すべての WINDOWS COMMUNICATION FOUNDATION (wcf) とサンプルをダウンロードして WFWF ください。If this directory does not exist, go to Windows Communication Foundation (WCF) and Windows Workflow Foundation (WF) Samples for .NET Framework 4 to download all Windows Communication Foundation (WCF) and WFWF samples. このサンプルは、次のディレクトリに格納されます。This sample is located in the following directory.

<InstallDrive>:\WF_WCF_Samples\WCF\Extensibility\Interop\AdvancedDispatchByBody