How to: Exchange Queued Messages with WCF Endpoints

Queues ensure that reliable messaging can occur between a client and a Windows Communication Foundation (WCF) service, even if the service is not available at the time of communication. The following procedures show how to ensure durable communication between a client and a service by using the standard queued binding when implementing the WCF service.

This section explains how to use NetMsmqBinding for queued communication between a WCF client and a WCF service.

To use queuing in a WCF service

  1. Define a service contract using an interface marked with the ServiceContractAttribute. Mark the operations in the interface that are part of the service contract with the OperationContractAttribute and specify them as one-way because no response to the method is returned. The following code provides an example service contract and its operation definition.

    <ServiceContract(Namespace:="http://Microsoft.ServiceModel.Samples")> _
    Public Interface IOrderProcessor
        <OperationContract(IsOneWay := True)> _
        Sub SubmitPurchaseOrder(ByVal po As PurchaseOrder)
    End Interface
    
    
  2. When the service contract passes user-defined types, define data contracts for those types. The following code shows two data contracts, PurchaseOrder and PurchaseOrderLineItem these two types define data that is sent to the service. (Note that the classes that define this data contract also define a number of methods. These methods are not considered part of the data contract. Only those members that are declared with the DataMember attribute are part of the data contract.)

    <DataContract(Namespace := "http://Microsoft.ServiceModel.Samples")> _
    Public Class PurchaseOrder
        Private Shared ReadOnly OrderStates() As String = { "Pending", "Processed", "Shipped" }
        Private Shared statusIndexer As New Random(137)
    
        <DataMember> _
        Public PONumber As String
    
        <DataMember> _
        Public CustomerId As String
    
        <DataMember> _
        Public orderLineItems() As PurchaseOrderLineItem
    
        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 OrderStates(statusIndexer.Next(3))
            End Get
        End Property
    
        Public Overrides Function ToString() As String
            Dim strbuf As New System.Text.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
    
    
  3. Implement the methods of the service contract defined in the interface in a class.

    Public Class OrderProcessorService
        Implements IOrderProcessor
        <OperationBehavior(TransactionScopeRequired := True, TransactionAutoComplete := True)> _
        Public Sub SubmitPurchaseOrder(ByVal po As PurchaseOrder) Implements IOrderProcessor.SubmitPurchaseOrder
            Orders.Add(po)
            Console.WriteLine("Processing {0} ", po)
        End Sub
    End Class
    
    

    Notice the OperationBehaviorAttribute placed on the SubmitPurchaseOrder method. This specifies that this operation must be called within a transaction and that the transaction automatically completes when the method completes.

  4. Create a transactional queue using System.Messaging. You can choose to create the queue using Microsoft Message Queuing (MSMQ) Microsoft Management Console (MMC) instead. If so, make sure you create a transactional queue.

    ' Create the transacted MSMQ queue if necessary.
    If (Not MessageQueue.Exists(queueName)) Then
        MessageQueue.Create(queueName, True)
    End If
    
    
  5. Define a ServiceEndpoint in configuration that specifies the service address and uses the standard NetMsmqBinding binding. For more information about using WCF configuration, see Configuring Windows Communication Foundation Applications.

    <services>
      <service 
          name="Microsoft.ServiceModel.Samples.OrderProcessorService"
          behaviorConfiguration="CalculatorServiceBehavior">
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:8000/ServiceModelSamples/service"/>
          </baseAddresses>
        </host>
        <!-- Define NetMsmqEndpoint -->
        <endpoint address="net.msmq://localhost/private/ServiceModelSamplesTransacted"
                  binding="netMsmqBinding"
                  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>
    
    
  6. Create a host for the OrderProcessing service using ServiceHost that reads messages from the queue and processes them. Open the service host to make the service available. Display a message that tells the user to press any key to terminate the service. Call ReadLine to wait for the key to be pressed and then close the service.

    ' Create a ServiceHost for the OrderProcessorService type.
    Using serviceHost As New ServiceHost(GetType(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 ServiceHostB to shutdown the service.
        serviceHost.Close()
    End Using
    
    

To create a client for the queued service

  1. Run the hosting application and use the Svcutil.exe tool to create the WCF client. The following command line shows how to do this.

    svcutil http://localhost:8000/ServiceModelSamples/service
    
  2. Define a ServiceEndpoint in configuration that specifies the address and uses the standard NetMsmqBinding binding.

    <client>
      <!-- Define NetMsmqEndpoint -->
      <endpoint address="net.msmq://localhost/private/ServiceModelSamplesTransacted" 
                binding="netMsmqBinding" 
                contract="Microsoft.ServiceModel.Samples.IOrderProcessor" />
    </client>
    
    
  3. Create a transaction scope to write to the transactional queue, call the SubmitPurchaseOrder operation and close the WCF client.

    'Create a transaction scope.
    Using scope As New TransactionScope(TransactionScopeOption.Required)
        ' Make a queued call to submit the purchase order.
        client.SubmitPurchaseOrder(po)
        ' Complete the transaction.
        scope.Complete()
    End Using
    
    'Closing the client gracefully closes the connection and cleans up resources.
    client.Close()
    
    

Example

' This is the service code
'  Copyright (c) Microsoft Corporation.  All Rights Reserved.

Imports System
Imports System.ServiceModel.Channels
Imports System.Configuration
Imports System.Messaging
Imports System.ServiceModel
Imports System.Transactions
Imports System.Runtime.Serialization
Imports System.Collections.Generic

Namespace Microsoft.ServiceModel.Samples
	' Define the purchase order line item.
	<DataContract(Namespace := "http://Microsoft.ServiceModel.Samples")> _
	Public Class PurchaseOrderLineItem
		<DataMember> _
		Public ProductId As String

		<DataMember> _
		Public UnitCost As Single

		<DataMember> _
		Public Quantity As Integer

		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 the purchase order.
	<DataContract(Namespace := "http://Microsoft.ServiceModel.Samples")> _
	Public Class PurchaseOrder
		Private Shared ReadOnly OrderStates() As String = { "Pending", "Processed", "Shipped" }
		Private Shared statusIndexer As New Random(137)

		<DataMember> _
		Public PONumber As String

		<DataMember> _
		Public CustomerId As String

		<DataMember> _
		Public orderLineItems() As PurchaseOrderLineItem

		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 OrderStates(statusIndexer.Next(3))
			End Get
		End Property

		Public Overrides Function ToString() As String
			Dim strbuf As New System.Text.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

	' Order Processing Logic
	' Can replace with transaction-aware resource such as SQL or transacted hashtable to hold the purchase orders.
	' This example uses a non-transactional resource.
	Public Class Orders
		Private Shared purchaseOrders As New Dictionary(Of String, PurchaseOrder)()

		Public Shared Sub Add(ByVal po As PurchaseOrder)
			purchaseOrders.Add(po.PONumber, po)
		End Sub

		Public Shared Function GetOrderStatus(ByVal poNumber As String) As String
            Dim po As PurchaseOrder = Nothing
			If purchaseOrders.TryGetValue(poNumber, po) Then
				Return po.Status
			Else
				Return Nothing
			End If
		End Function

		Public Shared Sub DeleteOrder(ByVal poNumber As String)
			If purchaseOrders(poNumber) IsNot Nothing Then
				purchaseOrders.Remove(poNumber)
			End If
		End Sub
	End Class

	' Define a service contract. 
	<ServiceContract(Namespace:="http://Microsoft.ServiceModel.Samples")> _
	Public Interface IOrderProcessor
		<OperationContract(IsOneWay := True)> _
		Sub SubmitPurchaseOrder(ByVal po As PurchaseOrder)
	End Interface

	' Service class that implements the service contract.
	' Added code to write output to the console window.
	Public Class OrderProcessorService
		Implements IOrderProcessor
		<OperationBehavior(TransactionScopeRequired := True, TransactionAutoComplete := True)> _
		Public Sub SubmitPurchaseOrder(ByVal po As PurchaseOrder) Implements IOrderProcessor.SubmitPurchaseOrder
			Orders.Add(po)
			Console.WriteLine("Processing {0} ", po)
		End Sub
	End Class
End Namespace

' This is the hosting application.

Imports System
Imports System.ServiceModel.Channels
Imports System.Configuration
Imports System.Messaging
Imports System.ServiceModel
Imports System.Transactions
Imports System.Runtime.Serialization
Imports System.Collections.Generic

Namespace Microsoft.ServiceModel.Samples
	Friend Class hostApp
		' Host the service within this EXE console application.
		Public Shared Sub Main()
			' Get MSMQ queue name from appsettings 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

			' Create a ServiceHost for the OrderProcessorService type.
			Using serviceHost As New ServiceHost(GetType(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 ServiceHostB to shutdown the service.
				serviceHost.Close()
			End Using
		End Sub
	End Class
End Namespace

<!-- This is the app.config for the service -->
<configuration>

  <appSettings>
    <!-- use appSetting to configure MSMQ queue name -->
    <add key="queueName" value=".\private$\ServiceModelSamplesTransacted" />
  </appSettings>

  <system.serviceModel>
    <services>
      <service 
          name="Microsoft.ServiceModel.Samples.OrderProcessorService"
          behaviorConfiguration="CalculatorServiceBehavior">
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:8000/ServiceModelSamples/service"/>
          </baseAddresses>
        </host>
        <!-- Define NetMsmqEndpoint -->
        <endpoint address="net.msmq://localhost/private/ServiceModelSamplesTransacted"
                  binding="netMsmqBinding"
                  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>
    
    <behaviors>
      <serviceBehaviors>
        <behavior name="CalculatorServiceBehavior">
          <serviceMetadata httpGetEnabled="True"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>

  </system.serviceModel>

</configuration>

' This is the client code.
'  Copyright (c) Microsoft Corporation.  All Rights Reserved.

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

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

	'Client implementation code.
	Friend Class Client
		Shared Sub Main()
			' Create a client.
			Dim client As New OrderProcessorClient()

			' Create the purchase order.
			Dim po As New PurchaseOrder()
			po.CustomerId = "somecustomer.com"
			po.PONumber = Guid.NewGuid().ToString()

			Dim lineItem1 As New PurchaseOrderLineItem()
			lineItem1.ProductId = "Blue Widget"
			lineItem1.Quantity = 54
			lineItem1.UnitCost = 29.99F

			Dim lineItem2 As New PurchaseOrderLineItem()
			lineItem2.ProductId = "Red Widget"
			lineItem2.Quantity = 890
			lineItem2.UnitCost = 45.89F

			po.orderLineItems = New PurchaseOrderLineItem(1){}
			po.orderLineItems(0) = lineItem1
			po.orderLineItems(1) = lineItem2

			'Create a transaction scope.
			Using scope As New TransactionScope(TransactionScopeOption.Required)
				' Make a queued call to submit the purchase order.
				client.SubmitPurchaseOrder(po)
				' Complete the transaction.
				scope.Complete()
			End Using

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

			Console.WriteLine()
			Console.WriteLine("Press <ENTER> to terminate client.")
			Console.ReadLine()
		End Sub
	End Class
End Namespace

<!-- This is the app.config for the client app -->
<configuration>
  <system.serviceModel>
    <client>
      <!-- Define NetMsmqEndpoint -->
      <endpoint address="net.msmq://localhost/private/ServiceModelSamplesTransacted" 
                binding="netMsmqBinding" 
                contract="Microsoft.ServiceModel.Samples.IOrderProcessor" />
    </client>
  </system.serviceModel>
</configuration>

See Also


© 2007 Microsoft Corporation. All rights reserved.
Last Published: 2010-03-21
Was this page helpful?
(1500 characters remaining)
Thank you for your feedback

Community Additions

ADD
Show:
© 2015 Microsoft