Question

I want to be able to send two different JSON messages on one queue. How do I, in C#, determine what type of message was received so that I can deserialize the message to the proper object? Should I use a message header or create another queue? A queue per message type seems excessive to me. Thanks!

Extra Details: I have a Windows service that processes "runs". A run ID is assigned by another system and the ID is dropped on a queue. My service picks up the ID and starts work. An object is created for each run. Right now, if I want to cancel work, I have to stop the service. But, that stops all work. I wanted to add a CancelRun type method, but all I need to the run ID. So, I could use the exact same JSON (so same class). Two queues wouldn't be horrible, but I thought it might be clever to add the type or something to a custom header.

Was it helpful?

Solution

Here's what I went with. I like this technique because I'm not adding stuff to the JSON that isn't part of the model.

IBasicProperties props = model.CreateBasicProperties();
props.Headers = new Dictionary<string, object>();
props.Headers.Add("RequestType", "CancelRunRequest");

Then, on the receiving side, I do this (I'm raising an event with a custom EventArg obj):

// Raise message received event
var args = new MessageReceivedArgs();
args.CorrelationId = response.BasicProperties.CorrelationId;
args.Message = Encoding.UTF8.GetString(response.Body);
args.Exchange = response.Exchange;
args.RoutingKey = response.RoutingKey;

if (response.BasicProperties.Headers != null && response.BasicProperties.Headers.ContainsKey("RequestType"))
{
args.RequestType = Encoding.UTF8.GetString((byte[])response.BasicProperties.Headers["RequestType"]);
}

MessageReceived(this, args);
model.BasicAck(response.DeliveryTag, false);

Elsewhere in the project:

private void NewRunIdReceived(object p, MessageReceivedArgs e)
{

if(e.RequestType.ToUpper() == "CANCELRUNREQUEST")
{
    // This is a cancellation request
    CancelRun(e);
}
else
{
    // Default to startrun request for backwards compatibility.
    StartRun(e);
}
}

OTHER TIPS

If an order or receiving and processing of these messages is not an issue I would like to suggest using separate queues for each type, it's not a problem for rabbit to handle tons of queues. If not, the order is crucial for you, you can put marker in header of the message defining it's type, however this will bind your Business Logic with transportation layer. In case you will want to change the transportation layer later in your application, you will have to adopt this section of code to keep it work. Instead of this you can make some sort of wrapper for both of those object types which hides the internal content, looks the same and can desalinize itself in type it contains.

Adding runtime type information as suggested by sphair works, but I dislike doing that since you loose the portability value of having a weak type. You might also consider having the deserialized C# object be generic enough to handle all the flavors, and then branch from there.

You can add all of them into array and then deserialise based on type, you will have to add type property manually, or you could create Object IHaveType and then inherit from it in objects that are being used, but that's a horrible way to do this.

ObjectType1 : HaveType 

public class HaveType { public string Type { get { this.GetType(); }}}

Json

[{Type: 'ObjectType1', ...[other object stuff]},{Type : 'ObjectType2',...}]

JSON does not say what a type it was serialized from, unless the serializer itself adds some information to the JSON.

You could add some structure to the JSON so you are able to deduct the type.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top