セッションとキュー

Session のサンプルでは、メッセージ キュー (MSMQ) トランスポートを介して、キュー通信で一連の関連メッセージを送受信する方法を示します。 このサンプルでは、netMsmqBinding バインディングを使用します。 サービスは自己ホスト型コンソール アプリケーションであるので、キューに置かれたメッセージをサービスが受信するようすを観察できます。

Note

このサンプルのセットアップ手順とビルド手順については、このトピックの最後を参照してください。

キュー通信では、クライアントはサービスとの通信にキューを使用します。 厳密には、クライアントはメッセージをキューに送信します。 サービスは、メッセージをキューから受信します。 したがって、キューを使用する通信では、サービスとクライアントが同時に実行されていなくてもかまいません。

クライアントは、グループ内で相互に関連する一連のメッセージを送信する場合があります。 メッセージを一緒に処理するか、または特定の順序で処理する必要がある場合は、キューを使用すると、単一の受信側アプリケーションでの処理に対応するためにメッセージをグループ化できます。 このことは、サーバーのグループに複数の受信側アプリケーションが存在する場合、メッセージのグループを同一の受信側アプリケーションで確実に処理する必要があるときに特に重要です。 キューに置かれたセッションは、同時処理を行う必要がある一連の関連メッセージを送受信する目的で使用される機構です。 キューに置かれたセッションには、このパターンを表すトランザクションが必要です。

このサンプルでは、クライアントは、複数のメッセージを単一トランザクションのスコープ内にあるセッションの一部として、サービスに送信します。

サービス コントラクトは IOrderTaker です。これは、キューでの使用に適した一方向サービスを定義します。 このコントラクトに使用される SessionMode は、次のサンプル コードで示すように、メッセージがセッションの一部であることを示します。

[ServiceContract(Namespace = "http://Microsoft.ServiceModel.Samples", SessionMode=SessionMode.Required)]
public interface IOrderTaker
{
    [OperationContract(IsOneWay = true)]
    void OpenPurchaseOrder(string customerId);

    [OperationContract(IsOneWay = true)]
    void AddProductLineItem(string productId, int quantity);

    [OperationContract(IsOneWay = true)]
    void EndPurchaseOrder();
}

このサービスは、最初の操作をトランザクションに参加させ、トランザクションを自動的に完了しないように、サービス操作を定義します。 後続の操作も同じトランザクションに参加しますが、同様に自動的に完了しません。 セッションの最後の操作は、トランザクションを自動的に完了します。 つまりサービス コントラクトでは、複数の操作呼び出しで同じトランザクションが使用されます。 任意の操作で例外がスローされた場合は、トランザクションはロールバックされてセッションはキューに戻ります。 最後の操作が正常に完了すると、トランザクションはコミットされます。 このサービスでは PerSession として InstanceContextMode を使用します。セッション内のすべてのメッセージをサービスの同じインスタンスで受信するためです。

[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerSession)]
public class OrderTakerService : IOrderTaker
{
    PurchaseOrder po;

    [OperationBehavior(TransactionScopeRequired = true,
                                 TransactionAutoComplete = false)]
    public void OpenPurchaseOrder(string customerId)
    {
        Console.WriteLine("Creating purchase order");
        po = new PurchaseOrder(customerId);
    }

    [OperationBehavior(TransactionScopeRequired = true,
                                  TransactionAutoComplete = false)]
    public void AddProductLineItem(string productId, int quantity)
    {
        po.AddProductLineItem(productId, quantity);
        Console.WriteLine("Product " + productId + " quantity " +
                            quantity + " added to purchase order");
    }

    [OperationBehavior(TransactionScopeRequired = true,
                                  TransactionAutoComplete = true)]
    public void EndPurchaseOrder()
    {
       Console.WriteLine("Purchase Order Completed");
       Console.WriteLine();
       Console.WriteLine(po.ToString());
    }
}

サービスは自己ホスト型です。 MSMQ トランスポートを使用する場合、使用するキューをあらかじめ作成しておく必要があります。 手動で作成することもコードで作成することもできます。 このサンプルでは、キューの存在を確認して、必要な場合は作成するための System.Messaging コードがサービスに含まれています。 キュー名は、AppSettings クラスを使用して構成ファイルから読み込まれます。

// Host the service within this EXE console application.
public static void Main()
{
    // Get MSMQ queue name from app settings in configuration.
    string queueName = ConfigurationManager.AppSettings["queueName"];

    // Create the transacted MSMQ queue if necessary.
    if (!MessageQueue.Exists(queueName))
        MessageQueue.Create(queueName, true);

    // Create a ServiceHost for the OrderTakerService type.
    using (ServiceHost serviceHost = new ServiceHost(typeof(OrderTakerService)))
    {
        // Open the ServiceHost to create listeners and start listening for messages.
        serviceHost.Open();

        // The service can now be accessed.
        Console.WriteLine("The service is ready.");
        Console.WriteLine("Press <ENTER> to terminate service.");
        Console.WriteLine();
        Console.ReadLine();

        // Close the ServiceHost to shutdown the service.
        serviceHost.Close();
    }
}

MSMQ キュー名は、構成ファイルの appSettings セクションで指定されます。 サービスのエンドポイントは、構成ファイルの system.serviceModel セクションで定義されます。このエンドポイントは netMsmqBinding バインディングを指定します。

<appSettings>
  <!-- Use appSetting to configure MSMQ queue name. -->
  <add key="queueName" value=".\private$\ServiceModelSamplesSession" />
</appSettings>

<system.serviceModel>
  <services>
    <service name="Microsoft.ServiceModel.Samples.OrderTakerService"
        behaviorConfiguration="CalculatorServiceBehavior">
      ...
      <!-- Define NetMsmqEndpoint -->
      <endpoint address="net.msmq://localhost/private/ServiceModelSamplesSession"
                binding="netMsmqBinding"
                contract="Microsoft.ServiceModel.Samples.IOrderTaker" />
      ...
    </service>
  </services>
  ...
</system.serviceModel>

クライアントはトランザクション スコープを作成します。 セッション内のすべてのメッセージは、トランザクション スコープ内のキューに送信され、すべてのメッセージが成功か失敗かを示すアトミックな単位として扱われます。 トランザクションは Complete を呼び出してコミットされます。

//Create a transaction scope.
using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required))
{
    // Create a client with given client endpoint configuration.
    OrderTakerClient client = new OrderTakerClient("OrderTakerEndpoint");
    // Open a purchase order.
    client.OpenPurchaseOrder("somecustomer.com");
    Console.WriteLine("Purchase Order created");

    // Add product line items.
    Console.WriteLine("Adding 10 quantities of blue widget");
    client.AddProductLineItem("Blue Widget", 10);

    Console.WriteLine("Adding 23 quantities of red widget");
    client.AddProductLineItem("Red Widget", 23);

    // Close the purchase order.
    Console.WriteLine("Closing the purchase order");
    client.EndPurchaseOrder();

    //Closing the client gracefully closes the connection and cleans up resources.
    client.Close();

    // Complete the transaction.
    scope.Complete();
}

注意

セッション内のすべてのメッセージに対して使用できるトランザクションは 1 つだけです。さらに、トランザクションをコミットする前にセッション内のすべてのメッセージを送信する必要があります。 クライアントを閉じると、セッションも閉じられます。 したがって、トランザクションが完了する前にクライアントを閉じ、セッション内のすべてのメッセージをキューに送信する必要があります。

サンプルを実行すると、クライアントとサービスのアクティビティがサービスとクライアントの両方のコンソール ウィンドウに表示されます。 サービスがクライアントから受信したメッセージを表示できます。 どちらかのコンソールで Enter キーを押すと、サービスとクライアントがどちらもシャットダウンされます。 キューが使用されているので、クライアントとサービスが同時に実行されている必要はありません。 クライアントを実行してシャットダウンした後にサービスを起動しても、サービスはメッセージを受信します。

クライアント側

Purchase Order created
Adding 10 quantities of blue widget
Adding 23 quantities of red widget
Closing the purchase order

Press <ENTER> to terminate client.

サービス側

The service is ready.
Press <ENTER> to terminate service.

Creating purchase order
Product Blue Widget quantity 10 added to purchase order
Product Red Widget quantity 23 added to purchase order
Purchase Order Completed

Purchase Order: 7c86fef0-2306-4c51-80e6-bcabcc1a6e5e
        Customer: somecustomer.com
        OrderDetails
                Order LineItem: 10 of Blue Widget @unit price: $2985
                Order LineItem: 23 of Red Widget @unit price: $156
        Total cost of this order: $33438
        Order status: Pending

サンプルをセットアップ、ビルド、実行する

  1. Windows Communication Foundation サンプルの 1 回限りのセットアップの手順を実行したことを確認します。

  2. ソリューションの C#、C++、Visual Basic .NET 版をビルドするには、「Windows Communication Foundation サンプルのビルド」の手順に従います。

  3. 単一または複数コンピューター構成でサンプルを実行するには、「Windows Communication Foundation サンプルの実行」の手順に従います。

NetMsmqBinding を使用する場合の既定では、トランスポート セキュリティが有効です。 MSMQ トランスポート セキュリティには、MsmqAuthenticationModeMsmqProtectionLevel. の 2 つの関連プロパティがあります。既定では、認証モードは Windows に設定され、保護レベルは Sign に設定されます。 MSMQ の認証機能と署名機能を利用するには、ドメインに MSMQ があることと、MSMQ に関する Active Directory の統合オプションがインストールされていることが必要です。 この条件を満たしていないコンピューターでこのサンプルを実行すると、エラーになります。

ワークグループに参加しているコンピューターでサンプルを実行する

  1. ドメインに属していないコンピューター、または Active Directory 統合がインストールされていないコンピューターを使用する場合は、トランスポート セキュリティをオフにします。オフにするには、認証モードと保護レベルを None にします。この構成の例を次に示します。

    <system.serviceModel>
      <services>
        <service name="Microsoft.ServiceModel.Samples.OrderTakerService"
                 behaviorConfiguration="OrderTakerServiceBehavior">
          <host>
            <baseAddresses>
              <add baseAddress=
             "http://localhost:8000/ServiceModelSamples/service"/>
            </baseAddresses>
          </host>
          <!-- Define NetMsmqEndpoint -->
          <endpoint
              address=
            "net.msmq://localhost/private/ServiceModelSamplesSession"
              binding="netMsmqBinding"
              bindingConfiguration="Binding1"
           contract="Microsoft.ServiceModel.Samples.IOrderTaker" />
          <!-- The mex endpoint is exposed at-->
          <!--http://localhost:8000/ServiceModelSamples/service/mex. -->
          <endpoint address="mex"
                    binding="mexHttpBinding"
                    contract="IMetadataExchange" />
        </service>
      </services>
    
      <bindings>
        <netMsmqBinding>
          <binding name="Binding1">
            <security mode="None" />
          </binding>
        </netMsmqBinding>
      </bindings>
    
        <behaviors>
          <serviceBehaviors>
            <behavior name="OrderTakerServiceBehavior">
              <serviceMetadata httpGetEnabled="True"/>
            </behavior>
          </serviceBehaviors>
        </behaviors>
    
      </system.serviceModel>
    
  2. サーバーとクライアントの両方の構成を変更したことを確認してから、サンプルを実行します。

    注意

    セキュリティ モードを None に設定することは、MsmqAuthenticationModeMsmqProtectionLevel、および Message のセキュリティを None に設定することと同じです。