4
votes

I am new at messaging architectures, so I might be going at this the wrong way. But I wanted to introduce NServiceBus slowly in my team by solving a tiny problem.

Appointments in Agenda's have states. Two users could be looking at the same appointment in the same agenda, in the same application. They start this application via a Remote session on a central server. So if user 1 updates the state of the appointment, I'd like user 2 to see the new state 'real time'.

To simulate this or make a proof of concept if you will, I made a new console application. Via NuGet I got both NServiceBus and NServiceBus.Host, because as I understood from the documentation I need both. And I know in production code it is not recommended to put everything in the same assembly, but the publisher and subscriber will most likely end up in the same assembly though...

In class Program method Main I wrote the following code:

BusConfiguration configuration = new BusConfiguration();

configuration.UsePersistence<InMemoryPersistence>();
configuration.UseSerialization<XmlSerializer>();
configuration.UseTransport<MsmqTransport>();
configuration.TimeToWaitBeforeTriggeringCriticalErrorOnTimeoutOutages(new TimeSpan(1, 0, 0));
ConventionsBuilder conventions = configuration.Conventions();
conventions.DefiningEventsAs(t => t.Namespace != null
    && t.Namespace.Contains("Events"));

using (IStartableBus bus = Bus.Create(configuration))
{
    bus.Start();

    Console.WriteLine("Press key");
    Console.ReadKey();

    bus.Publish<Events.AppointmentStateChanged>(a =>
    {
        a.AppointmentID = 1;
        a.NewState = "New state";
    });

    Console.WriteLine("Event published.");
    Console.ReadKey();
}

In class EndPointConfig method Customize I added:

configuration.UsePersistence<InMemoryPersistence>();
configuration.UseSerialization<XmlSerializer>();
configuration.UseTransport<MsmqTransport>();
ConventionsBuilder conventions = configuration.Conventions();
conventions.DefiningEventsAs(t => t.Namespace != null
    && t.Namespace.Contains("Events"));

AppointmentStateChanged is a simple class in the Events folder like so:

public class AppointmentStateChanged: IEvent {
    public int AppointmentID { get; set; }
    public string NewState { get; set; }
}

AppointmentStateChangedHandler is the event handler:

public class AppointmentStateChangedHandler : IHandleMessages<Events.AppointmentStateChanged> {
public void Handle(Events.AppointmentStateChanged message) {
        Console.WriteLine("AppointmentID: {0}, changed to state: {1}", 
            message.AppointmentID, 
            message.NewState);
    }
}

If I start up one console app everything works fine. I see the handler handle the event. But if I try to start up a second console app it crashes with: System.Messaging.MessageQueueException (Timeout for the requested operation has expired). So i must be doing something wrong and makes me second guess that I don't understand something on a higher level. Could anyone point me in the right direction please?

Update Everthing is in the namespace AgendaUpdates, except for the event class which is in the AgendaUpdates.Events namespace.

Update 2 Steps taken:

  • Copied AgendaUpdates solution (to AgendaUpdates2 folder)
  • In the copy I changed MessageEndpointMappings in App.Config the EndPoint attribute to "AgendaUpdates2" I got MSMQ exception: "the queue does not exist or you do not have sufficient permissions to perform the operation"
  • In the copy I added this line of code to EndPointConfig: configuration.EndpointName("AgendaUpdates2"); I got MSMQ exception: "the queue does not exist or you do not have sufficient permissions to perform the operation"
  • In the copy I added this line of code to the Main methodin the Program class: configuration.EndpointName("AgendaUpdates2"); Got original exception again after pressing key.

--> I tested it by starting 2 visual studio's with the original and the copied solution. And then start both console apps in the IDE.

2

2 Answers

3
votes

I'm not exactly sure why you are getting that specific exception, but I can explain why what you are trying to do fails. The problem is not having publisher and subscriber in the same application (this is possible and can be useful); the problem is that you are running two instances of the same application on the same machine.

NServiceBus relies on queuing technology (MSMQ in your case), and for everything to work properly each application needs to have its own unique queue. When you fire up two identical instances, both are trying to share the same queue.

There are a few things you can tinker with to get your scenario to work and to better understand how the queuing works:

  1. Change the EndPointName of your second instance
  2. Run the second instance on a separate machine
  3. Separate the publisher and subscriber into separate processes

Regardless of which way you go, you will need to adjust your MessageEndpointMappings (on the consumer/subscriber) to reflect where the host/publisher queue lives (the "owner" of the message type):

http://docs.particular.net/nservicebus/messaging/message-owner#configuring-endpoint-mapping

Edit based on your updates

I know this is a test setup/proof of concept, but it's still useful to think of these two deployments (of the same code) as publisher/host and subscriber/client. So let's call the original the host and the copy the client. I assume you don't want to have each subscribe to the other (at least for this basic test).

Also, make sure you are running both IDEs as Administrator on your machine. I'm not sure if this is interfering or not.

In the copy I changed MessageEndpointMappings in App.Config the EndPoint attribute to "AgendaUpdates2" I got MSMQ exception: "the queue does not exist or you do not have sufficient permissions to perform the operation"

Since the copy is the client, you want to point its mapping to the host. So this should be "AgendaUpdates" (omit the "2").

In the copy I added this line of code to EndPointConfig: configuration.EndpointName("AgendaUpdates2"); I got MSMQ exception: "the queue does not exist or you do not have sufficient permissions to perform the operation"

In the copy I added this line of code to the Main methodin the Program class: configuration.EndpointName("AgendaUpdates2"); Got original exception again after pressing key

I did not originally notice this, but you don't need to configure the endpoint twice. I believe your EndPointConfig is not getting called, as it is only used when hosting via the NSB host executable. You can likely just delete this class.

This otherwise sounds reasonable, but remember that your copy should not be publishing if its the subscriber, so don't press any keys after it starts (only press keys in the original).

1
votes

If you want to publisher to also be the receiver of the message, you want to specify this in configuration.

This is clearly explained in this article, where the solution to your problem is completely at the end of the article.