This sample demonstrates how to use the Windows Azure Service Bus using WCF channels.
The sample shows the use of WCF channels to send and receive messages via a Service Bus queue. The sample shows both session and non-session communication over the Service Bus. The sample prompts for service namespace credentials for the purpose of creating and deleting the queues, and sending and receiving messages. The credentials are used to authenticate with the Access Control service, and acquire an access token that proves to the Service Bus infrastructure that the client is authorized to access the queue. The senders first send messages to the non-session queue which are then received by the receivers as they become available, illustrating a non-session communication using the WCF channel model. The senders then send messages to the session queue which are received by the session receivers, illustrating session-based communication using the WCF channel model.
Prerequisites
If you haven't already done so, please read the release notes document that explains how to sign up for a Windows Azure account and how to configure your environment.
Configuration File
The sender and receiver use NetMessagingBinding for non-session communication and CustomBinding for session communication. Both the bindings are defined in the respective App.Config files. NetMessagingBinding uses BinaryMessageEncoding as its encoder and NetMessagingTransportBindingElement as its transport element. TransportSettings is a part of the transport element, and represents the runtime factory used by the Service Bus. An extension section must be added to the configuration file in order to use Service Bus components with WCF.
In addition to the binding, both the config files have a
App.Config - Config Extensions and Binding | |
---|---|
<extensions> <bindingElementExtensions> <add name="netMessagingTransport" type="Microsoft.ServiceBus.Messaging.Configuration.NetMessagingTransportExtensionElement, Microsoft.ServiceBus, Version=1.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" /> </bindingElementExtensions> <bindingExtensions> <add name="netMessagingBinding" type="Microsoft.ServiceBus.Messaging.Configuration.NetMessagingBindingCollectionElement, Microsoft.ServiceBus, Version=1.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" /> </bindingExtensions> </extensions> <bindings> <customBinding> <binding name="customBinding" sendTimeout="00:02:00" receiveTimeout="00:00:30" openTimeout="00:02:00" closeTimeout="00:02:00"> <binaryMessageEncoding /> <netMessagingTransport manualAddressing="false" > <transportSettings /> </netMessagingTransport> </binding> </customBinding> <netMessagingBinding> <binding name="messagingBinding" sendTimeout="00:02:00" receiveTimeout="00:00:30" openTimeout="00:02:00" closeTimeout="00:02:00" > <transportSettings /> </binding> </netMessagingBinding> </bindings> |
Credentials
The sample gets user credentials and creates a NamespaceManager. This entity holds the credentials and is used for all messaging management operations - in this case, to create and delete queues.
C# | |
---|---|
public static void GetUserCredentials() { // User namespace Console.WriteLine("Please provide the namespace to use:"); serviceBusNamespace = Console.ReadLine(); // Issuer name Console.WriteLine("Please provide the Issuer name to use:"); serviceBusIssuerName = Console.ReadLine(); // Issuer key Console.WriteLine("Please provide the Issuer key to use:"); serviceBusIssuerKey = Console.ReadLine(); } // Create the NamespaceManager for management operations (queue) static void CreateNamespaceManager() { // Create SharedSecretCredential object for access control service TokenProvider credentials = TokenProvider.CreateSharedSecretTokenProvider(serviceBusIssuerName, serviceBusIssuerKey); // Create the management Uri Uri managementUri = ServiceBusEnvironment.CreateServiceUri("sb", serviceBusNamespace, string.Empty); namespaceClient = new NamespaceManager(managementUri, credentials); } // Create the entity (queue) static Queue CreateQueue(bool session) { string queueName = (session ? sessionQueueName : sessionlessQueueName); QueueDescription queueDescription = new QueueDescription(queueName) { RequiresSession = session }; // Try deleting the queue before creation. Ignore exception if queue does not exist. try { namespaceClient.DeleteQueue(queueDescription.Path); } catch (MessagingEntityNotFoundException) { } return namespaceClient.CreateQueue(queueDescription); } |
The preceding code prompts for the issuer credential and then constructs the listening URI using that information. The static ServiceBusEnvironment.CreateServiceUri function is provided to help construct the URI with the correct format and domain name. It is strongly recommended that you use this function instead of building the URI from scratch because the URI construction logic and format might change in future releases. At present, the resulting URI is scheme://<service-namespace>.servicebus.windows.net/.
The CreateNamespaceManager() function creates the object to perform management operations, in this case creating and deleting queues. Both ‘https’ and ‘sb’ Uri schemes are allowed as a part of the service Uri.
The CreateQueue(bool session) function creates a queue with the RequireSession property set according to the argument passed.
Sender
The Service Bus only supports IOutputChannel for sending messages using NetMessagingBinding or CustomBinding. Both the session-based and non-session send operations are mapped to the IOutputChannel.
C# | |
---|---|
// Get credentials as Endpoint behavior TransportClientEndpointBehavior securityBehavior = new TransportClientEndpointBehavior(); securityBehavior.TokenProvider = TokenProvider.CreateSharedSecretTokenProvider(serviceBusIssuerName, serviceBusIssuerKey); // Create factory and channel using NetMessagingBinding NetMessagingBinding messagingBinding = new NetMessagingBinding("messagingBinding"); EndpointAddress address = SampleManager.GetEndpointAddress(SampleManager.SessionlessQueueName, serviceBusNamespace); IChannelFactory<IOutputChannel> messagingChannelFactory = messagingBinding.BuildChannelFactory<IOutputChannel>(securityBehavior); messagingChannelFactory.Open(); IOutputChannel messagingOutputChannel = messagingChannelFactory.CreateChannel(address); messagingOutputChannel.Open(); // Create factory and channel using custom binding CustomBinding customBinding = new CustomBinding("customBinding"); address = SampleManager.GetEndpointAddress(SampleManager.SessionQueueName, serviceBusNamespace); IChannelFactory<IOutputChannel> customChannelFactory = customBinding.BuildChannelFactory<IOutputChannel>(securityBehavior); customChannelFactory.Open(); IOutputChannel customOutputChannel = customChannelFactory.CreateChannel(address); customOutputChannel.Open(); public static EndpointAddress GetEndpointAddress(string queueName, string serviceBusNamespace) { return new EndpointAddress(ServiceBusEnvironment.CreateServiceUri("sb", serviceBusNamespace, queueName)); } |
Service Bus credentials are passed to the client via an endpoint behavior of type TransportClientEndpointBehavior.
The Endpoint address is constructed using the ServiceBusEnvironment.CreateServiceUri function and is of the form sb://<service-namespace>.servicebus.windows.net/<entity-name>. Note that the Uri scheme ‘sb’ is mandatory for all runtime operations such as send/receive.
To accomplish session communication over NetMessagingBinding, the BrokeredMessageProperty.SessionId must be set to the desired session value. All the messages with the same SessionId are grouped together in a single session. This property is required to be set for session-based communication and is optional for non-session communication.
C# | |
---|---|
string sessionName = rand.Next(SampleManager.NumSessions).ToString(); // Creating BrokeredMessageProperty BrokeredMessageProperty property = new BrokeredMessageProperty(); property.SessionId = sessionName; property.Label = "Order_" + Guid.NewGuid().ToString().Substring(0, 5); // Creating message and adding BrokeredMessageProperty to the properties bag Message message = Message.CreateMessage(binding.MessageVersion, "Order"); message.Properties.Add(BrokeredMessageProperty.Name, property); // Sending message clientChannel.Send(message); |
Receiver
The sample illustrates non-session receive over a Service Bus queue with the receive mode set to ‘Receive and Delete’. In this mode, the message is removed from the queue as soon as it is delivered to the receiver. This mode is useful when the communication between sender and receiver can tolerate loss of data.
The non-session receive operation is done using IInputChannel. Non-session receive operations require a single channel for communication with the queue. Since the receiver is in receive and delete mode, ReceiveContext.Complete operation is not required. In the sample, the while loop continuously polls the queue for message. If a message is available, the operation returns the message. If no more messages are available in the queue, the receive operation waits for the specified Timeout period for new messages to arrive. If no new messages are still available, it generates a TimeoutException.
In the sample, the service collects all the items in a single session and then displays the total at the end. The service is defined in its App.config file.
C# | |
---|---|
// Create channel listener and channel using NetMessagingBinding NetMessagingBinding messagingBinding = new NetMessagingBinding("messagingBinding"); EndpointAddress address = SampleManager.GetEndpointAddress(SampleManager.SessionlessQueueName, serviceBusNamespace); TransportClientEndpointBehavior securityBehavior = new TransportClientEndpointBehavior(); securityBehavior.TokenProvider = SampleManager.GetSharedSecretToken(serviceBusIssuerName, serviceBusIssuerKey); IChannelListener<IInputChannel> inputChannelListener = messagingBinding.BuildChannelListener<IInputChannel>(address.Uri, securityBehavior); inputChannelListener.Open(); IInputChannel inputChannel = inputChannelListener.AcceptChannel(); inputChannel.Open(); while (true) { try { // Receive message from queue. If no more messages available, the operation throws a TimeoutException. Message receivedMessage = inputChannel.Receive(receiveMessageTimeout); SampleManager.OutputMessageInfo("Receive", receivedMessage); } catch (TimeoutException) { break; } } // Close inputChannel.Close(); inputChannelListener.Close(); |
The sample also illustrates session-based receive over the Service Bus queue with receive mode as ‘Peek Lock’. In this mode, the message is removed from the queue only after an explicit ReceiveContext.Complete() operation is performed by the receiver. If the ReceiveContext.Complete() is not received within the LockDuration, the message is unlocked and placed in the queue again. The LockDuration is a customizable property of the queue and can only be set during queue creation.
The session-based receive operation is performed using IInputSessionChannel. Session-based receive operations require a new session channel for every available session in the queue. An explicit ReceiveContext.Complete() operation is performed for every message received. In the sample, the while loop continuously polls the queue for available new sessions. If the session is available, AcceptChannel() creates a session channel for that particular session. The inner while loop receives all the messages in the sessions until the TryReceive operation returns false indicating no messages available for that session. This is repeated for all available sessions in the queue. If no new session is available in the queue, the AcceptChannel() request generates a TimeoutException.
C# | |
---|---|
// Create listener and channel using custom binding CustomBinding customBinding = new CustomBinding("customBinding"); EndpointAddress address = SampleManager.GetEndpointAddress(SampleManager.SessionQueueName, serviceBusNamespace); TransportClientEndpointBehavior securityBehavior = new TransportClientEndpointBehavior(); securityBehavior.TokenProvider = SampleManager.GetSharedSecretToken(serviceBusIssuerName, serviceBusIssuerKey); customBinding.GetProperty<IReceiveContextSettings>(new BindingParameterCollection()).Enabled = true; IChannelListener<IInputSessionChannel> inputSessionChannelListener = customBinding.BuildChannelListener<IInputSessionChannel>(address.Uri, securityBehavior); inputSessionChannelListener.Open(); while (true) { IInputSessionChannel inputSessionChannel = null; try { // Create a new session channel for every new session available. If no more sessions available, // then the operation throws a TimeoutException. inputSessionChannel = inputSessionChannelListener.AcceptChannel(acceptSessionReceiverTimeout); inputSessionChannel.Open(); // TryReceive operation returns true if message is available otherwise it returns false. Message receivedMessage; while (inputSessionChannel.TryReceive(receiveSessionMessageTimeout, out receivedMessage)) { if (receivedMessage != null) { SampleManager.OutputMessageInfo("Receive", receivedMessage); // Since the binding has ReceiveContext enabled, a manual complete operation is mandatory // if the message was processed successfully. ReceiveContext rc; if (ReceiveContext.TryGet(receivedMessage, out rc)) { rc.Complete(TimeSpan.FromSeconds(10.0d)); } else { throw new InvalidOperationException("Receiver is in peek lock mode but receive context is not available!"); } } else { // This IInputSessionChannel doesn't have any more messages break; } } // Close session channel inputSessionChannel.Close(); inputSessionChannel = null; } catch (TimeoutException) { break; } finally { if (inputSessionChannel != null) { inputSessionChannel.Abort(); } } } // Close channel listener inputSessionChannelListener.Close(); |
Running the Sample
To run the sample, build the solution in Visual Studio or from the command line, then run the executable ‘SampleManager.exe’. The program prompts for your service namespace and the issuer credentials. For the issuer secret, be sure to use the "Default Issuer Key" value (typically "owner") from the portal, rather than one of the management keys.
Expected Output - Sample Manager
Please provide the namespace to use: <Service Namespace> Please provide the Issuer name to use: <Issuer Name> Please provide the Issuer key to use: <Issuer Key> Creating Queues... Created OrderQueue_NoSession, Queue.RequiresSession = false Created OrderQueue_Session, Queue.RequiresSession = true Launching senders and receivers... Press [Enter] to exit. |
Expected Output – Sender
Preparing to send messages to OrderQueue_NoSession... Started sending messages... Send: Order_9bc3d - Group 0. Send: Order_4ca9e - Group 1. Send: Order_4ed05 - Group 0. Send: Order_0618d - Group 2. Send: Order_f6429 - Group 3. Send: Order_2c0ba - Group 0. Send: Order_a928b - Group 2. Send: Order_707f7 - Group 3. Send: Order_e092a - Group 1. Send: Order_b068e - Group 1. Send: Order_d6b7f - Group 0. Send: Order_98b1b - Group 2. Finished sending messages Preparing to send messages to OrderQueue_Session... Started sending messages... Send: Order_05125 - Group 0. Send: Order_15aad - Group 1. Send: Order_19a20 - Group 3. Send: Order_df3d6 - Group 2. Send: Order_77f8d - Group 1. Send: Order_a857a - Group 0. Send: Order_43594 - Group 2. Send: Order_d3ece - Group 3. Send: Order_1f312 - Group 1. Finished sending messages Sender complete. Press [Enter] to exit. |
Expected Output – Message Receiver
Ready to receive messages from OrderQueue_NoSession... Reading messages from queue OrderQueue_NoSession... Receiver Type: Receive and Delete Receive: Order_f87fb - Group 0. Receive: Order_cf32b - Group 1. Receive: Order_29195 - Group 0. Receive: Order_80c3d - Group 2. Receive: Order_e9758 - Group 3. Receive: Order_7578a - Group 0. Receive: Order_36de1 - Group 2. Receive: Order_478f8 - Group 3. Receive: Order_128b5 - Group 1. Receive: Order_64a86 - Group 1. Receive: Order_d7943 - Group 0. Receive: Order_7fc59 - Group 2. Receiver complete. Press [Enter] to exit. |
Expected Output - Session Message Receiver
Ready to receive messages from OrderQueue_Session... Reading messages from queue OrderQueue_Session... Receiver Type: PeekLock Receive: Order_df3d6 - Group 0. Receive: Order_f78dd - Group 0. Receive: Order_807eb - Group 1. Receive: Order_808e2 - Group 1. Receive: Order_f37d3 - Group 1. Receive: Order_c970d - Group 3. Receive: Order_77f8d - Group 3. Receive: Order_ff8bd - Group 2. Receive: Order_1a0f4 - Group 2. Receiver complete. Press [Enter] to exit. |
Did you find this information useful? Please send your suggestions and comments about the documentation.