メッセージ相関
MessageCorrelation サンプルでは、メッセージ キュー (MSMQ) アプリケーションで MSMQ メッセージを Windows Communication Foundation (WCF) サービスに送信する方法と、要求または応答のシナリオで、メッセージを送信側のアプリケーションと受信側のアプリケーション間で相互に関連付ける方法を示します。 このサンプルでは、msmqIntegrationBinding バインディングを使用します。 この場合、サービスは自己ホスト型コンソール アプリケーションで、サービスがキュー内のメッセージを受信したかどうかを監視できます。 k
サービスは、送信側からの受信メッセージを処理し、送信側に応答メッセージを返信します。 送信側は、受信した応答を、最初に送信した要求に関連付けます。 メッセージの MessageID
プロパティと CorrelationID
プロパティを使用すると、要求メッセージと応答メッセージが関連付けられます。
IOrderProcessor
サービス コントラクトは、キューでの使用に適した一方向サービス操作を定義します。 MSMQ メッセージには Action ヘッダーがないので、さまざまな MSMQ メッセージを操作コントラクトに自動的にマッピングすることはできません。 したがってこの場合、存在する操作コントラクトは 1 つだけです。 サービス内に複数の操作コントラクトを定義する場合は、ディスパッチする操作コントラクトを決定するために使用できる、MSMQ メッセージ内のヘッダー (ラベルや correlationID など) に関する情報をアプリケーションで提供する必要があります。
さらに、MSMQ メッセージには、操作コントラクトの別のパラメータにマッピングされるヘッダーに関する情報は含まれません。 したがって、操作コントラクトに存在するパラメータは 1 つだけです。 パラメーターの型は MsmqMessage<T> です。これには、基になる MSMQ メッセージが含まれています。 MsmqMessage<T>
クラスの型 "T" は、シリアル化されて MSMQ メッセージ本文に含まれているデータを表します。 このサンプルでは、PurchaseOrder
型がシリアル化されて MSMQ メッセージ本文になっています。
[ServiceContract(Namespace = "http://Microsoft.ServiceModel.Samples")]
[ServiceKnownType(typeof(PurchaseOrder))]
public interface IOrderProcessor
{
[OperationContract(IsOneWay = true, Action = "*")]
void SubmitPurchaseOrder(MsmqMessage<PurchaseOrder> msg);
}
このサービス操作は発注書を処理し、サービス コンソール ウィンドウにその発注書の内容と状態を表示します。 OperationBehaviorAttribute はこの操作を構成してキューを伴うトランザクションに登録し、操作が返されたときにトランザクションに完了とマークします。 PurchaseOrder
には、サービスで処理する必要のある発注の詳細が含まれています。
// Service class that implements the service contract.
public class OrderProcessorService : IOrderProcessor
{
[OperationBehavior(TransactionScopeRequired = true,
TransactionAutoComplete = true)]
public void SubmitPurchaseOrder(MsmqMessage<PurchaseOrder> ordermsg)
{
PurchaseOrder po = (PurchaseOrder)ordermsg.Body;
Random statusIndexer = new Random();
po.Status = PurchaseOrder.OrderStates[statusIndexer.Next(3)];
Console.WriteLine("Processing {0} ", po);
//Send a response to the client that the order has been received
// and is pending fulfillment.
SendResponse(ordermsg);
}
private void SendResponse(MsmqMessage<PurchaseOrder> ordermsg)
{
OrderResponseClient client = new OrderResponseClient("OrderResponseEndpoint");
//Set the correlation ID such that the client can correlate the response to the order.
MsmqMessage<PurchaseOrder> orderResponseMsg = new MsmqMessage<PurchaseOrder>(ordermsg.Body);
orderResponseMsg.CorrelationId = ordermsg.Id;
using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required))
{
client.SendOrderResponse(orderResponseMsg);
scope.Complete();
}
client.Close();
}
}
サービスは、MSMQ メッセージをキューに送信するためにカスタム クライアント OrderResponseClient
を使用します。 メッセージを受信して処理するアプリケーションは MSMQ アプリケーションであり、WCF アプリケーションではないため、2 つのアプリケーション間に暗黙のサービス コントラクトはありません。 したがって、このシナリオでは Svcutil.exe ツールを使用してプロキシを作成することはできません。
カスタム プロキシは基本的に、msmqIntegrationBinding
バインディングを使用してメッセージを送信するすべての WCF アプリケーションで同じです。 他のプロキシと異なり、サービス操作の範囲は含まれません。 メッセージ送信操作のみです。
[System.ServiceModel.ServiceContractAttribute(Namespace = "http://Microsoft.ServiceModel.Samples")]
public interface IOrderResponse
{
[System.ServiceModel.OperationContractAttribute(IsOneWay = true, Action = "*")]
void SendOrderResponse(MsmqMessage<PurchaseOrder> msg);
}
public partial class OrderResponseClient : System.ServiceModel.ClientBase<IOrderResponse>, IOrderResponse
{
public OrderResponseClient()
{ }
public OrderResponseClient(string configurationName)
: base(configurationName)
{ }
public OrderResponseClient(System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress address)
: base(binding, address)
{ }
public void SendOrderResponse(MsmqMessage<PurchaseOrder> msg)
{
base.Channel.SendOrderResponse(msg);
}
}
サービスは自己ホスト型です。 MSMQ 統合トランスポートを使用する場合、使用するキューをあらかじめ作成しておく必要があります。 手動で作成することもコードで作成することもできます。 このサンプルでは、キューの存在を確認して、必要な場合は作成するための System.Messaging コードがサービスに含まれています。 キュー名は構成ファイルから読み込まれます。
public static void Main()
{
// Get the MSMQ queue name from application settings in configuration.
string queueName =
ConfigurationManager.AppSettings["orderQueueName"];
// Create the transacted MSMQ queue if necessary.
if (!MessageQueue.Exists(queueName))
MessageQueue.Create(queueName, true);
// Create a ServiceHost for the OrderProcessorService type.
using (ServiceHost serviceHost = new
ServiceHost(typeof(OrderProcessorService)))
{
serviceHost.Open();
// The service can now be accessed.
Console.WriteLine("The service is ready.");
Console.WriteLine("Press <ENTER> to terminate service.");
Console.ReadLine();
// Close the ServiceHost to shutdown the service.
serviceHost.Close();
}
}
発注要求が送信される MSMQ キューは、構成ファイルの appSettings セクションで指定されます。 クライアントおよびサービスのエンドポイントは、構成ファイルの system.serviceModel セクションで定義されます。 どちらも msmqIntegrationBinding
バインディングを指定します。
<appSettings>
<add key="orderQueueName" value=".\private$\Orders" />
</appSettings>
<system.serviceModel>
<client>
<endpoint name="OrderResponseEndpoint"
address="msmq.formatname:DIRECT=OS:.\private$\OrderResponse"
binding="msmqIntegrationBinding"
bindingConfiguration="OrderProcessorBinding"
contract="Microsoft.ServiceModel.Samples.IOrderResponse">
</endpoint>
</client>
<services>
<service
name="Microsoft.ServiceModel.Samples.OrderProcessorService">
<endpoint address="msmq.formatname:DIRECT=OS:.\private$\Orders"
binding="msmqIntegrationBinding"
bindingConfiguration="OrderProcessorBinding"
contract="Microsoft.ServiceModel.Samples.IOrderProcessor">
</endpoint>
</service>
</services>
<bindings>
<msmqIntegrationBinding>
<binding name="OrderProcessorBinding" >
<security mode="None" />
</binding>
</msmqIntegrationBinding>
</bindings>
</system.serviceModel>
クライアント アプリケーションは System.Messaging を使用して、非揮発性メッセージとトランザクション メッセージをキューに送信します。 メッセージの本文には発注書が含まれます。
static void PlaceOrder()
{
//Connect to the queue
MessageQueue orderQueue =
new MessageQueue(
ConfigurationManager.AppSettings["orderQueueName"])
// Create the purchase order.
PurchaseOrder po = new PurchaseOrder();
po.CustomerId = "somecustomer.com";
po.PONumber = Guid.NewGuid().ToString();
PurchaseOrderLineItem lineItem1 = new PurchaseOrderLineItem();
lineItem1.ProductId = "Blue Widget";
lineItem1.Quantity = 54;
lineItem1.UnitCost = 29.99F;
PurchaseOrderLineItem lineItem2 = new PurchaseOrderLineItem();
lineItem2.ProductId = "Red Widget";
lineItem2.Quantity = 890;
lineItem2.UnitCost = 45.89F;
po.orderLineItems = new PurchaseOrderLineItem[2];
po.orderLineItems[0] = lineItem1;
po.orderLineItems[1] = lineItem2;
Message msg = new Message();
msg.UseDeadLetterQueue = true;
msg.Body = po;
//Create a transaction scope.
using (TransactionScope scope = new
TransactionScope(TransactionScopeOption.Required))
{
// Submit the purchase order.
orderQueue.Send(msg, MessageQueueTransactionType.Automatic);
// Complete the transaction.
scope.Complete();
}
//Save the messageID for order response correlation.
orderMessageID = msg.Id;
Console.WriteLine("Placed the order, waiting for response...");
}
発注の応答を受信する MSMQ キューは、構成ファイルの appSettings セクションで指定されます。次のサンプル構成を参照してください。
Note
キュー名では、ドット (.) を使用してローカル コンピューターを表し、バックスラッシュを使用してパスを区切ります。 WCF エンドポイント アドレスでは msmq.formatname スキームが指定され、ローカル コンピューターを表す "localhost" が使用されます。 URI の msmq.formatname の後には、MSMQ ガイドラインに沿って正しく書式設定された形式名が続きます。
<appSettings>
<add key=" orderResponseQueueName" value=".\private$\Orders" />
</appSettings>
クライアント アプリケーションは、サービスに送信する発注要求メッセージの messageID
を保存し、サービスからの応答を待機します。 応答がキューに到着すると、クライアントは発注メッセージの correlationID
プロパティを使用して、送信した発注メッセージと応答を関連付けます。このプロパティには、クライアントが最初にサービスに送信した発注メッセージの messageID
が含まれています。
static void DisplayOrderStatus()
{
MessageQueue orderResponseQueue = new
MessageQueue(ConfigurationManager.AppSettings
["orderResponseQueueName"]);
//Create a transaction scope.
bool responseReceived = false;
orderResponseQueue.MessageReadPropertyFilter.CorrelationId = true;
while (!responseReceived)
{
Message responseMsg;
using (TransactionScope scope2 = new
TransactionScope(TransactionScopeOption.Required))
{
//Receive the Order Response message.
responseMsg =
orderResponseQueue.Receive
(MessageQueueTransactionType.Automatic);
scope2.Complete();
}
responseMsg.Formatter = new
System.Messaging.XmlMessageFormatter(new Type[] {
typeof(PurchaseOrder) });
PurchaseOrder responsepo = (PurchaseOrder)responseMsg.Body;
//Check if the response is for the order placed.
if (orderMessageID == responseMsg.CorrelationId)
{
responseReceived = true;
Console.WriteLine("Status of current Order: OrderID-{0},Order
Status-{1}", responsepo.PONumber, responsepo.Status);
}
else
{
Console.WriteLine("Status of previous Order: OrderID-{0},Order
Status-{1}", responsepo.PONumber, responsepo.Status);
}
}
}
サンプルを実行すると、クライアントとサービスのアクティビティがサービスとクライアントの両方のコンソール ウィンドウに表示されます。 サービスがクライアントからメッセージを受信し、クライアントに応答を返信するアクティビティを参照できます。 クライアントでは、サービスから受信した応答が表示されます。 どちらかのコンソールで Enter キーを押すと、サービスとクライアントがどちらもシャットダウンされます。
Note
このサンプルを実行するには、メッセージ キュー (MSMQ) がインストールされている必要があります。 インストールの手順については、「参照」セクションを参照してください。
サンプルをセットアップ、ビルド、実行する
Windows Communication Foundation サンプルの 1 回限りのセットアップの手順を実行したことを確認します。
サービスを最初に実行すると、サービスはキューが存在するかどうかを確認します。 キューが存在しない場合、サービスによってキューが作成されます。 最初にサービスを実行してキューを作成することも、MSMQ キュー マネージャーでキューを作成することもできます。 Windows 2008 でキューを作成するには、次の手順に従います。
Visual Studio 2012 でサーバー マネージャーを開きます。
[機能] タブを展開します。
[プライベート メッセージ キュー] を右クリックし、 [新規作成] 、 [プライベート キュー] の順に選択します。
[トランザクション] ボックスをオンにします。
新しいキューの名前として「
ServiceModelSamplesTransacted
」と入力します。
ソリューションの C# 版または Visual Basic .NET 版をビルドするには、「 Building the Windows Communication Foundation Samples」の手順に従います。
単一コンピューター構成でサンプルを実行するには、「Windows Communication Foundation サンプルの実行」の手順に従います。
複数のコンピューターでサンプルを実行する
サービスのプログラム ファイルを、言語固有のフォルダーにある \service\bin\ フォルダーからサービス コンピューターにコピーします。
クライアント プログラム ファイルを、言語固有のフォルダーにある \client\bin\ フォルダーからクライアント コンピューターにコピーします。
Client.exe.config ファイルを開き、orderQueueName を変更して "." の代わりにサービス コンピューター名を指定します。
Service.exe.config ファイルで、クライアント エンドポイントのアドレスを変更して "." の代わりにクライアント コンピューター名を指定します。
サービス コンピューターで、コマンド プロンプトから Service.exe を起動します。
クライアント コンピューターで、コマンド プロンプトから Client.exe を起動します。