Using NServiceBus and ServiceBroker.net – Part 2

Posted by Jens Pettersson on December 7th, 2010

In 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><![CDATA[<Messages xmlns="http://tempuri.net/ServiceBrokerNetSample.Events"><'+@MessageName+'>...

And then in our trigger, we passed in the MessageName to the store procedure. This gives us the following in our event handler:

image

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

Using NServiceBus and ServiceBroker.net – Part 1

Posted by Jens Pettersson on December 6th, 2010

This will be (at least) a two part series on how to use NServiceBus and ServiceBroker.net, where I walk you through how we (Frontwalker) used those techniques in a recent project. The first part will be how to set up the SQL Server Service Broker and the next part I’ll use the ServiceBroker.net and NServiceBus.

ServiceBroker.net was created by Joseph Daigle and it’s a simple wrapper API for the SQL Service Broker. It also has a transport for NServiceBus that uses the SSSB. This transport is now part of the official NServiceBus-contrib over at github.

Our scenario was:

The client had a rather old application and they wanted that some of the changes made to that applications underlying data would be processed and sent to another application. In other words, pretty simple, one-way, integration. The old application didn’t publish any events when the client did something, it just inserted/updated/deleted rows in a SQL Server 2005 database. Just for the record, the application itself was a-not-so-task-based MS Access application…

To solve this we had to listen to changes in the columns we were interested in and then notify our integration service to take action. We are using NServiceBus in our projects and decided to use the ServiceBroker.net to solve this scenario. In this series I will be using the Chinook database and a simple NServiceBus sample to illustrate how we did this.

Setting up the Service Broker in SQL Server

First, make sure that the Service Broker is enabled on your database. Right click your database in Microsoft SQL Server Management Studio and select Properties –> Options. Set “Broker Enabled” to True.

image

The next thing we need to do is to define a message type and a message contract that the Service Broker will use. We do that by running the following SQL:

CREATE MESSAGE TYPE NServiceBusTransportMessage
    VALIDATION = NONE ;
GO

CREATE CONTRACT NServiceBusTransportMessageContract
    ( NServiceBusTransportMessage SENT BY ANY);
GO

After that, we create our service queue, our service and also an error service queue and an error service.

CREATE QUEUE [dbo].[ChinookEventServiceQueue];
GO

CREATE SERVICE ChinookEventService
    ON QUEUE [dbo].[ChinookEventServiceQueue]
    (NServiceBusTransportMessageContract);
GO

-- Error service
CREATE QUEUE [dbo].[ErrorServiceQueue];
GO

CREATE SERVICE ErrorService
    ON QUEUE [dbo].[ErrorServiceQueue]
    (NServiceBusTransportMessageContract);
GO

Run these scripts and refresh your database and you should see the following:

image

It’s in the ChinookEventServiceQueue all your messages from your SQL server will end up before being picked up by ServiceBroker.net and NServiceBus.

Now we need something in our database to actually send messages to our newly created service queue. For this, we will use a Store Procedure. You can copy and paste this from below but make sure the @MessageContract and the @MessageType variables have the same name as the message type and message contract you created earlier.

Another very important part is that the @TransportMessage variable get the correct xmlns set (the <Messages xmlns=”http://tempuri.net/Namespace.of_your.Events”>). The xmlns need to be set to tempuri.net/ and then the correct namespace of where your events resides. (If you don’t know what your namespace will be, just alter this store procedure later).

Note! The http://tempuri.net/Namespace.of_your.Events is the default namespace of NServiceBus XmlSerializer. You can change this when configuring NServiceBus.

CREATE PROCEDURE [dbo].[SendNServiceBusMessage]
 @TargetService NVARCHAR(200),
 @MessageName NVARCHAR(200),
 @MessageContent NVARCHAR(4000)
AS

BEGIN
-- Sending a Service Broker Message
DECLARE @InitDlgHandle UNIQUEIDENTIFIER;
DECLARE @MessageContract NVARCHAR(200);
DECLARE @MessageType NVARCHAR(200);
DECLARE @TransportMessage NVARCHAR(4000);

SET NOCOUNT ON

SET  @MessageContract = 'NServiceBusTransportMessageContract';
SET  @MessageType = 'NServiceBusTransportMessage';

BEGIN TRANSACTION;

 BEGIN DIALOG @InitDlgHandle
   FROM SERVICE @TargetService
   TO SERVICE @TargetService
   ON CONTRACT @MessageContract
   WITH ENCRYPTION = OFF;

SET @TransportMessage ='<TransportMessage><Body><![CDATA[<Messages xmlns="http://tempuri.net/ServiceBrokerNetSample.Events"><'+@MessageName+'>' +
    @MessageContent +'</'+@MessageName+'></Messages>]]></Body></TransportMessage>';

 SEND ON CONVERSATION @InitDlgHandle
   MESSAGE TYPE @MessageType
    (@TransportMessage);

COMMIT TRANSACTION;

END

GO

Now we need something to actually use this Store Procedure to create messages for our service queue. This is the scary part, we will need to use a trigger on the table we’re interested in!

Now you have a choice. You can either create one trigger per change you’re interested in or create one “global” trigger that listens to all changes to a given table and then use an event handler in NServiceBus to later decide what was changed. We used the latter in our project, but in this sample I’m just going to listen for changes in one column.

Let’s say we have the following scenario for this example:

When a customer changes his email address, we want to notify some other service…

The Chinook database have a table named “Customer” and it contains a column named “Email”. Let’s create the trigger that listens for changes to that column.

Here’s the SQL for creating the trigger:

CREATE TRIGGER [dbo].[TRG_EmailChanged]
   ON  [dbo].[Customer]
   FOR UPDATE
AS
IF UPDATE(Email)
BEGIN
	SET NOCOUNT ON;

	DECLARE @MESSAGENAME NVARCHAR(255)

	-- This is what event your application will use later
	SET @MESSAGENAME = 'CustomerEmailChangedEvent'

	DECLARE @CustomerId int
	DECLARE @OldEmail NVARCHAR(60)
	DECLARE @NewEmail NVARCHAR(60)

	SELECT @OldEmail = Email FROM deleted
	SELECT @NewEmail = Email, @CustomerId = CustomerId FROM inserted

	IF @OldEmail <> @NewEmail
	BEGIN

		DECLARE @CustomerIdXml XML
		DECLARE @PreviousEmailAddressXml XML
		DECLARE @NewEmailAddressXml XML
		DECLARE @XmlText NVARCHAR(MAX)

		-- I'm using select for xml because otherwise special characters might cause problems
		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 is the text that we will use in our message
		SET @XmlText = cast(@CustomerIdXml as NVARCHAR(MAX))
			+ cast(@PreviousEmailAddressXml as NVARCHAR(MAX))
			+ cast(@NewEmailAddressXml as NVARCHAR(MAX))

		exec SendNServiceBusMessage
			@TargetService = N'ChinookEventService',
			@MessageName = @MESSAGENAME,
			@MessageContent = @XmlText;
	END
END
GO

Updated the trigger and added CustomerId too. Kind of useless to send an event like this without saying wich customer it affects…

I’m by no means a SQL expert, so I’ve kept the trigger as simple as possible. Basically, what’s going on is that I check if the old value differs from the new value and if so I create a message body in XML and pass that and the name of the message to the store procedure we created earlier.

Now we have a trigger that, if a customers email address is changed, creates a message and hands that to the Service Broker. You can try it out by running a simple update on a customer:

update Customer set Email = 'jens.pettersson@local.loc' where CustomerId = 60

Now, under your Service Broker > Queues, right click the ChinookEventServiceQueue and select “Select top 1000 rows” and you should see one row. Look in the last column, called “casted_message_body”. There you should see the XML your trigger passed on to your store procedure and more that the sp added for this message to work with NServiceBus.

image

That’s it for the first part! It’s quite a lot of scary SQL stuff going on, but when you’ve set this up you really only need to worry about new triggers later on.

In the next part, I will build a simple NServiceBus sample that actually uses the ServiceBroker.net. This post didn’t really say anything about that or NServiceBus, but this stuff is needed to get something up and running.

//J


Copyright © 2010 NullReference. Web hosting.