Multi-Instance Mode

Component: SQL Server Transport | Nuget: NServiceBus.SqlServer (Version: 2.x)
Target NServiceBus Version: 5.x

Prerequisites

  1. Make sure SQL Server Express is installed and accessible as .\SQLEXPRESS.
  2. Create two databases called receivercatalog and sendercatalog.
  3. Make sure that Distributed Transaction Coordinator (DTC) is running. It can be started from the command line by running net start msdtc.

Running the project

  1. Start both projects.
  2. Hit <enter> in Sender's console window to send a new message.

Verifying that the sample works correctly

  1. The Receiver displays information that an order was submitted.
  2. The Sender displays information that the order was accepted.

Code walk-through

This sample contains the following projects:

  • Sender - A console application responsible for sending the initial ClientOrder message and processing the follow-up ClientOrderAccepted message.
  • Receiver - A console application responsible for processing the order message.
  • Messages - A class library containing message definitions.
In Versions 2 and below it is required to pass connection string for the currently configured endpoint either using ConnectionString() method or in the app.config file.

Sender project

The Sender does not store any data. It mimics the front-end system where orders are submitted by the users and passed via the bus to the back-end. It is configured to use SQL Server transport and run in the multi-instance mode. ConnectionProvider.GetConnection method is used for providing connections.

Edit
var busConfiguration = new BusConfiguration();
busConfiguration.EndpointName("Samples.SqlServer.MultiInstanceSender");
var transport = busConfiguration.UseTransport<SqlServerTransport>();
transport.UseSpecificConnectionInformation(ConnectionInfoProvider.GetConnection);
transport.ConnectionString(ConnectionInfoProvider.DefaultConnectionString);
busConfiguration.UseSerialization<JsonSerializer>();
busConfiguration.UsePersistence<InMemoryPersistence>();

The Sender sends a message to the Receiver:

Edit
var order = new ClientOrder
{
    OrderId = Guid.NewGuid()
};
bus.Send("Samples.SqlServer.MultiInstanceReceiver", order);

Receiver project

The Receiver mimics a back-end system. It is configured to use SQLServer transport in the multi-instance mode.

Edit
var busConfiguration = new BusConfiguration();
busConfiguration.EndpointName("Samples.SqlServer.MultiInstanceReceiver");
var transport = busConfiguration.UseTransport<SqlServerTransport>();
transport.UseSpecificConnectionInformation(ConnectionInfoProvider.GetConnection);
transport.ConnectionString(ConnectionInfoProvider.DefaultConnectionString);
busConfiguration.UseSerialization<JsonSerializer>();
busConfiguration.UsePersistence<InMemoryPersistence>();

It receives ClientOrder message sent by Sender and replies to them with ClientOrderAccepted.

Edit
public void Handle(ClientOrder message)
{
    log.Info($"Handling ClientOrder with ID {message.OrderId}");
    var clientOrderAccepted = new ClientOrderAccepted
    {
        OrderId = message.OrderId
    };
    bus.Reply(clientOrderAccepted);
}

Multi-instance connection lookup

Both sender and receiver provide a custom lookup mechanism for providing connection information for given destination. The following snippet shows lookup logic used by Sender.

Edit
static class ConnectionInfoProvider
{
    const string ReceiverConnectionString = @"Data Source=.\SqlExpress;Database=ReceiverCatalog;Integrated Security=True";
    public const string DefaultConnectionString = @"Data Source=.\SqlExpress;Database=SenderCatalog;Integrated Security=True";

    public static ConnectionInfo GetConnection(string transportAddress)
    {
        var connectionString = transportAddress.StartsWith("Samples.SqlServer.MultiInstanceReceiver")
            ? ReceiverConnectionString
            : DefaultConnectionString;

        return ConnectionInfo
                .Create()
                .UseConnectionString(connectionString);
    }
}

How it works

Sender and Receiver use different catalogs on the same SQL Server instance. The tables representing queues for a particular endpoint are created in the appropriate catalog, i.e. in the receivercatalog for the Receiver endpoint and in the sendercatalog for the Sender endpoint. It is possible to register a custom SqlConnection factory that provides connection instance per given transport address. The operations performed on queues stored in different catalogs are atomic because SQL Server allows multiple SqlConnection enlisting in a single distributed transaction.

In this sample DTC is required by the Receiver because it operates on two different catalogs when receiving Sender's request. It picks message from input queue stored in receivercatalog and sends back reply to Sender's input queue stored in sendercatalog. In addition error queue is stored also in sendercatalog so without DTC Receiver will not be able handle failed messages properly.

Related Articles


Last modified