CHAPTER 6 ■ AZURE .NET SERVICES—SERVICE BUS 203 </behavior> </endpointBehaviors> </behaviors> <client> <endpoint name="RelayEndpoint" contract="SoftnetSolutions.RelayService.ServiceContract.IPublishEventService" binding="netTcpRelayBinding" bindingConfiguration="default" behaviorConfiguration="CardSpaceBehavior" address="http://AddressToBeReplacedInCode/" /> </client> <services> <service name="SoftnetSolutions.Shape.Draw.FormDrawShape"> <endpoint name="RelayEndpoint" contract="SoftnetSolutions.RelayService.ServiceContract.IPublishEventService" binding="netTcpRelayBinding" bindingConfiguration="default" behaviorConfiguration="CardSpaceBehavior" address="" /> </service> </services> </system.serviceModel> </configuration> Listing 6-18. Change the Credential Type to automaticRenewalClientCredentials in the Configuration File App.config of ShapeController <?xml version="1.0" encoding="utf-8" ?> <configuration> <appSettings> <add key="Topic" value="PublishEventService"/> <add key="Solution" value="SoftnetSolutionsServiceBus"/> <add key="password" value="9j!Ns$R8%7"/> </appSettings> <system.serviceModel> <bindings> <netTcpRelayBinding> <binding name="default" connectionMode="Hybrid"> <security mode="None" /> </binding> </netTcpRelayBinding> </bindings> <behaviors> <endpointBehaviors> <behavior name="automaticRenewalClientCredentials"> <transportClientEndpointBehavior credentialType="AutomaticRenewal" /> </behavior> CHAPTER 6 ■ AZURE .NET SERVICES—SERVICE BUS 204 </endpointBehaviors> </behaviors> <client> <endpoint name="RelayEndpoint" contract="SoftnetSolutions.RelayService.ServiceContract.IPublishEventService" binding="netTcpRelayBinding" bindingConfiguration="default" behaviorConfiguration="automaticRenewalClientCredentials" address="http://AddressToBeReplacedInCode/" /> </client> <services> <service name="SoftnetSolutions.Shape.Draw.FormDrawShape"> <endpoint name="RelayEndpoint" contract="SoftnetSolutions.RelayService.ServiceContract.IPublishEventService" binding="netTcpRelayBinding" bindingConfiguration="default" behaviorConfiguration="automaticRenewalClientCredentials" address="" /> </service> </services> </system.serviceModel> </configuration> In Chapter 3 we discussed Azure Queue storage, which is one of the three basic storage types used as part of the Azure framework. .NET Service Bus also provides application-level queue storage, called .NET Service Bus Queue, that tremendously simplifies the message delivery between applications run behind a firewall. In the next exercise we are going to refactor this exercise to use .NET Service Bus Queue and let you get hands-on experience with .NET Service Bus Queue. You'll be able to reuse the libraries from the project in your future development. .NET Service Bus Queue Client Facade The .NET Service Bus Queue leverages every Internet communication protocol to allow message delivery through the cloud. The .NET Services SDK provides rich .NET Service Bus Queue examples and covers all its features in detail. This book does not intend to duplicate these examples but build a facade QueueClientFactory component allowing you to easily integrate the .NET Service Bus Queue into applications run from either a cloud or on-premises environment. You should be able to easily find a lot of blogs and technical articles, such as http://vasters.com/clemensv/PermaLink,guid,0f64f592-7239- 42fc-aed2-f0993701c5f6.aspx. Before we move on, let us cover some background information that may be useful in future development. As you know from Chapter 3, the Azure Queue service runs from local or cloud fabric. In contrast, .NET Service Bus Queue exists on the Internet and is URI addressable using the address format list shown in the following bullet points. The URI addresses should contain the phrase servicebus.windows.net. There is no limitation to the format for the .NET Service Bus queue, so the queue message can carry user-defined data types. The .NET Service Bus uses the URI for the name or address of a queue. CHAPTER 6 ■ AZURE .NET SERVICES—SERVICE BUS 205 The format of a queue address is: • sb://solution.servicebus.windows.net/QueueName • http://solution.servicebus.windows.net/QueueName • https://solution.servicebus.windows.net/QueueName The queue name can be in any format. In other words, the .NET Service Bus queues can be addressed by subject in the hierarchical structure. This makes the .NET Service Bus Queue a good candidate to be used in an event-driven distributed system. This exercise refactors the .NET Services SDK example TypedMessages. The original source code and document can be found at [install drive]:\Program Files\Microsoft .NET Services SDK\Samples\ServiceBus\ExploringFeatures\Queues\TypedMessages. ■ Note The code for this example is in the Exercise 6-4 bundle from the code download. To use the .NET Service Bus Queue it is essential to create an instance of the QueueClient class. The core class is the QueueClientFactory, which accepts a generic type T as a parameter since the core member variable in this class, queueMessage (of type of QueueMessage), does. The steps to create a .NET Service Bus QueueClient are straightforward. 1. Create a service URI by calling a static method CreateServiceUri() from the class ServiceBusEnvironment in the Microsoft.ServiceBus.dll assembly. (This assembly can be found in the Assemblies folder of the .NET Services SDK path.) 2. Create a QueuePolicy. The simplest way, which this exercise uses, is to create the queue policy using the .NET Services SDK’s Microsoft.ServiceBus.QueuePolicy class. The QueueClient instances cannot be created directly but must be created via the QueueManagementClient. 3. Create the QueueClientFactory class to wrap up the methods from the SDK’s TypedMessages example. This class can be used for both client-side and server- side applications. If this factory class is used for a server-side application, the application can register an update callback notification when a new queue message is detected. The implementation of the QueueClientFactory is shown in Listing 6-19. In order to process the update callback notification, the data entity object needs to implement the IComparable interface allowing the caller object to detect the value change of the internal custom-defined data types (in this example the custom data is the enumerator type). This all applies where a WCF data contract type class is used (since under the hood QueueClient uses the WCF service for communication). The QueueClientFactory class has two member variables, queueMessage and lastQueueMessage, with the type of QueueMessage defined in the Microsoft.Samples.ServiceBus namespace. Unlike the Azure Queue we explored in Chapter 3, this class dose not fire the callback event when a new message is put into the queue. Therefore our wrapper class has to actively poll the queue periodically to get the new message. This is done from the timer tick handler PollingQueueData. If the internal data value of the message is different from that of the last message, the factory wrapper class fires a notification event and persists the message instance to the member variable lastQueueMessage. This is the reason why the data CHAPTER 6 ■ AZURE .NET SERVICES—SERVICE BUS 206 contract class must implement the IComparable interface if the embedded data is a custom-defined type. Another difference between the Azure Queue storage and .NET Service Bus Queue is that the queue message is not persisted in the QueueMessage class. As I mentioned at the beginning of this exercise, the .NET Service Bus Queue is an application-level queue available through the Internet. Therefore, it would make sense to expect it to have a permanent persistence storage space. This also explains why the Azure Queue does not actively remove the message from queue storage until the client explicitly calls a service to delete it, while the .NET Service Bus Queue does actively remove messages. Listing 6-19. Implementation of QueueClientFactory using System; using System.ServiceModel; using System.ServiceModel.Description; using Microsoft.ServiceBus; using Microsoft.ServiceBus.Description; using System.Text; using System.ServiceModel.Channels; using System.Configuration; namespace SoftnetSolutions.ServiceBus.QueueFacade { using Microsoft.Samples.ServiceBus; using SoftnetSolutions.AzureSolutionCredential; using CSharpBuildingBlocks.EventsHelper; public class QueueClientFactory<T> where T: class { protected System.Timers.Timer timer = null; protected QueueClientFactory<T> queueClientFactory = null; protected QueueMessage<T> queueMessage = null; protected QueueMessage<T> lastQueueMessage = null; protected event EventNotificationHandler dataUpdateEvent = null; public QueueClient<T> QueueClient { get; set; } public QueueClientFactory() { Initialization(); StartQueuePollingTimer(); } public event EventNotificationHandler DataUpdateEvent { add { dataUpdateEvent += value; StartQueuePollingTimer(); } remove { dataUpdateEvent -= value; } } private void StartQueuePollingTimer() CHAPTER 6 ■ AZURE .NET SERVICES—SERVICE BUS 207 { timer = new System.Timers.Timer(1000); timer.Elapsed += new System.Timers.ElapsedEventHandler(PollingQueueData); timer.AutoReset = true; timer.Enabled = true; timer.Start(); } private void Initialization() { string solutionName = ConfigurationManager.AppSettings["Solution"]; string queueName = ConfigurationManager.AppSettings["QueueName"]; AzureSolutionCredential azureSolutionCredential = new AzureSolutionCredential(solutionName); Uri queueUri = ServiceBusEnvironment.CreateServiceUri("sb", solutionName, string.Format("/{0}/", queueName)); TransportClientEndpointBehavior userNamePasswordServiceBusCredential = new TransportClientEndpointBehavior(); userNamePasswordServiceBusCredential.CredentialType = TransportClientCredentialType.UserNamePassword; userNamePasswordServiceBusCredential.Credentials.UserName.UserName = solutionName; userNamePasswordServiceBusCredential.Credentials.UserName.Password = azureSolutionCredential.Password; QueuePolicy queuePolicy = new QueuePolicy(); queuePolicy.ExpirationInstant = DateTime.UtcNow + TimeSpan.FromHours(1); QueueClient = QueueRenewalHelper<T>.GetOrCreateQueue<T>( userNamePasswordServiceBusCredential, queueUri, ref queuePolicy); } protected void PollingQueueData(object source, System.Timers.ElapsedEventArgs e) { if (null != QueueClient) { try { queueMessage = QueueClient.Retrieve(); } catch { } if (null != queueMessage) { T queueData = queueMessage.Value as T; . queue address is: • sb://solution.servicebus .windows. net/QueueName • http://solution.servicebus .windows. net/QueueName • https://solution.servicebus .windows. net/QueueName The queue name can be. Initialization() { string solutionName = ConfigurationManager.AppSettings["Solution"]; string queueName = ConfigurationManager.AppSettings["QueueName"]; AzureSolutionCredential. list shown in the following bullet points. The URI addresses should contain the phrase servicebus .windows. net. There is no limitation to the format for the .NET Service Bus queue, so the queue