Gruppieren von Nachrichten in der Warteschlange einer SitzungGrouping Queued Messages in a Session

Windows Communication Foundation (WCF)Windows Communication Foundation (WCF) stellt eine Sitzung bereit, mit der verwandte Nachrichten gruppiert und von einer empfangenden Anwendung verarbeitet werden können. provides a session that allows you to group a set of related messages together for processing by a single receiving application. Die Nachrichten in einer Sitzung müssen Teil der gleichen Transaktion sein.Messages that are part of a session must be part of the same transaction. Da alle Nachrichten Teil der gleichen Transaktion sind, wird die gesamte Sitzung zurückgesetzt, wenn eine Nachricht nicht verarbeitet werden kann.Because all messages are part of the same transaction, if one message fails to be processed the entire session is rolled back. Sitzungen weisen ähnliche Verhaltensweisen bezüglich Warteschlangen für unzustellbare Nachrichten und Warteschlangen für potenziell schädliche Nachrichten auf.Sessions have similar behaviors with regard to dead-letter queues and poison queues. Die Time to Live (TTL)-Eigenschaft einer Bindung in der Warteschlange, die für Sitzungen konfiguriert wurde, wird auf die gesamte Sitzung angewendet.The Time to Live (TTL) property set on a queued binding configured for sessions is applied to the session as a whole. Wenn nur ein Teil der Nachrichten in der Sitzung vor Ablauf der TTL gesendet wird, wird die gesamte Sitzung in der Warteschlange für unzustellbare Nachrichten abgelegt.If only some of the messages in the session are sent before the TTL expires, the entire session is placed in the dead-letter queue. Analog wird ggf. die gesamte Sitzung in der Warteschlange für potenziell schädliche Nachrichten abgelegt, wenn Nachrichten in einer Sitzung nicht von der Anwendungswarteschlange an eine Anwendung gesendet werden können.Similarly, when messages in a session fail to be sent to an application from the application queue, the entire session is placed in the poison queue (if available).

Beispiel für das Gruppieren von NachrichtenMessage Grouping Example

Das Gruppieren von Nachrichten kann beispielsweise hilfreich sein, wenn Sie eine Anwendung für die Auftragsverarbeitung als WCFWCF-Dienst implementieren.One example where grouping messages is helpful is when implementing an order-processing application as a WCFWCF service. Angenommen, ein Client sendet einen Auftrag an diese Anwendung, die eine Anzahl von Elementen enthält.For instance, a client submits an order to this application that contains a number of items. Der Client ruft für jedes Element den Dienst auf. Dadurch wird jeweils eine separate Nachricht gesendet.For each item, the client makes a call to the service, which results in a separate message being sent. Es kann sein, dass Server A das erste Element empfängt und Server B das zweite.It is possible for serve A to receive the first item, and server B to receive the second item. Immer, wenn ein Element hinzugefügt wird, muss der Server, der das Element verarbeitet, die entsprechende Reihenfolge ermitteln, und das Element muss hinzugefügt werden. Dieser Vorgang ist nicht sehr effizient.Each time an item is added, the server processing that item has to find the appropriate order and add the item to it, which is highly inefficient. Dieses Problem tritt auch dann auf, wenn alle Anfragen nur von einem Server behandelt werden, da der Server alle Aufträge überwachen muss, die derzeit verarbeitet werden, und das neue Element einem Auftrag zugeordnet werden muss.You still run into such inefficiencies with only a single server handling all requests, because the server must keep track of all orders currently being processed and determine which one the new item belongs to. Durch das Gruppieren aller Anforderungen für einen Auftrag wird die Implementierung einer solchen Anwendung stark vereinfacht.Grouping all requests for a single order greatly simplifies implementation of such an application. Alle Elemente für einen Auftrag werden von der Clientanwendung in einer Sitzung gesendet, sodass der Auftrag vom Dienst in einer Sitzung verarbeitet werden kann.The client application sends all items for a single order in a session, so when the service processes the order, it processes the entire session at once. \

VerfahrenProcedures

So richten Sie einen Dienstvertrag für Sitzungen einTo set up a service contract to use sessions

  1. Definieren Sie einen Dienstvertrag, der eine Sitzung erfordert.Define a service contract that requires a session. Verwenden Sie dazu das OperationContractAttribute-Attribut, und geben Sie Folgendes an:Do this with the OperationContractAttribute attribute and by specifying:

    SessionMode=SessionMode.Required  
    
  2. Markieren Sie die Vorgänge im Vertrag als unidirektional, da von diesen Methoden nichts zurückgegeben wird.Mark the operations in the contract as one-way, because these methods do not return anything. Verwenden Sie dazu das OperationContractAttribute-Attribut, und geben Sie Folgendes an:This is done with the OperationContractAttribute attribute and by specifying:

    [OperationContract(IsOneWay = true)]  
    
  3. Implementieren Sie den Dienstvertrag, und geben Sie einen InstanceContextMode von PerSession an.Implement the service contract and specify an InstanceContextMode of PerSession. Dadurch wird der Dienst für jede Sitzung nur einmal instanziiert.This instantiates the service only once for each session.

    [ServiceBehavior(InstanceContextMode=InstanceContextMode.PerSession)]  
    
  4. Jeder Dienstvorgang erfordert eine Transaktion.Each service operation requires a transaction. Verwenden Sie das OperationBehaviorAttribute-Attribut für die Angabe.Specify this with the OperationBehaviorAttribute attribute. Der Vorgang, mit dem die Transaktion abgeschlossen wird, sollte auch TransactionAutoComplete auf true festlegen.The operation that completes the transaction should also set TransactionAutoComplete to true.

    [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)]   
    
  5. Konfigurieren Sie einen Endpunkt, der die vom System bereitgestellte NetMsmqBinding-Bindung verwendet.Configure an endpoint that uses the system-provided NetMsmqBinding binding.

  6. Erstellen Sie eine Transaktionswarteschlange mit System.Messaging.Create a transactional queue using System.Messaging. Sie können die Warteschlange auch mit Message Queuing (MSMQ) oder MMC erstellen.You can also create the queue by using Message Queuing (MSMQ) or MMC. Erstellen Sie in diesem Fall eine Transaktionswarteschlange.If you do, create a transactional queue.

  7. Erstellen Sie mit ServiceHost einen Host für den Dienst.Create a service host for the service by using ServiceHost.

  8. Öffnen Sie den Diensthost, um den Dienst verfügbar zu machen.Open the service host to make the service available.

  9. Schließen Sie den Diensthost.Close the service host.

So richten Sie einen Client einTo set up a client

  1. Erstellen Sie einen Transaktionsbereich zum Schreiben in die Transaktionswarteschlange.Create a transaction scope to write to the transactional queue.

  2. Erstellen der WCFWCF Client mithilfe der ServiceModel Metadata Utility Tool (Svcutil.exe) Tool.Create the WCFWCF client using the ServiceModel Metadata Utility Tool (Svcutil.exe) tool.

  3. Platzieren Sie den Auftrag.Place the order.

  4. Schließen Sie den WCFWCF-Client.Close the WCFWCF client.

BeispielExample

BeschreibungDescription

Im folgenden Beispiel wird der Code für den IProcessOrder-Dienst sowie für einen Client bereitgestellt, der diesen Dienst verwendet.The following example provides the code for the IProcessOrder service and for a client that uses this service. Es wird veranschaulicht, wie WCFWCF die Gruppierungsfunktion mit Warteschlangensitzungen bereitstellt.It shows how WCFWCF uses queued sessions to provide the grouping behavior.

Code für den DienstCode for the Service

// Service Code:

using System;

using System.ServiceModel.Channels;
using System.Configuration;
using System.Messaging;
using System.ServiceModel;
using System.Transactions;
using System.Text;
using System.Collections.Generic;

namespace Microsoft.ServiceModel.Samples
{
    // Define a service contract. 
    [ServiceContract(Namespace = "http://Microsoft.ServiceModel.Samples", SessionMode=SessionMode.Required)]
    public interface IOrderTaker
    {
        [OperationContract(IsOneWay = true)]
        void OpenPurchaseOrder(string customerId);

        [OperationContract(IsOneWay = true)]
        void AddProductLineItem(string productId, int quantity);

        [OperationContract(IsOneWay = true)]
        void EndPurchaseOrder();
    }

    // Define the Purchase Order Line Item
    public class PurchaseOrderLineItem
    {
        static Random r = new Random(137);

        string ProductId;
        float UnitCost;
        int Quantity;

        public PurchaseOrderLineItem(string productId, int quantity)
        {
            this.ProductId = productId;
            this.Quantity = quantity;
            this.UnitCost = r.Next(10000);
        }

        public override string ToString()
        {
            String displayString = "Order LineItem: " + Quantity + " of " + ProductId + " @unit price: $" + UnitCost + "\n";
            return displayString;
        }

        public float TotalCost
        {
            get { return UnitCost * Quantity; }
        }
    }

    // Define Purchase Order
    public class PurchaseOrder
    {
        string PONumber;
        string CustomerId;
        LinkedList<PurchaseOrderLineItem> orderLineItems = new LinkedList<PurchaseOrderLineItem>();

        public PurchaseOrder(string customerId)
        {
            this.CustomerId = customerId;
            this.PONumber = Guid.NewGuid().ToString();
        }

        public void AddProductLineItem(string productId, int quantity)
        {
            orderLineItems.AddLast(new PurchaseOrderLineItem(productId, quantity));
        }

        public float TotalCost
        {
            get
            {
                float totalCost = 0;
                foreach (PurchaseOrderLineItem lineItem in orderLineItems)
                    totalCost += lineItem.TotalCost;
                return totalCost;
            }
        }

        public string Status
        {
            get
            {
                return "Pending";
            }
        }

        public override string ToString()
        {
            StringBuilder strbuf = new StringBuilder("Purchase Order: " + PONumber + "\n");
            strbuf.Append("\tCustomer: " + CustomerId + "\n");
            strbuf.Append("\tOrderDetails\n");

            foreach (PurchaseOrderLineItem lineItem in orderLineItems)
            {
                strbuf.Append("\t\t" + lineItem.ToString());
            }

            strbuf.Append("\tTotal cost of this order: $" + TotalCost + "\n");
            strbuf.Append("\tOrder status: " + Status + "\n");
            return strbuf.ToString();
        }
    }


    // Service class which implements the service contract.
    // Added code to write output to the console window
    [ServiceBehavior(InstanceContextMode=InstanceContextMode.PerSession)]
    public class OrderTakerService : IOrderTaker
    {
        PurchaseOrder po;

        [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = false)]
        public void OpenPurchaseOrder(string customerId)
        {
            Console.WriteLine("Creating purchase order");
            po = new PurchaseOrder(customerId);
        }

        [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = false)]
        public void AddProductLineItem(string productId, int quantity)
        {
            po.AddProductLineItem(productId, quantity);
            Console.WriteLine("Product " + productId + " quantity " + quantity + " added to purchase order");
        }

        [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)]
        public void EndPurchaseOrder()
        {
            Console.WriteLine("Purchase Order Completed");
            Console.WriteLine();
            Console.WriteLine(po.ToString());
        }


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

            // Create the transacted MSMQ queue if necessary.
            if (!MessageQueue.Exists(queueName))
                MessageQueue.Create(queueName, true);


            // Get the base address that is used to listen for WS-MetaDataExchange requests
            string baseAddress = ConfigurationManager.AppSettings["baseAddress"];

            // Create a ServiceHost for the OrderTakerService type.
            using (ServiceHost serviceHost = new ServiceHost(typeof(OrderTakerService), new Uri(baseAddress)))
            {
                // Open the ServiceHostBase 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 ServiceHostBase to shutdown the service.
                serviceHost.Close(); 
            }
        }
    }
}
' Service Code:

Imports System
Imports System.ServiceModel.Channels
Imports System.Configuration
Imports System.Messaging
Imports System.ServiceModel
Imports System.Transactions
Imports System.Text
Imports System.Collections.Generic

Namespace Microsoft.ServiceModel.Samples
	' Define a service contract. 
	<ServiceContract(Namespace := "http://Microsoft.ServiceModel.Samples", SessionMode:=SessionMode.Required)> _
	Public Interface IOrderTaker
		<OperationContract(IsOneWay := True)> _
		Sub OpenPurchaseOrder(ByVal customerId As String)

		<OperationContract(IsOneWay := True)> _
		Sub AddProductLineItem(ByVal productId As String, ByVal quantity As Integer)

		<OperationContract(IsOneWay := True)> _
		Sub EndPurchaseOrder()
	End Interface

	' Define the Purchase Order Line Item
	Public Class PurchaseOrderLineItem
		Private Shared r As New Random(137)

		Private ProductId As String
		Private UnitCost As Single
		Private Quantity As Integer

		Public Sub New(ByVal productId As String, ByVal quantity As Integer)
			Me.ProductId = productId
			Me.Quantity = quantity
			Me.UnitCost = r.Next(10000)
		End Sub

		Public Overrides Function ToString() As String
			Dim displayString As String = "Order LineItem: " & Quantity & " of " & ProductId & " @unit price: $" & UnitCost + Constants.vbLf
			Return displayString
		End Function

		Public ReadOnly Property TotalCost() As Single
			Get
				Return UnitCost * Quantity
			End Get
		End Property
	End Class

	' Define Purchase Order
	Public Class PurchaseOrder
		Private PONumber As String
		Private CustomerId As String
		Private orderLineItems As New LinkedList(Of PurchaseOrderLineItem)()

		Public Sub New(ByVal customerId As String)
			Me.CustomerId = customerId
			Me.PONumber = Guid.NewGuid().ToString()
		End Sub

		Public Sub AddProductLineItem(ByVal productId As String, ByVal quantity As Integer)
			orderLineItems.AddLast(New PurchaseOrderLineItem(productId, quantity))
		End Sub

		Public ReadOnly Property TotalCost() As Single
			Get
                Dim totalCost_Renamed As Single = 0
                For Each lineItem In orderLineItems
                    totalCost_Renamed += lineItem.TotalCost
                Next lineItem
				Return totalCost_Renamed
			End Get
		End Property

		Public ReadOnly Property Status() As String
			Get
				Return "Pending"
			End Get
		End Property

		Public Overrides Function ToString() As String
			Dim strbuf As New StringBuilder("Purchase Order: " & PONumber & Constants.vbLf)
			strbuf.Append(Constants.vbTab & "Customer: " & CustomerId & Constants.vbLf)
			strbuf.Append(Constants.vbTab & "OrderDetails" & Constants.vbLf)

            For Each lineItem In orderLineItems
                strbuf.Append(Constants.vbTab + Constants.vbTab + lineItem.ToString())
            Next lineItem

			strbuf.Append(Constants.vbTab & "Total cost of this order: $" & TotalCost + Constants.vbLf)
			strbuf.Append(Constants.vbTab & "Order status: " & Status + Constants.vbLf)
			Return strbuf.ToString()
		End Function
	End Class


	' Service class which implements the service contract.
	' Added code to write output to the console window
	<ServiceBehavior(InstanceContextMode:=InstanceContextMode.PerSession)> _
	Public Class OrderTakerService
		Implements IOrderTaker
		Private po As PurchaseOrder

		<OperationBehavior(TransactionScopeRequired := True, TransactionAutoComplete := False)> _
		Public Sub OpenPurchaseOrder(ByVal customerId As String) Implements IOrderTaker.OpenPurchaseOrder
			Console.WriteLine("Creating purchase order")
			po = New PurchaseOrder(customerId)
		End Sub

		<OperationBehavior(TransactionScopeRequired := True, TransactionAutoComplete := False)> _
		Public Sub AddProductLineItem(ByVal productId As String, ByVal quantity As Integer) Implements IOrderTaker.AddProductLineItem
			po.AddProductLineItem(productId, quantity)
			Console.WriteLine("Product " & productId & " quantity " & quantity & " added to purchase order")
		End Sub

		<OperationBehavior(TransactionScopeRequired := True, TransactionAutoComplete := True)> _
		Public Sub EndPurchaseOrder() Implements IOrderTaker.EndPurchaseOrder
			Console.WriteLine("Purchase Order Completed")
			Console.WriteLine()
			Console.WriteLine(po.ToString())
		End Sub


		' Host the service within this EXE console application.
		Public Shared Sub Main()
			' Get MSMQ queue name from app settings in configuration
			Dim queueName As String = ConfigurationManager.AppSettings("queueName")

			' Create the transacted MSMQ queue if necessary.
			If (Not MessageQueue.Exists(queueName)) Then
				MessageQueue.Create(queueName, True)
			End If


			' Get the base address that is used to listen for WS-MetaDataExchange requests
			Dim baseAddress As String = ConfigurationManager.AppSettings("baseAddress")

			' Create a ServiceHost for the OrderTakerService type.
			Using serviceHost As New ServiceHost(GetType(OrderTakerService), New Uri(baseAddress))
				' Open the ServiceHostBase 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 ServiceHostBase to shutdown the service.
				serviceHost.Close()
			End Using
		End Sub
	End Class
End Namespace

Code für den ClientCode for the Client

using System;
using System.Configuration;
using System.Messaging;
using System.ServiceModel;
using System.Transactions;

namespace Microsoft.ServiceModel.Samples
{
    //The service contract is defined in generatedProxy.cs, generated from the service by the svcutil tool.

    //Client implementation code.
    class Client
    {
        static void Main()
        {
            //Create a transaction scope.
            using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required))
            {
                // Create a proxy with given client endpoint configuration
                OrderTakerClient client = new OrderTakerClient("OrderTakerEndpoint");
                try
                {
                    // Open a purchase order
                    client.OpenPurchaseOrder("somecustomer.com");
                    Console.WriteLine("Purchase Order created");

                    // Add product line items
                    Console.WriteLine("Adding 10 quantities of blue widget");
                    client.AddProductLineItem("Blue Widget", 10);

                    Console.WriteLine("Adding 23 quantities of red widget");
                    client.AddProductLineItem("Red Widget", 23);

                    // Close the purchase order
                    Console.WriteLine("Closing the purchase order");
                    client.EndPurchaseOrder();
                    client.Close();
                }
                catch (CommunicationException ex)
                {
                    client.Abort();
                }
                // Complete the transaction.
                scope.Complete();
            }
            Console.WriteLine();
            Console.WriteLine("Press <ENTER> to terminate client.");
            Console.ReadLine();
        }
    }
}

Imports System
Imports System.Configuration
Imports System.Messaging
Imports System.ServiceModel
Imports System.Transactions

Namespace Microsoft.ServiceModel.Samples
	'The service contract is defined in generatedProxy.cs, generated from the service by the svcutil tool.

	'Client implementation code.
	Friend Class Client
		Shared Sub Main()
			'Create a transaction scope.
			Using scope As New TransactionScope(TransactionScopeOption.Required)
				' Create a proxy with given client endpoint configuration
				Dim client As New OrderTakerClient("OrderTakerEndpoint")
				Try
					' Open a purchase order
					client.OpenPurchaseOrder("somecustomer.com")
					Console.WriteLine("Purchase Order created")

					' Add product line items
					Console.WriteLine("Adding 10 quantities of blue widget")
					client.AddProductLineItem("Blue Widget", 10)

					Console.WriteLine("Adding 23 quantities of red widget")
					client.AddProductLineItem("Red Widget", 23)

					' Close the purchase order
					Console.WriteLine("Closing the purchase order")
					client.EndPurchaseOrder()
					client.Close()
				Catch ex As CommunicationException
					client.Abort()
				End Try
				' Complete the transaction.
				scope.Complete()
			End Using
			Console.WriteLine()
			Console.WriteLine("Press <ENTER> to terminate client.")
			Console.ReadLine()
		End Sub
	End Class
End Namespace

Siehe auchSee Also

Sitzungen und WarteschlangenSessions and Queues
WarteschlangenübersichtQueues Overview