Using NServiceBus and ServiceBroker.net – Part 2
Posted by Jens Pettersson on December 7th, 2010In the previous post I showed how to set up the SQL Server Service Broker and in this post I will use a simple NServiceBus sample to show how to get the messages from the Service Broker, using ServiceBroker.net.
You can find the NServiceBus transport for ServiceBroker.net in the official NServiceBus-contrib at github.
If you want to look at the code while reading you can get it at my github repository for this sample application.
Configuring NServiceBus and ServiceBroker.net
Begin by creating a new NServiceBus sample application. Look in the official samples included with NServiceBus if you need help with this. Beyond the references to NServiceBus you need to add references to the following dll’s:
- ServiceBroker.Net.dll
- NServiceBus.Unicast.Transport.ServiceBroker.dll
The first thing we want to do is to create our own Transport Factory. We do this to create a “bridge” between the SQL Server Service Broker and our MSMQ transport which we will use later to publish other events. You don’t have to use MSMQ at all, and only stick to the SSSB transport, but in our scenario we will use MSMQ as our main transport.
Create a new interfaced (I called mine IChinookTransportFactory) which has one method, GetTransport.
public interface IChinookTransportFactory
{
ITransport GetTransport();
}
It’s the GetTransport method that will return the ServiceBrokerTransport for NServiceBus to use. Create a class implementing your newly created interface:
public class ChinookTransportFactory : IChinookTransportFactory
{
private readonly string _connectionString;
private readonly IMessageSerializer _messageSerializer;
public ChinookTransportFactory(string connectionString, IMessageSerializer messageSerializer)
{
_connectionString = connectionString;
_messageSerializer = messageSerializer;
}
public ITransport GetTransport()
{
return new ServiceBrokerTransport
{
InputQueue = "ChinookEventServiceQueue",
ErrorService = "ErrorService",
ReturnService = "ChinookEventService",
NumberOfWorkerThreads = 1,
MessageSerializer = _messageSerializer,
MaxRetries = 2,
ConnectionString = _connectionString
};
}
}
In the GetTransport method you specify what Service Broker Services to use (the ones we created in the previous post) along with some other settings.
Notice that the Transport Factory takes a connection string as a parameter. We will have to tell our NServiceBus service which connection string to use. We do this in our StructureMap bootstrapper:
public class Bootstrapper : IWantCustomInitialization
{
public void Init()
{
ObjectFactory.Configure(x =>
{
x.For<IChinookTransportFactory>().Use<ChinookTransportFactory>()
.Ctor<string>()
.EqualToAppSetting("connectionString");
});
}
}
The connection string itself is set in our app.config:
<appSettings> <add key="connectionString" value="Server=localhost\SQLEXPRESS;Database=Chinook;Integrated Security=SSPI;" /> </appSettings>
We now have most of the “back bone” done and the next thing to do is to create our event message in our application and then create something that actually picks up the messages from the Service Broker and hands them over to NServiceBus.
When creating our event message we need to make sure we match the namespace we defined in our store procedure in the last post. We used the following:
<Messages xmlns="http://tempuri.net/ServiceBrokerNetSample.Events">
As my sample application is called ServiceBrokerNetSample, what we’ll do is creating a new folder called “Events” in our application root. In that folder, create a new class called CustomerEmailChangedEvent that implements IMessage:
namespace ServiceBrokerNetSample.Events
{
public class CustomerEmailChangedEvent : IMessage
{
public int CustomerId { get; set; }
public string PreviousEmailAddress { get; set; }
public string NewEmailAddress { get; set; }
}
}
First, make sure your event is called the same thing as what we named the MessageName variable in our trigger we created in the previous post. The properties must also be named the same thing as we named the xml elements in the trigger:
SET @CustomerIdXml = (select @CustomerId for xml path('CustomerId'))
SET @PreviousEmailAddressXml = (select @OldEmail for xml path ('PreviousEmailAddress'))
SET @NewEmailAddressXml = (select @NewEmail for xml path ('NewEmailAddress'))
This will make sure that the NServiceBus XmlSerializer correctly serialize the xml to our CustomerEmailChangedEvent.
Now that we have our message event let’s create a class called ChinookNotificationForwarder that implements IWantToRunAtStartup:
public class ChinookNotificationForwarder : IWantToRunAtStartup
{
private readonly IBus _bus;
private readonly ITransport _transport;
public ChinookNotificationForwarder(IBus bus, IChinookTransportFactory transportFactory)
{
_bus = bus;
_transport = transportFactory.GetTransport();
_transport.TransportMessageReceived += TransportMessageReceived;
}
void TransportMessageReceived(object sender, TransportMessageReceivedEventArgs e)
{
var message = e.Message.Body[0];
_bus.SendLocal(message);
}
public void Run()
{
_transport.Start();
}
public void Stop()
{
_transport.Dispose();
}
}
This class has a dependency on the bus and on our Transport Factory we created earlier. In the constructor we use this Transport Factory to create our ITransport which in this case will be the ServiceBrokerTransport. Then we wire up an event handler for when a transport message is received on that transport and in that event handler we get our event message from the event args.
How do we know that our message is at position 0 in e.Message.Body? Well, in the previous post, where we created the store procedure, we defined our Transport Message as:
<TransportMessage><Body><
Note! If you named your event message in your application to something else than what it’s named in the trigger you most probably will get the following exception:
Could not extract message data. System.TypeLoadException: Could not handle type…
Your message will then end up in the ErrorServiceQueue in the SQL Server Service Broker.
The last thing we do in the event handler is telling the bus to do a SendLocal on our message. From here it’s just regular NServiceBus stuff going on, but for the record (and for the sake of fulfilling our scenario mentioned in the previous post), I’ll show the event handler that handles the CustomerEmailChangedEvent:
namespace ServiceBrokerNetSample.EventHandlers
{
public class CustomerEmailChangedHandler : IHandleMessages<CustomerEmailChangedEvent>
{
private readonly IBus _bus;
public CustomerEmailChangedHandler(IBus bus)
{
_bus = bus;
}
public void Handle(CustomerEmailChangedEvent message)
{
//Do what you want to do... We will just publish an event for anyone interested.
_bus.Publish<ChinookCustomerEmailChangedEvent>(x =>
{
x.CustomerId = message.CustomerId;
x.NewEmailAddress = message.NewEmailAddress;
});
}
}
}
All this event handler does is publish an event for the rest of the world to use in whatever way they want.
That’s it! We now have a working sample application that uses ServiceBroker.net to fetch messages from the SQL Server Service Broker and hands it over to NServiceBus.
To try it out, run your application and set a debug point in your notification forwarder and in your handler, and then do an update on an email address in the Customer table.
There are quite a lot of “magic strings” floating around in our Store Procedure and Triggers that have to match our class names and properties, but once you’ve set this up it’s pretty easy to add more functionality.
I’ve added the sample application to a github repository. It also includes all the SQL scripts needed to set up the SQL Server Service Broker.
If you have any questions, feel free to post a comment here or contact me.
Twitter: @jens_pettersson
//J


Recent Comments