Delen via


Berichtcorrelatie

Het MessageCorrelation-voorbeeld laat zien hoe een Message Queuing-toepassing (MSMQ) een MSMQ-bericht kan verzenden naar een WCF-service (Windows Communication Foundation) en hoe berichten kunnen worden gecorreleerd tussen afzender- en ontvangertoepassingen in een aanvraag-/antwoordscenario. In dit voorbeeld wordt de binding msmqIntegrationBinding gebruikt. De service is in dit geval een zelf-hostende consoletoepassing waarmee u de service kunt observeren die berichten in de wachtrij ontvangt. k

De service verwerkt het bericht dat van de afzender is ontvangen en stuurt een antwoordbericht terug naar de afzender. De afzender correleert het antwoord dat deze heeft ontvangen voor de aanvraag die oorspronkelijk is verzonden. De MessageID en CorrelationID eigenschappen van het bericht worden gebruikt om de aanvraag- en antwoordberichten te correleren.

Het IOrderProcessor servicecontract definieert een eenrichtingsservicebewerking die geschikt is voor gebruik met wachtrijen. Een MSMQ-bericht heeft geen actieheader, dus het is niet mogelijk om verschillende MSMQ-berichten automatisch toe te wijzen aan bewerkingscontracten. Daarom kan er in dit geval slechts één operationeel contract zijn. Als u meer bewerkingscontracten in de service wilt definiëren, moet de toepassing informatie opgeven over welke header in het MSMQ-bericht (bijvoorbeeld het label of correlationID) kan worden gebruikt om te bepalen welk bewerkingscontract moet worden verzonden.

Het MSMQ-bericht bevat ook geen informatie over welke headers worden toegewezen aan de verschillende parameters van het bewerkingscontract. Daarom kan er slechts één parameter in het bewerkingscontract zijn. De parameter is van het type MsmqMessage<T>, dat het onderliggende MSMQ-bericht bevat. Het type 'T' in de MsmqMessage<T> klasse vertegenwoordigt de gegevens die in de hoofdtekst van het MSMQ-bericht worden geserialiseerd. In dit voorbeeld wordt het PurchaseOrder type geserialiseerd in de hoofdtekst van het MSMQ-bericht.

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

De servicebewerking verwerkt de inkooporder en geeft de inhoud van de inkooporder en de status ervan weer in het venster van de serviceconsole. Hiermee OperationBehaviorAttribute configureert u de bewerking om in te schakelen in een transactie met de wachtrij en om de transactie als voltooid te markeren wanneer de bewerking wordt geretourneerd. De PurchaseOrder bevat de ordergegevens die door de service moeten worden verwerkt.

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

De service maakt gebruik van een aangepaste client OrderResponseClient om het MSMQ-bericht naar de wachtrij te verzenden. Omdat de toepassing die het bericht ontvangt en verwerkt, een MSMQ-toepassing is en geen WCF-toepassing, is er geen impliciet servicecontract tussen de twee toepassingen. We kunnen dus geen proxy maken met behulp van het hulpprogramma Svcutil.exe in dit scenario.

De aangepaste proxy is in wezen hetzelfde voor alle WCF-toepassingen die gebruikmaken van de binding voor het msmqIntegrationBinding verzenden van berichten. In tegenstelling tot andere proxy's bevat het geen reeks servicebewerkingen. Het is alleen een verzendberichtbewerking.

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

De service wordt zelf gehost. Wanneer u het MSMQ-integratietransport gebruikt, moet de gebruikte wachtrij vooraf worden gemaakt. Dit kan handmatig of via code. In dit voorbeeld bevat System.Messaging de service code om te controleren op het bestaan van de wachtrij en deze zo nodig te maken. De naam van de wachtrij wordt gelezen uit het configuratiebestand.

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

De MSMQ-wachtrij waarnaar de orderaanvragen worden verzonden, wordt opgegeven in de app Instellingen sectie van het configuratiebestand. De client- en service-eindpunten worden gedefinieerd in de sectie system.serviceModel van het configuratiebestand. Beide geven de msmqIntegrationBinding binding op.

<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>

De clienttoepassing gebruikt System.Messaging om een duurzaam en transactioneel bericht naar de wachtrij te verzenden. De hoofdtekst van het bericht bevat de inkooporder.

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

De MSMQ-wachtrij waaruit de orderantwoorden worden ontvangen, wordt opgegeven in een app Instellingen sectie van het configuratiebestand, zoals wordt weergegeven in de volgende voorbeeldconfiguratie.

Notitie

De wachtrijnaam maakt gebruik van een punt (.) voor de lokale computer en backslash-scheidingstekens in het pad. Het WCF-eindpuntadres geeft een msmq.formatname-schema op en gebruikt 'localhost' voor de lokale computer. Een correct gevormde indelingsnaam volgt msmq.formatname in de URI volgens de MSMQ-richtlijnen.

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

De clienttoepassing slaat het messageID bericht van de orderaanvraag op dat naar de service wordt verzonden en wacht op een reactie van de service. Zodra een antwoord in de wachtrij binnenkomt, correleert de client het met het orderbericht dat het heeft verzonden met behulp van de correlationID eigenschap van het bericht, dat het messageID orderbericht bevat dat de client oorspronkelijk naar de service heeft verzonden.

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

Wanneer u het voorbeeld uitvoert, worden de client- en serviceactiviteiten weergegeven in zowel de service- als clientconsolevensters. U kunt de service berichten van de client ontvangen en een antwoord terugsturen naar de client. De client geeft het antwoord weer dat van de service is ontvangen. Druk in elk consolevenster op Enter om de service en client af te sluiten.

Notitie

Voor dit voorbeeld is de installatie van Message Queuing (MSMQ) vereist. Zie de MSMQ-installatie-instructies in de sectie Zie ook.

Het voorbeeld instellen, bouwen en uitvoeren

  1. Zorg ervoor dat u de eenmalige installatieprocedure voor de Windows Communication Foundation-voorbeelden hebt uitgevoerd.

  2. Als de service eerst wordt uitgevoerd, wordt gecontroleerd of de wachtrij aanwezig is. Als de wachtrij niet aanwezig is, maakt de service er een. U kunt de service eerst uitvoeren om de wachtrij te maken of u kunt er een maken via MSMQ Queue Manager. Volg deze stappen om een wachtrij te maken in Windows 2008.

    1. Open Serverbeheer in Visual Studio 2012.

    2. Vouw het tabblad Functies uit.

    3. Klik met de rechtermuisknop op Persoonlijke berichtenwachtrijen en selecteer Nieuwe, Privéwachtrij.

    4. Schakel het selectievakje Transactioneel in.

    5. Voer de ServiceModelSamplesTransacted naam in van de nieuwe wachtrij.

  3. Als u de C# of Visual Basic .NET-editie van de oplossing wilt bouwen, volgt u de instructies in het bouwen van de Windows Communication Foundation-voorbeelden.

  4. Als u het voorbeeld wilt uitvoeren in een configuratie met één computer, volgt u de instructies in Het uitvoeren van de Windows Communication Foundation-voorbeelden.

Het voorbeeld uitvoeren op computers

  1. Kopieer de serviceprogrammabestanden uit de map \service\bin\ onder de taalspecifieke map naar de servicecomputer.

  2. Kopieer de clientprogrammabestanden uit de map \client\bin\ onder de taalspecifieke map naar de clientcomputer.

  3. Wijzig in het bestand Client.exe.config de orderQueueName om de naam van de servicecomputer op te geven in plaats van '.'.

  4. Wijzig in het bestand Service.exe.config het adres van het clienteindpunt om de naam van de clientcomputer op te geven in plaats van '.'.

  5. Start Service.exe vanaf een opdrachtprompt op de servicecomputer.

  6. Start Client.exe vanaf een opdrachtprompt op de clientcomputer.

Zie ook