Condividi tramite


Associazione MSMQ transazionata

L'esempio Transacted illustra come eseguire comunicazioni transazionali in coda mediante il servizio di accodamento dei messaggi (MSMQ).

Annotazioni

La procedura di installazione e le istruzioni di compilazione per questo esempio si trovano alla fine di questo argomento.

Nella comunicazione in coda, il client comunica con il servizio usando una coda. Più precisamente, il client invia messaggi a una coda. Il servizio riceve messaggi dalla coda. Il servizio e il client, pertanto, non devono essere in esecuzione contemporaneamente per comunicare tramite una coda.

Quando le transazioni vengono usate per inviare e ricevere messaggi, esistono effettivamente due transazioni separate. Quando il client invia messaggi nell'ambito di una transazione, la transazione è locale al client e al gestore della coda del client. Quando il servizio riceve messaggi all'interno dell'ambito della transazione, la transazione è locale per il servizio e il gestore code ricevente. È molto importante ricordare che il client e il servizio non partecipano alla stessa transazione; usano invece transazioni diverse durante l'esecuzione delle operazioni, ad esempio l'invio e la ricezione, con la coda.

In questo esempio, il client invia un batch di messaggi al servizio dall'interno dell'ambito di una transazione. I messaggi inviati alla coda vengono quindi ricevuti dal servizio nell'ambito di transazione definito dal servizio.

Il contratto di servizio è IOrderProcessor, come illustrato nel codice di esempio seguente. L'interfaccia definisce un servizio unidirezionale adatto per l'uso con le queue.

[ServiceContract(Namespace="http://Microsoft.ServiceModel.Samples")]
public interface IOrderProcessor
{
    [OperationContract(IsOneWay = true)]
    void SubmitPurchaseOrder(PurchaseOrder po);
}

Il comportamento del servizio definisce un comportamento dell'operazione con TransactionScopeRequired impostato su true. In questo modo si garantisce che lo stesso ambito di transazione usato per recuperare il messaggio dalla coda venga usato da qualsiasi gestore di risorse a cui accede il metodo . Garantisce inoltre che se il metodo genera un'eccezione, il messaggio viene restituito alla coda. Senza impostare questo comportamento dell'operazione, un canale in coda crea una transazione per leggere il messaggio dalla coda e lo esegue automaticamente prima dell'invio in modo che, in caso di errore dell'operazione, il messaggio andrà perso. Lo scenario più comune consiste nell'integrare le operazioni del servizio nella transazione usata per leggere il messaggio dalla coda, come illustrato nel codice seguente.

 // This service class that implements the service contract.
 // This added code writes output to the console window.
public class OrderProcessorService : IOrderProcessor
 {
     [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)]
     public void SubmitPurchaseOrder(PurchaseOrder po)
     {
         Orders.Add(po);
         Console.WriteLine("Processing {0} ", po);
     }
  …
}

Il servizio è autogestito. Quando si usa il trasporto MSMQ, la coda usata deve essere creata in anticipo. Questa operazione può essere eseguita manualmente o tramite il codice. In questo esempio il servizio contiene codice per verificare l'esistenza della coda e creare la coda, se non esiste. Il nome della coda viene letto dal file di configurazione. L'indirizzo di base viene usato dallo strumento utilità metadati ServiceModel (Svcutil.exe) per generare il proxy al servizio.

// Host the service within this EXE console application.
public static void Main()
{
    // Get the MSMQ queue name from appSettings in configuration.
    string queueName = ConfigurationManager.AppSettings["queueName"];

    // 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)))
    {
        // Open the ServiceHost to create listeners and start listening for messages.
        serviceHost.Open();

        // The service can now be accessed.
        Console.WriteLine("The service is ready.");
        Console.WriteLine("Press <ENTER> to terminate service.");
        Console.WriteLine();
        Console.ReadLine();

        // Close the ServiceHost to shut down the service.
        serviceHost.Close();
    }
}

Il nome della coda MSMQ viene specificato in una sezione appSettings del file di configurazione, come illustrato nella configurazione di esempio seguente.

<appSettings>
    <add key="queueName" value=".\private$\ServiceModelSamplesTransacted" />
</appSettings>

Annotazioni

Il nome della coda utilizza un punto (.) per il computer locale e le barre inverse nei percorsi quando si crea la coda tramite System.Messaging. L'endpoint di Windows Communication Foundation (WCF) utilizza l'indirizzo in coda con lo schema net.msmq, utilizza "localhost" per indicare il computer locale e utilizza barre oblique nel suo percorso.

Il client crea un ambito di transazione. La comunicazione con la coda avviene all'interno dell'ambito della transazione, causandone la gestione come un'unità atomica in cui tutti i messaggi vengono inviati alla coda o nessuno dei messaggi viene inviato alla coda. La transazione viene confermata chiamando Complete sull'ambito della transazione.

// Create a client.
OrderProcessorClient client = new OrderProcessorClient();

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

// Create a transaction scope.
using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required))
{
    // Make a queued call to submit the purchase order.
    client.SubmitPurchaseOrder(po);
    // Complete the transaction.
    scope.Complete();
}

// Closing the client gracefully closes the connection and cleans up resources.
client.Close();

Console.WriteLine();
Console.WriteLine("Press <ENTER> to terminate client.");
Console.ReadLine();

Per verificare che le transazioni funzionino, modificare il client commentando l'ambito della transazione come illustrato nel codice di esempio seguente, ricompilare la soluzione ed eseguire il client.

//scope.Complete();

Poiché la transazione non è stata completata, i messaggi non vengono inviati alla coda.

Quando si esegue l'esempio, le attività client e di servizio vengono visualizzate sia nelle finestre del servizio che della console client. È possibile visualizzare il servizio che riceve messaggi dal client. Premere INVIO in ogni finestra della console per spegnere il servizio e il client. Si noti che poiché l'accodamento è in uso, il client e il servizio non devono essere operativi contemporaneamente. È possibile eseguire il client, arrestarlo, quindi avviare il servizio, e il client continuerà a ricevere messaggi.

The service is ready.
Press <ENTER> to terminate service.

Processing Purchase Order: 7b31ce51-ae7c-4def-9b8b-617e4288eafd
        Customer: somecustomer.com
        OrderDetails
                Order LineItem: 54 of Blue Widget @unit price: $29.99
                Order LineItem: 890 of Red Widget @unit price: $45.89
        Total cost of this order: $42461.56
        Order status: Pending

Per 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 un solo computer o tra computer diversi, seguire le istruzioni in Esecuzione degli esempi di Windows Communication Foundation.

Per impostazione predefinita con il NetMsmqBinding, la sicurezza del trasporto è abilitata. Esistono due proprietà rilevanti per la sicurezza del trasporto MSMQ, MsmqAuthenticationMode e MsmqProtectionLevel. Per impostazione predefinita, la modalità di autenticazione è impostata su Windows e il livello di protezione è impostato su Sign. Affinché MSMQ fornisca la funzionalità di autenticazione e firma, deve far parte di un dominio e deve essere installata l'opzione di integrazione di Active Directory per MSMQ. Se si esegue questo esempio in un computer che non soddisfa questi criteri, viene visualizzato un errore.

Per eseguire l'esempio in un computer aggiunto a un gruppo di lavoro o senza integrazione di Active Directory

  1. Se il computer non fa parte di un dominio o non ha installato l'integrazione di Active Directory, disattivare la sicurezza del trasporto impostando la modalità di autenticazione e il livello di protezione su None come illustrato nel codice di configurazione di esempio seguente.

    <system.serviceModel>
      <services>
        <service name="Microsoft.ServiceModel.Samples.OrderProcessorService"
                 behaviorConfiguration="OrderProcessorServiceBehavior">
          <host>
            <baseAddresses>
              <add baseAddress="http://localhost:8000/ServiceModelSamples/service"/>
            </baseAddresses>
          </host>
          <!-- Define NetMsmqEndpoint. -->
          <endpoint
              address="net.msmq://localhost/private/ServiceModelSamplesTransacted"
              binding="netMsmqBinding"
              bindingConfiguration="Binding1"
           contract="Microsoft.ServiceModel.Samples.IOrderProcessor" />
          <!-- The mex endpoint is exposed at http://localhost:8000/ServiceModelSamples/service/mex. -->
          <endpoint address="mex"
                    binding="mexHttpBinding"
                    contract="IMetadataExchange" />
        </service>
      </services>
    
      <bindings>
        <netMsmqBinding>
          <binding name="Binding1">
            <security mode="None" />
          </binding>
        </netMsmqBinding>
      </bindings>
    
        <behaviors>
          <serviceBehaviors>
            <behavior name="OrderProcessorServiceBehavior">
              <serviceMetadata httpGetEnabled="True"/>
            </behavior>
          </serviceBehaviors>
        </behaviors>
    
      </system.serviceModel>
    
  2. Assicurarsi di modificare la configurazione sia nel server che nel client prima di eseguire l'esempio.

    Annotazioni

    Impostare security mode su None equivale a impostare la sicurezza di MsmqAuthenticationMode, MsmqProtectionLevel e Message su None.