Solutions    |    Downloads    |    License    |    Documentation    |    Training    |    Support    |    Customers    |    About Us

Full Duplex Sample v.3

Last Updated: Feb 12, 2013 07:00PM IST

In order to see full-duplex, request/response communication, open the FullDuplex sample.

First of all run the solution - you should see two console applications start up. Find the client application by looking for the one with "Client" in its path and press 'Enter' a couple of times in the window. Your screen should look something like this:

Full Duplex sample running

Now let's go look at the code:

Full Duplex sample

Code Walk-Through

Let's start by looking at the Messages.cs file in the MyMessages project:

namespace MyMessages
{
    public class RequestDataMessage : IMessage
    {
        public Guid DataId { get; set; }
        public string String { get; set; }
    }

    public class DataResponseMessage : IMessage
    {
        public Guid DataId { get; set; }
        public string String { get; set; }
    }
}

The two classes we see here both implement the NServiceBus IMessage interface, indicating that they are messages. The only thing these classes have are properties, each with both get and set access. The RequestDataMessage is sent from the client to the server, and the DataResponseMessage is replied back from the server to the client.

If you open up the references of the MyClient and MyServer projects, you'll see that both reference the MyMessages project. This is quite common when building smaller systems. In larger systems, it is recommended to keep the messages project in a separate solution from both client and server which will reference a compiled DLL of the messages project instead.

Next, open up the EndpointConfig.cs file in the MyClient project. You should see this:

public class EndpointConfig : IConfigureThisEndpoint, AsA_Client {}

This code instructs the NServiceBus.Host.exe referenced by the MyClient project to configure the endpoint using the defaults for clients. For more information about the host process, see the "Built-in Configurations" section on this page.

Now open up the ClientEndpoint.cs file in the MyClient project. In there, you'll see a class that implements IWantToRunAtStartup. Implementers of this interface have their Run method invoked by NServiceBus when the endpoint starts up. Let's take a look at the Run method:

Console.WriteLine("Press 'Enter' to send a message.To exit, Ctrl + C");

while (Console.ReadLine() != null)
{
    var g = Guid.NewGuid();

    Console.WriteLine("Requesting to get data by id: {0}", g.ToString("N"));

    Bus.OutgoingHeaders["Test"] = g.ToString("N");

    Bus.Send<RequestDataMessage>(m =>
                                        {
                                            m.DataId = g;
                                            m.String = "<node>it's my \"node\" & i like it<node>";
                                        })
        .Register(i => Console.Out.WriteLine(
                            "Response with header 'Test' = {0}, 1 = {1}, 2 = {2}.",
                            Bus.CurrentMessageContext.Headers["Test"],
                            Bus.CurrentMessageContext.Headers["1"],
                            Bus.CurrentMessageContext.Headers["2"]));
}

This code performs the following action every time that the 'Enter' key is pressed:

A new Guid is created, and then set in the outgoing headers of the bus under the key "Test". All headers in the outgoing headers are appended to all messages sent from that point on. We'll see how to access these headers in the receiving code shortly.

Then the bus is used to send a RequestDataMessage whose DataId property is set to the same Guid, and whose String property is set to an XML fragment. Also, a callback is registered at the end that will be invoked when a response arrives to the request sent. In that callback we're writing to the console the values of several headers.

Now open the app.config file of the MyClient project, and take a look at the UnicastBusConfig section:

<UnicastBusConfig ForwardReceivedMessagesTo="audit">
    <MessageEndpointMappings>
      <add Messages="MyMessages" Endpoint="MyServer"/>
    </MessageEndpointMappings>
  </UnicastBusConfig>

What you can see here is that the bus is being given a mapping from message types to an endpoint address. In this case, all the classes the implement the IMessage interface from the assembly called MyMessages are being mapped to the endpoint called MyServer. This means that when we call Bus.Send<RequestDataMessage> in the client code, the bus knows that that message needs to be sent to the MyServer endpoint.

MyServer endpoint name (similar to the MyClient endpoint name) is determined by the NServiceBus framework or by the user.
In this sample, we did not explicitly change the endpoint name so NServiceBus framework, will do it for us using its standard convention.
Read here to see how to change the endpoint name.

This sample also shows how an audit queue may be used. In this case, the ForwardReceivedMessagesTo attributes, will instruct NServiceBus to Forward all successful received messages (on the client endpoint) to the audit endpoint.

When a RequestDataMessage arrives in the server queue, the bus dispatches it to the message handler found in the RequestDataMessageHandler.cs file in the MyServer project. The way that the bus knows which classes to call is based on the interface they implement:

public class RequestDataMessageHandler : IHandleMessages<RequestDataMessage>

As a part of the activities performed at start up by the bus, it scans all assemblies and builds a dictionary indicating which classes handle which messages. So when a given message arrives in a queue, the bus knows which class to invoke.

In the Handle method of this class, we see the following:

var response = Bus.CreateInstance<DataResponseMessage>(m => 
{ 
    m.DataId = message.DataId;
    m.String = message.String;
});

response.CopyHeaderFromRequest("Test");
response.SetHeader("1", "1");
response.SetHeader("2", "2");

Bus.Reply(response); //You can try experimenting with sending multiple
    replies

In here, the bus is used to create an instance of the DataResponseMessage class rather than just newing up the class. This technique is useful primarily when using interfaces for messages (rather than classes) as interfaces can't be instantiated directly. For more information on this, see the FAQ here.

Next, the header called "Test" that was set on the request is copied to the response, and a couple of additional headers are set on the response message. Finally, the bus is instructed to reply with the response message.

What happens is that the bus sends the response message to the queue specified as the InputQueue in the MsmqTransportConfig section, in the app.config of the MyClient endpoint.

If you'll look at the app.config in the MyServer project, you'll see that the UnicastBusConfig section doesn't have a mapping instructing the bus to send messages to "MyClientInputQueue". The way that the bus knows to where to send the responses is that every time the bus sends a message, it includes the queue from where the message was sent - so when the message was sent from the client, it already included the queue of the client.

When configuring the routing in the bus, you can continue under the premise of regular request/response communication that clients need to know where the server is, but servers don't need to know about clients.

Look back at ClientEndpoint.cs.

Now you can see that it's getting the header information from the handler on the server.

Also, open up DataResponseMessageHandler.cs in the MyClient project. In there you'll see a class whose signature looks similar to the message handler on the server:

class DataResponseMessageHandler : IHandleMessages<DataResponseMessage>

In NServiceBus, clients can also have message handlers - just like servers. This is useful for separating the concerns on the client. Logic that needs the context of the request should be put in the callback code, logic that doesn't need that context should be put in a separate message handler class.

Now let's look at how we can unit-test the server.

Open the Tests.cs file in the MyServer.Tests project - here's what you should see:

Test.Initialize();

var dataId = Guid.NewGuid();
var str = "hello";

Test.Handler<RequestDataMessageHandler>()
    .SetIncomingHeader("Test", "abc")
    .ExpectReply<DataResponseMessage>(m => m.DataId == dataId && m.String == str)
    .AssertOutgoingHeader("Test", "abc")
    .OnMessage<RequestDataMessage>(m => { m.DataId = dataId; m.String = str; });

Take a look at the references of the MyServer.Tests project - notice the NServiceBus.Testing reference in addition to the other NServiceBus assemblies. NServiceBus.Testing provides an additional API to simplify testing NServiceBus code.

For a walkthrough of the unit testing code, see the unit testing documentation.

About NServiceBus    |    Contact Us    |    Privacy    |    Follow us on:   
Copyright 2010-2013 NServiceBus. All rights reserved
support@nservicebus.com
http://assets2.desk.com/raca8b478c9bd89640c451013350d59caa6b66ee1/javascripts/
nservicebus
Loading
seconds ago
a minute ago
minutes ago
an hour ago
hours ago
a day ago
days ago
about
true
Invalid characters found
/customer/en/portal/articles/autocomplete
There was an error contacting Get Satisfaction
View All
0
discussions
replies
Questions
Ideas
Problems
Praise