.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 firewal
Trang 1203
</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>
Trang 2204
</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
Trang 3205
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-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
Trang 4206
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()
Trang 5207
{
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;