Monday, September 1, 2014

Publishing and Subscribing using NServiceBus

NServiceBus is one of the widely used service bus for .NET. I have been spending some quality time learning NServiceBus and I realized there is not much resources for learning NServiceBus. Though there is a documentation, it seems to be still evolving over time and not all is still documented. So thought of writing this post as someone might find it helpful.

In this post let’s see how we can write a application where some application publishes events and another application consumes those.

I will start  off with the application by creating a blank solution named “NServiceBusPublishSubscribeSample”.

image
Adding a blank solution
For this solution, I am adding three class library projects named “NServiceBusPublishSubscribeSample.Publisher”, “NServiceBusPublishSubscribeSample.Subscriber” and “NServiceBusPublishSubscribeSample.Messages”. I am deleting default Class1.cs from all the three projects. Now I am running NServiceBus.Host nuget package on “NServiceBusPublishSubscribeSample.Publisher” and “NServiceBusPublishSubscribeSample.Subscriber” projects using Package Manager Console.

Install on Publisher

image
Installing NServiceBus.Host on publisher project.

Install on Subscriber

image
Installing NServiceBus.Host on subscriber project.
Once Install on Package Manager Console completed installing all the required files, you can see in both “NServiceBusPublishSubscribeSample.Publisher” and “NServiceBusPublishSubscribeSample.Subscriber” projects, there is a new class added named “EndpointConfig”.

Now let’s move into Publisher Project. I am modifying the “EndpointConfig” class as follows.
public class EndpointConfig : IConfigureThisEndpoint, AsA_Publisher, IWantCustomInitialization
{
    public void Init()
    { 

    }
}
I have changed the AsA_Server into AsA_Publisher. In addition I am implementing IWantCustomInitialization interface on my “EndpointConfig” and it is giving me a method to implement named “Init()”. For now, I will keep it as it is.

Now I am adding a new class there and I am naming it as “MyTask”. I am implementing an interface named IWantToRunWhenBusStartsAndStops on “MyTask”.
public class MyTask : IWantToRunWhenBusStartsAndStops
{
    public IBus Bus { get; set; }

    public void Start()
    {
 
    }
 
    public void Stop()
    {

    }
}
Now here in “Start()”, I am planning on publishing some messages which the subscribed clients can consume. For that between the Publisher and Subscriber, I need to share messages. Those messages, I am going to put in Messages Project. I am adding new class named “MyMessage” inside Messages project.

There I have a single property named “Message” of type string.
public class MyMessage 
{
    public string Message { get; set; }
}

Then I am adding references from both Publisher and Subscriber to Messages project.

Now moving back to Publisher project, I am modifying the “Start()” method in “MyTask” as follows.
public class MyTask : IWantToRunWhenBusStartsAndStops
{
    public IBus Bus { get; set; }

    public void Start()
    {
        Bus.Publish(new MyMessage()
        {
            Message = "Hello World!"
        });

        Console.WriteLine("Published a message.");
    }
 
    public void Stop()
    {

    }
}
Now inside my Subscriber project, I am modifying the “EndpointConfig” as I did in Publisher project.
public class EndpointConfig : IConfigureThisEndpoint, AsA_Server, IWantCustomInitialization
{
    public void Init()
    {
 
    }
}
I am keeping AsA_Server as it is and in this class also I am implementing IWantCustomInitialization interface and I am keeping the “Init()” as it is.

Now I am adding a class named “MessageSubscriber”. I am implementing an interface named “IHandleMessages<T>”. In this case it’s “IHandleMessages<MyMessage>”.
public class MessageSubscriber : IHandleMessages<MyMessage>
{
    public void Handle(MyMessage message)
    {
        Console.WriteLine(message.Message);
    }
}
Now I am all set. Now what I need to do is configuring the end points of publisher and subscriber. For that let’s modify the “Init()” methos in both “EndpointConfig” classes in both Publisher and Subscriber projects.

EndpointConfig in Publisher
namespace NServiceBusPublishSubscribeSample.Publisher
{
    using NServiceBus;

    public class EndpointConfig : IConfigureThisEndpoint, AsA_Publisher, IWantCustomInitialization
    {
        public void Init()
        {
            Configure
                .With()
                .DefaultBuilder()
                .DefiningEventsAs(t => t.Namespace != null && t.Namespace.StartsWith("NServiceBusPublishSubscribeSample.Messages"));
        }
    }
}

EndpointConfig in Subscriber
namespace NServiceBusPublishSubscribeSample.Subscriber
{
    using NServiceBus;

    public class EndpointConfig : IConfigureThisEndpoint, AsA_Server, IWantCustomInitialization
    {
        public void Init()
        {
            Configure
                .With()
                .DefaultBuilder()
                .DefiningEventsAs(t => t.Namespace != null && t.Namespace.StartsWith("NServiceBusPublishSubscribeSample.Messages"));
        }
    }
}
Here I have configured both end points with Fluent API. Once this is done, there is only one part left to get this up and running. Still my Subscriber doesn’t know where to listen for. For that we need to modify the <UnicastBusConfig /> section in Subscriber projects’ app.config file as follows.
<UnicastBusConfig>
    <MessageEndpointMappings>
        <add Assembly="NServiceBusPublishSubscribeSample.Messages" Endpoint="NServiceBusPublishSubscribeSample.Publisher" />
    </MessageEndpointMappings>
</UnicastBusConfig>
Now since I need to run multiple projects, I am selecting multiple startup projects.

image
Setting multiple startup projects
Now when I run the project, I am getting the following.
SNAGHTML1c5dbc74
Output
Can you imagine, if it hadn't been for NServiceBus, how much of coding we will need to write  implement such a scenario.

I am uploading the full sample to my OneDrive.


Happy Coding.

Regards,
Jaliya