download

Subscribe

When you subscribe to a certain type of message. your subscription is put into the publisher assemblies subscription queue. (if you use MsmqSubscriptionStorage()) By default when you call “LoadMessageHandlers()” your assembly automatically subscribes to all messages that your assembly has handlers for.

1 bus.Subscribe(typeof(NewEmployeeHired));

Publish

When you publish a message it gets sent to all subscribers of that message type or assembly that contains that message.

1 bus.Publish<NewEmployeeHired>(x =>
2     {
3         x.Id = employee.Id;
4         x.FirstName = message.first_name;
5         x.LastName = message.last_name;
6     });

Don’t publish from the web app

You should avoid doing this because it makes it difficult to scale out, and puts unnecessary load on your web server. Also, requests that come from the web don’t correspond to events have actually happened. They are still requests, until they are processed, then they become an event.

For example, a web tier might send the “ChangeEmployeeAddress” command to the app tier. The app tier then processes the command and changes the employees address, then publishes the “EmployeeAddressChanged” event. In this scenario, it wouldn’t have made sense to publish “ChangeEmployeeAddress” on to the bus. Event’s that happened should be published on the bus, not requests that we intend to process.

Send

when you send a message it is sent to a specific end point. You must tell what endpoint to send the messages to. In order to do this you must specify it in the app.config.

1 [AcceptVerbs(HttpVerbs.Post)]
2     public ActionResult new_hire(HireNewEmployee command)
3     {
4         bus.Send(command);
5         return RedirectToAction("all");
6     }

web.config

In the following configuration, we are telling nservice bus that whenever we send a message that from an assembly called ‘common.messages.dll’ it should get sent to the endpoint named ‘easyhr.service.queue’.

 1 <configuration>
 2       <configSections>
 3             <section name="MsmqTransportConfig" type="NServiceBus.Config.MsmqTransportConfig, NServiceBus.Core"/>
 4             <section name="UnicastBusConfig" type="NServiceBus.Config.UnicastBusConfig, NServiceBus.Core"/>
 5             <section name="RijndaelEncryptionServiceConfig" type="NServiceBus.Config.RijndaelEncryptionServiceConfig, NServiceBus.Core"/>
 6       </configSections>
 7       <MsmqTransportConfig InputQueue="easyhr.web.queue" ErrorQueue="error" NumberOfWorkerThreads="1" MaxRetries="5"/>
 8       <UnicastBusConfig>
 9         <MessageEndpointMappings>
10                 <add Messages="common.messages" Endpoint="easyhr.service.queue"/>
11                 <add Messages="easyhr.messages" Endpoint="easyhr.service.queue"/>
12                 <add Messages="it.messages" Endpoint="it.service.queue"/>
13             </MessageEndpointMappings>
14         </UnicastBusConfig>
15       <RijndaelEncryptionServiceConfig Key="gdDbqRpqdRbTs3mhdZh9qCaDaxJXl+e7"/>
16     </configuration>

Reply

When you reply to a message that was sent to you, the reply is sent to the endpoint that sent you the original message.

replying to a message

In the example below, our handler receives a message of type GetAllUserNamesQuery from some endpoint. When we respond by called “bus.Reply(x)” our reply gets sent back to the endpoint that contacted us in the first place. We do not need to know which endpoint contacted us, but the endpoint who contacted us, had to specify the location of our queue in their app.config.

 1 public class GetAllUsernamesMessageHandler : IHandleMessages<GetAllUserNamesQuery>
 2     {
 3         IBus bus;
 4         IUserRepository users;
 5         IMapper mapper;
 6 
 7         public GetAllUsernamesMessageHandler(IBus bus, IUserRepository users, IMapper mapper)
 8         {
 9             this.bus = bus;
10             this.mapper = mapper;
11             this.users = users;
12         }
13 
14         public void Handle(GetAllUserNamesQuery message)
15         {
16             users.FindAll().MapAllUsing<User, UserCredentials>(mapper).Each(x =>
17             {
18                 bus.Reply(x);
19             });
20         }
21     }

Hosting NService Bus

With NService bus you can either have your class library hosted by the NServiceBus.Host.exe or you can self host it. Using the NServiceBus.Host.exe allows you to deploy your class library as a windows service quite easily.

To debug and make use of the hosted solution: 1. Add a reference to NServiceBus.Host.exe to your class library. 2. Compile the library. 3. In the project properties for you class library go to the “Debug” tab and select the option that says “Start external program:” 4. Select the elipsis (…) and go to /bin/debug/NServiceBus.Host.exe for your class library.

example of how to leverage a hosted solution.

The NServiceBus.Host.exe will scan all assemblies in the same runtime directory as it looking for types that implement certain NServiceBus interfaces. This is how your configuration get picked up.

In the example below, we created a few classes that implement certain NServiceBus interfaces. By doing that, our configuration automatically gets picked up and is used to configure NServiceBus.

 1 public class ConfigureThisEndPoint : IConfigureThisEndpoint, AsA_Publisher, IWantCustomLogging
 2     {
 3         public void Init() {}
 4     }
 5 
 6     public class Initialize : IWantCustomInitialization
 7     {
 8         public void Init()
 9         {
10             var container = new WindsorContainer();
11             Configure.Instance.RijndaelEncryptionService();
12             Configure
13                 .With()
14                 .Log4Net()
15                 .CastleWindsorBuilder(container)
16                 .XmlSerializer()
17                 .RijndaelEncryptionService()
18                 .MsmqTransport().IsTransactional(true).PurgeOnStartup(true)
19                 .MsmqSubscriptionStorage()
20                 .UnicastBus().ImpersonateSender(true).LoadMessageHandlers()
21                 .CreateBus()
22                 .Start()
23                 ;
24         }
25     }

Self Hosting NService Bus

In a web application, you wont need to have your assembly hosted by anything because it is already hosted by ASP.NET. You will need to tell NServiceBus how your application should be configured. You can do this by configuring the application in Global.asax.

self hosted nservice bus example

 1 public class Global : HttpApplication
 2     {
 3         static public IBus Bus { get; private set; }
 4 
 5         protected void Application_Start()
 6         {
 7             AreaRegistration.RegisterAllAreas();
 8             RouteTable.Routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
 9             RouteTable.Routes.MapRoute( "Default", "{controller}/{action}/{id}", new {controller = "home", action = "index", id = UrlParameter.Optional} );
10 
11             Bootstrap();
12         }
13 
14         static void Bootstrap()
15         {
16             var container = new WindsorContainer();
17             ControllerBuilder.Current.SetControllerFactory(new WindsorControllerFactory(container));
18 
19             Bus = Configure.WithWeb()
20                 .Log4Net()
21                 .CastleWindsorBuilder(container)
22                 .XmlSerializer()
23                 .RijndaelEncryptionService()
24                 .MsmqTransport().IsTransactional(true).PurgeOnStartup(true)
25                 .MsmqSubscriptionStorage()
26                 .UnicastBus().ImpersonateSender(true).LoadMessageHandlers()
27                 .CreateBus()
28                 .Start();
29         }
30     }

In the above configuration, we are telling NService Bus that the application * is a web application (“WithWeb”), * we want to use CastleWindsor as our inversion of control container, (“CastleWindsorBuilder”) * we want our messages to be serialized/deserialized using an XmlSerializer, (“XmlSerializer”) * we want the WireEncryptedString’s to be encrypted and decrypted using the Rijndael encryption algorithm, (“RijndaelEncryptionService”) * we want our messaging communication to use MSMQ as the storage mechanism, and it should be transactional, (“MsmqTransport().IsTransactional(true)”) * we want all existing messages in the message queue to be purged at startup, (“PurgeOnStartup”) * we want to use MSMQ to manage client subscriptions. (“MsmqSubscriptionStorage”)

When we tell NServiceBus to “LoadMessageHandlers” this will scan all assemblies in the runtime directory for all classes that implement “IHandleMessages of T”, and register it in to the container. It will also subscribe to all messages of type T from the correct publisher out on the bus.

Things to Remember

When debugging it’s always important to make sure that your ‘Publishers’ are started first, then your ‘Subscribers’. The reason for this is, if your Subscriber starts up before the Publisher, then it has no one to subscribe to.

As long as you have handlers that implement IHandleMessages of T then your end point will automatically subscribe to the end point that publishes messages of T. in you app config you will need to configure each end point the publishes messages from a certain assembly.

I use a convention of having a messages assembly per service. so each message assembly will contain message types that is sent to the service or that it replies with, or that it publishes.

additional information

things that suck

out of the box you can’t decorate your IHandleMessages implementation with an Interceptor attribute because this creates a proxy of the handler, and when nservicebus tries to load the type it does it based on it's actual type not on whether the class is assignable to the type. so basically you can't use interceptors easily.

download

comments powered by Disqus