共用方式為


訊息相互關聯

MessageCorrelation 範例展示如何讓消息佇列(MSMQ)應用程式將 MSMQ 訊息傳送至 Windows Communication Foundation(WCF)服務,以及在要求/回應情境中,如何在傳送者與接收者的應用程式之間建立訊息關聯。 此範例會使用 msmqIntegrationBinding 系結。 在此情況下,服務是自我裝載的控制台應用程式,可讓您觀察接收佇列訊息的服務。 k

服務會處理從寄件者接收的訊息,並將回應消息傳回給寄件者。 傳送者會將其收到的回應與原先傳送的要求相互關聯。 訊息 MessageID 的 和 CorrelationID 屬性可用來將要求和回應訊息相互關聯。

服務 IOrderProcessor 合約會定義適合用於佇列的單向服務作業。 MSMQ 訊息沒有 Action 標頭,因此無法自動將不同的 MSMQ 訊息對應至作業合約。 因此,在此情況下,只能有一個作業合約。 如果您想要在服務中定義更多作業合約,應用程式必須提供 MSMQ 訊息中哪個標頭的資訊(例如標籤或 correlationID),可用來決定要分派的作業合約。

MSMQ 訊息也不包含將標頭與作業合約的不同參數對應的資訊。 因此,作業合約中只能有一個參數。 參數的類型為 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();
    }
}

服務會使用自定義用戶端 OrderResponseClient 將 MSMQ 訊息傳送至佇列。 因為接收和處理訊息的應用程式是 MSMQ 應用程式,而不是 WCF 應用程式,因此這兩個應用程式之間沒有隱含的服務合約。 因此,在此案例中,我們無法使用 Svcutil.exe 工具來建立 Proxy。

所有使用 msmqIntegrationBinding 系結來傳送訊息的 WCF 應用程式,自定義 Proxy 基本上都相同。 與其他 Proxy 不同,它不包含一系列服務作業。 這只是送出訊息作業。

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

系統會在組態檔的 appSettings 區段中指定傳送訂單要求的 MSMQ 佇列。 用戶端和服務端點定義於組態檔的 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」。 遵循 MSMQ 指導方針,正確格式的名稱會在 URI 內的 msmq.formatname 之後。

<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 安裝指示。

設定、建置和執行範例

  1. 請確定您已針對 Windows Communication Foundation 範例 執行One-Time 安裝程式。

  2. 如果服務先執行,它會檢查以確定佇列存在。 如果佇列不存在,服務將會建立一個佇列。 您可以先執行服務來建立佇列,也可以透過 MSMQ 佇列管理員建立佇列。 請遵循下列步驟,在 Windows 2008 中建立佇列。

    1. 在 Visual Studio 2012 中開啟伺服器管理員。

    2. 展開 [功能] 索引標籤。

    3. 以滑鼠右鍵按下 私人消息佇列,然後選取 新增私人佇列

    4. 勾選 [交易式] 方塊。

    5. 輸入 ServiceModelSamplesTransacted 作為新佇列的名稱。

  3. 若要建置解決方案的 C# 或 Visual Basic .NET 版本,請遵循建置 Windows Communication Foundation 範例 中的指示。

  4. 若要在單一計算機設定中執行範例,請遵循 執行 Windows Communication Foundation 範例中的指示。

在不同的電腦上運行範例

  1. 將服務程式檔案從語言專用資料夾中的 \service\bin\ 資料夾,複製到服務電腦。

  2. 將用戶端程式檔案從語言特定資料夾下的 \client\bin\ 資料夾,複製到用戶端電腦。

  3. 在 Client.exe.config 檔案中,變更 orderQueueName 以指定服務計算機名稱,而不是 “.”。

  4. 在 Service.exe.config 檔案中,變更用戶端端點位址以指定用戶端計算機名稱,而不是 “.”。

  5. 在伺服器上,從命令列啟動 Service.exe。

  6. 在用戶端電腦上,從命令提示符啟動 "Client.exe"。

另請參閱