Partager via


Liaison MSMQ transactionnelle

L’exemple Transacted montre comment effectuer une communication en file d’attente transactionnelle à l’aide de Message Queuing (MSMQ).

Remarque

La procédure d’installation et les instructions de génération de cet exemple se trouvent à la fin de cette rubrique.

Dans la communication en file d'attente, le client communique avec le service à l’aide d’une file d’attente. Plus précisément, le client envoie des messages à une file d’attente. Le service reçoit des messages de la file d’attente. Par conséquent, le service et le client n'ont pas besoin de fonctionner simultanément pour communiquer via une file de messages.

Lorsque les transactions sont utilisées pour envoyer et recevoir des messages, il existe en fait deux transactions distinctes. Lorsque le client envoie des messages dans l’étendue d’une transaction, la transaction est locale au client et au gestionnaire de file d’attente du client. Lorsque le service reçoit des messages dans l’étendue de la transaction, la transaction est locale au service et au gestionnaire de file d’attente de réception. Il est très important de se rappeler que le client et le service ne participent pas à la même transaction ; ils utilisent plutôt des transactions différentes lors de l’exécution de leurs opérations (telles que l’envoi et la réception) avec la file d’attente.

Dans cet exemple, le client transmet un ensemble de messages au service dans le cadre d'une transaction. Les messages envoyés à la file d’attente sont ensuite reçus par le service dans l’étendue de transaction définie par le service.

Le contrat de service est IOrderProcessor, comme indiqué dans l’exemple de code suivant. L’interface définit un service unidirectionnel qui convient pour une utilisation avec des files d’attente.

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

Le comportement du service définit un comportement d’opération avec TransactionScopeRequired défini à true. Cela garantit que la même étendue de transaction utilisée pour récupérer le message à partir de la file d’attente est utilisée par tous les gestionnaires de ressources accessibles par la méthode. Elle garantit également que si la méthode lève une exception, le message est retourné à la file d’attente. Sans définir ce comportement d’opération, un canal mis en file d’attente crée une transaction pour lire le message à partir de la file d’attente et le valide automatiquement avant la distribution, de sorte que si l’opération échoue, le message est perdu. Le scénario le plus courant pour les opérations de service est de s'enrôler dans la transaction qui est utilisée pour lire le message à partir de la file d’attente, comme illustré dans le code suivant.

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

Le service est auto-hébergé. Lorsque vous utilisez le transport MSMQ, la file d’attente utilisée doit être créée à l’avance. Cette opération peut être effectuée manuellement ou via du code. Dans cet exemple, le service contient du code pour vérifier l’existence de la file d’attente et créer la file d’attente s’il n’existe pas. Le nom de la file d’attente est lu à partir du fichier de configuration. L’adresse de base est utilisée par l’outil utilitaire de métadonnées ServiceModel (Svcutil.exe) pour générer le proxy vers le service.

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

Le nom de la file d’attente MSMQ est spécifié dans une section appSettings du fichier de configuration, comme indiqué dans l’exemple de configuration suivant.

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

Remarque

Le nom de la file d’attente comporte un point (.) pour l’ordinateur local et des barres obliques inverses comme séparateur dans son chemin d’accès lors de la création de la file d’attente à l’aide de System.Messaging. Le point de terminaison Windows Communication Foundation (WCF) utilise l’adresse de la file d’attente avec le modèle net.msmq, « localhost » pour désigner l’ordinateur local et des barres obliques dans son chemin d’accès.

Le client crée un périmètre de transaction. La communication avec la file d’attente a lieu dans l’étendue de la transaction, entraînant son traitement en tant qu’une unité atomique dans laquelle tous les messages sont envoyés à la file d’attente ou aucun des messages n’est envoyé à la file d’attente. La transaction est validée par l'appel de la méthode Complete sur l'étendue de la transaction.

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

Pour vérifier que les transactions fonctionnent, modifiez le client en commentant l’étendue de la transaction, comme indiqué dans l’exemple de code suivant, régénérez la solution et exécutez le client.

//scope.Complete();

Étant donné que la transaction n’est pas terminée, les messages ne sont pas envoyés à la file d’attente.

Lorsque vous exécutez l’exemple, les activités du client et du service s’affichent dans les fenêtres de la console du service et du client. Vous pouvez voir le service recevoir des messages du client. Appuyez sur Entrée dans chaque fenêtre de console pour arrêter le service et le client. Notez que, étant donné que la mise en file d’attente est en cours d’utilisation, le client et le service n’ont pas besoin d’être opérationnel en même temps. Vous pouvez exécuter le client, l’arrêter, puis démarrer le service et recevoir les messages.

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

Pour configurer, générer et exécuter l’exemple

  1. Assurez-vous d’avoir effectué la Procédure d’installation unique pour les exemples Windows Communication Foundation.

  2. Si le service est exécuté en premier, il vérifie que la file d’attente est présente. Si la file d’attente n’est pas présente, le service en crée un. Vous pouvez d’abord exécuter le service pour créer la file d’attente, ou en créer un via le Gestionnaire de files d’attente MSMQ. Suivez ces étapes pour créer une file d’attente dans Windows 2008.

    1. Ouvrez le Gestionnaire de serveur dans Visual Studio 2012.

    2. Développez l’onglet Fonctionnalités.

    3. Cliquez avec le bouton droit sur file d’attente de messages privés, puis sélectionnez Nouveau, file d’attente privée.

    4. Activez la case à cocher Transactionnelle.

    5. Entrez ServiceModelSamplesTransacted comme nom de la nouvelle file d’attente.

  3. Pour générer l’édition C# ou Visual Basic .NET de la solution, conformez-vous aux instructions figurant dans Building the Windows Communication Foundation Samples.

  4. Pour exécuter l’exemple dans une configuration monoposte ou multiposte, suivez les instructions de Exécution des exemples Windows Communication Foundation.

Par défaut, la sécurité de transport est activée avec le NetMsmqBinding. Il existe deux propriétés pertinentes pour la sécurité du transport MSMQ : MsmqAuthenticationMode et MsmqProtectionLevel. Par défaut, le mode d’authentification est défini Windows sur et le niveau de protection est défini sur Sign. Pour que MSMQ fournisse la fonctionnalité d’authentification et de signature, elle doit faire partie d’un domaine et l’option d’intégration Active Directory pour MSMQ doit être installée. Si vous exécutez cet exemple sur un ordinateur qui ne répond pas à ces critères, vous recevez une erreur.

Pour exécuter l’exemple sur un ordinateur joint à un groupe de travail ou sans intégration Active Directory

  1. Si votre ordinateur ne fait pas partie d’un domaine ou si l’intégration d’Active Directory n’est pas installée, désactivez la sécurité du transport en définissant le mode d’authentification et le niveau None de protection comme indiqué dans l’exemple de code de configuration suivant.

    <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. Veillez à modifier la configuration sur le serveur et le client avant d’exécuter l’exemple.

    Remarque

    Régler security mode sur None équivaut à régler MsmqAuthenticationMode, MsmqProtectionLevel et Message sur le niveau de sécurité None.