Condividi tramite


Correlazione dei messaggi

L'esempio MessageCorrelation illustra come un'applicazione di Accodamento messaggi (MSMQ) può inviare un messaggio MSMQ a un servizio Windows Communication Foundation (WCF) e come i messaggi possono essere correlati tra le applicazioni mittente e ricevente in uno scenario di richiesta/risposta. Questo esempio utilizza il collegamento msmqIntegrationBinding. Il servizio in questo caso è un'applicazione console autogestita che consente di monitorare il servizio che riceve i messaggi in coda. Okay

Il servizio elabora il messaggio ricevuto dal mittente e invia un messaggio di risposta al mittente. Il mittente correla la risposta ricevuta alla richiesta inviata originariamente. Le MessageID proprietà e CorrelationID del messaggio vengono usate per correlare i messaggi di richiesta e risposta.

Il IOrderProcessor contratto di servizio definisce un'operazione di servizio unidirezionale adatta per l'uso con accodamento. Un messaggio MSMQ non dispone di un'intestazione Action, pertanto non è possibile eseguire automaticamente il mapping di messaggi MSMQ diversi ai contratti operativi. Pertanto, in questo caso può essere presente un solo contratto di operazione. Se si desidera nel servizio definire più contratti di operazione, l'applicazione deve fornire informazioni su quale intestazione nel messaggio MSMQ (ad esempio, l'etichetta o il correlationID) può essere usata per scegliere quale contratto di operazione dev'essere eseguito.

Il messaggio MSMQ inoltre non contiene informazioni su quali intestazioni sono associate ai diversi parametri del contratto dell'operazione. Pertanto, può essere presente un solo parametro nel contratto dell'operazione. Il parametro è di tipo MsmqMessage<T>, che contiene il messaggio MSMQ sottostante. Il tipo "T" nella MsmqMessage<T> classe rappresenta i dati serializzati nel corpo del messaggio MSMQ. In questo esempio, il PurchaseOrder tipo viene serializzato nel corpo del messaggio MSMQ.

[ServiceContract(Namespace = "http://Microsoft.ServiceModel.Samples")]
[ServiceKnownType(typeof(PurchaseOrder))]
public interface IOrderProcessor
{
    [OperationContract(IsOneWay = true, Action = "*")]
    void SubmitPurchaseOrder(MsmqMessage<PurchaseOrder> msg);
}

L'operazione del servizio elabora l'ordine di acquisto e visualizza il contenuto dell'ordine di acquisto e il relativo stato nella finestra della console del servizio. OperationBehaviorAttribute configura l'operazione per integrarsi in una transazione con la coda e contrassegnare il completamento della transazione al ritorno dell'operazione. PurchaseOrder Contiene i dettagli dell'ordine che devono essere elaborati dal servizio.

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

Il servizio usa un client OrderResponseClient personalizzato per inviare il messaggio MSMQ alla coda. Poiché l'applicazione che riceve ed elabora il messaggio è un'applicazione MSMQ e non un'applicazione WCF, non esiste alcun contratto di servizio implicito tra le due applicazioni. Non è quindi possibile creare un proxy usando lo strumento Svcutil.exe in questo scenario.

Il proxy personalizzato è essenzialmente lo stesso per tutte le applicazioni WCF che usano l'associazione msmqIntegrationBinding per inviare messaggi. A differenza di altri proxy, non include una gamma di operazioni del servizio. Si tratta solo di un'operazione di invio del messaggio.

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

Il servizio è autogestito. Quando si utilizza il trasporto di integrazione MSMQ, occorre creare la coda in anticipo. Questa operazione può essere eseguita manualmente o tramite il codice. In questo esempio il servizio contiene System.Messaging codice per verificare l'esistenza della coda e crearla, se necessario. Il nome della coda viene letto dal file di configurazione.

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

La coda MSMQ a cui vengono inviate le richieste di ordine viene specificata nella sezione appSettings del file di configurazione. Gli endpoint client e di servizio sono definiti nella sezione system.serviceModel del file di configurazione. Entrambi gli elementi specificano l'associazione 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>

L'applicazione client usa System.Messaging per inviare un messaggio durevole e transazionale alla coda. Il corpo del messaggio contiene l'ordine di acquisto.

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

La coda MSMQ da cui vengono ricevute le risposte dell'ordine viene specificata in una sezione appSettings del file di configurazione, come illustrato nella configurazione di esempio seguente.

Annotazioni

Il nome della coda usa un punto (.) per il computer locale e i separatori di barra rovesciata nel percorso. L'indirizzo dell'endpoint WCF specifica uno schema msmq.formatname e usa "localhost" per il computer locale. Un nome di formato corretto segue msmq.formatname nell'URI in base alle linee guida MSMQ.

<appSettings>
    <add key=" orderResponseQueueName" value=".\private$\Orders" />
</appSettings>

L'applicazione client salva l'oggetto messageID del messaggio di richiesta dell'ordine inviato al servizio e attende una risposta dal servizio. Una volta ricevuta una risposta nella coda, il client lo correla al messaggio di ordine inviato usando la correlationID proprietà del messaggio, che contiene il messageID del messaggio di ordine che il client ha inviato al servizio in origine.

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

Quando si esegue l'esempio, le attività client e di servizio vengono visualizzate sia nelle finestre del servizio che della console client. È possibile osservare il servizio che riceve messaggi dal client e invia una risposta al client. Il client visualizza la risposta ricevuta dal servizio. Premere INVIO in ogni finestra della console per spegnere il servizio e il client.

Annotazioni

Questo esempio richiede l'installazione dell'Accodamento messaggi (MSMQ). Vedere le istruzioni di installazione di MSMQ nella sezione Vedere anche.

Configurare, compilare ed eseguire l'esempio

  1. Assicurati di aver eseguito la procedura di installazione di One-Time per gli esempi di Windows Communication Foundation.

  2. Se il servizio viene eseguito per primo, verifica che la coda sia presente. Se la coda non è presente, il servizio ne creerà uno. È possibile eseguire prima il servizio per creare la coda oppure crearne una tramite Gestione code MSMQ. Seguire questa procedura per creare una coda in Windows 2008.

    1. Aprire Server Manager in Visual Studio 2012.

    2. Espandi la scheda funzionalità.

    3. Fare clic con il pulsante destro del mouse code di messaggi privati e selezionare Nuovo, coda privata.

    4. Selezionare la casella transazionale.

    5. Inserire ServiceModelSamplesTransacted come nome della nuova coda.

  3. Per compilare l'edizione C# o Visual Basic .NET della soluzione, seguire le istruzioni in Compilazione degli esempi di Windows Communication Foundation.

  4. Per eseguire l'esempio in una configurazione a computer singolo, seguire le istruzioni riportate in Esecuzione degli esempi di Windows Communication Foundation.

Eseguire il campione su diversi computer

  1. Copiare i file di programma del servizio dalla cartella \service\bin\ nella cartella specifica della lingua nel computer del servizio.

  2. Copiare i file di programma client dalla cartella \client\bin\ nella cartella specifica della lingua nel computer client.

  3. Nel file Client.exe.config modificare orderQueueName per specificare il nome del computer del servizio anziché ".".

  4. Nel file Service.exe.config modificare l'indirizzo dell'endpoint client per specificare il nome del computer client anziché ".".

  5. Sul computer di servizio, avvia Service.exe da un prompt dei comandi.

  6. Sul computer client, esegui Client.exe da un prompt dei comandi.

Vedere anche