MessageCorrelation サンプルでは、メッセージ キュー (MSMQ) アプリケーションが MSMQ メッセージを Windows Communication Foundation (WCF) サービスに送信する方法と、要求/応答シナリオで送信側アプリケーションと受信側アプリケーション間でメッセージを関連付ける方法を示します。 このサンプルでは、msmqIntegrationBinding バインドを使用します。 この場合のサービスは、キューに登録されたメッセージを受信するサービスを監視するためのセルフホステッド コンソール アプリケーションです。 k
このサービスは、送信者から受信したメッセージを処理し、応答メッセージを送信者に返します。 送信者は、受信した応答を最初に送信した要求に関連付けます。 メッセージの MessageID
プロパティと CorrelationID
プロパティは、要求メッセージと応答メッセージを関連付けるために使用されます。
IOrderProcessor
サービス コントラクトは、キューでの使用に適した一方向のサービス操作を定義します。 MSMQ メッセージには Action ヘッダーがないため、異なる MSMQ メッセージを操作コントラクトに自動的にマップすることはできません。 したがって、この場合、操作コントラクトは 1 つだけ存在できます。 サービスでより多くの操作コントラクトを定義する場合、アプリケーションは MSMQ メッセージのどのヘッダー (ラベルや correlationID など) を使用してディスパッチする操作コントラクトを決定できるかについての情報を提供する必要があります。
MSMQ メッセージには、操作コントラクトのさまざまなパラメーターにマップされるヘッダーに関する情報も含まれません。 そのため、操作コントラクトにはパラメーターを 1 つだけ指定できます。 パラメーターは、基になる MSMQ メッセージを含む MsmqMessage<T>型です。
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();
}
}
このサービスでは、カスタム クライアント OrderResponseClient
を使用して、MSMQ メッセージをキューに送信します。 メッセージを受信して処理するアプリケーションは、WCF アプリケーションではなく MSMQ アプリケーションであるため、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 セクションで指定されます。
注
キュー名にはローカルコンピュータ用にドット (.) を、パス内にはバックスラッシュの区切り記号を使用しています。 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 キーを押すと、サービスとクライアントがどちらもシャットダウンされます。
注
このサンプルでは、メッセージ キュー (MSMQ) をインストールする必要があります。 「関連項目」セクションの MSMQ のインストール手順を参照してください。
サンプルを設定、ビルド、実行する
Windows Communication Foundation サンプル のOne-Time セットアップ手順を実行していることを確認します。
サービスが最初に実行される場合は、キューが存在することを確認します。 キューが存在しない場合、サービスによってキューが作成されます。 最初にサービスを実行してキューを作成することも、MSMQ キュー マネージャーを使用して作成することもできます。 Windows 2008 でキューを作成するには、次の手順に従います。
Visual Studio 2012 でサーバー マネージャーを開きます。
[機能] タブを展開します。
[プライベート メッセージ キュー] を右クリックし、[新規作成]、[プライベート キュー] の順に選択します。
[トランザクション] ボックスをオンにします。
新しいキューの名前として「
ServiceModelSamplesTransacted
」と入力します。
ソリューションの C# または Visual Basic .NET エディションをビルドするには、「Windows Communication Foundation サンプルのビルド」の手順に従います。
単一コンピューター構成でサンプルを実行するには、「 Windows Communication Foundation サンプルの実行」の手順に従います。
複数のコンピューターでサンプルを実行する
サービス プログラム ファイルを、言語固有のフォルダーの下にある \service\bin\ フォルダーからサービス コンピューターにコピーします。
クライアント プログラム ファイルを、言語固有のフォルダーの下にある \client\bin\ フォルダーからクライアント コンピューターにコピーします。
Client.exe.config ファイルで orderQueueName を変更し、"." ではなくサービス コンピューター名を指定します。
Service.exe.config ファイルで、クライアント エンドポイント アドレスを変更して、"." ではなくクライアント コンピューター名を指定します。
サービス コンピューターで、コマンド プロンプトから Service.exe を起動します。
クライアント コンピューターで、コマンド プロンプトから Client.exe を起動します。