Message Queues with different message types
Asked Answered
F

4

10

I'm investigating Microsoft Message Queues for doing inter-process cross-network messaging. But when I receive a message, I don't know a priori what type of object I'm getting, so the code

queue.Formatter = new XmlMessageFormatter(new Type[] { typeof(Wibble) });

can't be applied before I get the message because I don't know if it's a Wibble. So how do I receive different message types?

Fortnight answered 29/5, 2012 at 11:55 Comment(2)
Two options spring to mind; 1) have one queue for Wibbles and one queue for Gizmos; 2) try to coalesce to a Wibble, if that fails then try to cast to a GizmoInterdependent
typeof(Object) and reflect classes onto the object until it fits?Dragonhead
A
3

You might consider not storing your object in the MSMQ message, but instead putting a reference to it's persistent location if you can. MSMQ has finite space on the message queues, so smaller messages are best.

If you can't do that, you can serialize your object to the messages BodyStream directly, using whatever serializer you like. Then store the type name as well, probably best in the message Label.

Something very similar to this (scratched it out here, no IDE on this computer) to put it in, and the analagous action on the way out:

public void FormatObject(object toFormat, Message message)
{
    var serializer = new XmlSerializer(toFormat.GetType());
    var stream = new MemoryStream();
    serializer.Serialize(toFormat, stream);

    //don't dispose the stream
    message.BodyStream = stream;
    message.Label = toFormat.GetType().AssemblyQualifiedName;
}
Aleishaalejandra answered 29/5, 2012 at 12:18 Comment(7)
I did something similar with RabbitMQ. I used a message header to indicate the type of the object serialized in the message body.Dominations
@tallseth: At the receiver end, how do we do it? Do we compare label with typeof all classes that we have? That can be multipleAchlorhydria
Regarding the comment //don't dispose the stream. When does the stream get disposed then?Semivowel
not exactly @RajeshMishra. You have a full type name, so just create your serializer using it. Ex var serializer = new XmlSerializer(message.Label)Aleishaalejandra
@Semivowel It's been 5 years since I looked at this, and don't recall exactly. :) I think it must be that MSMQ wants to poke at it when you send the message. But you can try with it taken out and see what happens. If I'm off base I'l edit the answer.Aleishaalejandra
@Aleishaalejandra Nope, you're definitely right. You get an exception when sending a message if you don't. I just like to know that my objects are disposed.Semivowel
-1 as this is building a solution that duplicates what is already built in. It's just complex unnecessary code. see Damien's answer.Samson
O
12

You're already using the constructor overload for XmlMessageFormatter that accepts an array of types. So just add all of the types that you're expecting to receive into that array, rather than just one type.

queue.Formatter = new XmlMessageFormatter(new Type[] {
    typeof(Wibble),
    typeof(Fleem),
    typeof(Boo)
});

From TargetTypes:

The instance serialized in the message body must comply with one of the schemas represented in the type array. When you read the message using the Receive method, the method creates an object of the type that corresponds to the schema identified and reads the message body into it.

(Emphasis added)

Orvalorvan answered 29/5, 2012 at 12:18 Comment(1)
The docs gives this example: MessageQueue queue = new MessageQueue(queuePath); ((XmlMessageFormatter)queue.Formatter).TargetTypeNames = new string[] {“Order"}; However, this gives an exception that the type Order could not be found in System.Messaging name space. Since the queue has a default XMLMessageFormatter if this code works we would not need to construct another formatter. So the interest.Myca
A
3

You might consider not storing your object in the MSMQ message, but instead putting a reference to it's persistent location if you can. MSMQ has finite space on the message queues, so smaller messages are best.

If you can't do that, you can serialize your object to the messages BodyStream directly, using whatever serializer you like. Then store the type name as well, probably best in the message Label.

Something very similar to this (scratched it out here, no IDE on this computer) to put it in, and the analagous action on the way out:

public void FormatObject(object toFormat, Message message)
{
    var serializer = new XmlSerializer(toFormat.GetType());
    var stream = new MemoryStream();
    serializer.Serialize(toFormat, stream);

    //don't dispose the stream
    message.BodyStream = stream;
    message.Label = toFormat.GetType().AssemblyQualifiedName;
}
Aleishaalejandra answered 29/5, 2012 at 12:18 Comment(7)
I did something similar with RabbitMQ. I used a message header to indicate the type of the object serialized in the message body.Dominations
@tallseth: At the receiver end, how do we do it? Do we compare label with typeof all classes that we have? That can be multipleAchlorhydria
Regarding the comment //don't dispose the stream. When does the stream get disposed then?Semivowel
not exactly @RajeshMishra. You have a full type name, so just create your serializer using it. Ex var serializer = new XmlSerializer(message.Label)Aleishaalejandra
@Semivowel It's been 5 years since I looked at this, and don't recall exactly. :) I think it must be that MSMQ wants to poke at it when you send the message. But you can try with it taken out and see what happens. If I'm off base I'l edit the answer.Aleishaalejandra
@Aleishaalejandra Nope, you're definitely right. You get an exception when sending a message if you don't. I just like to know that my objects are disposed.Semivowel
-1 as this is building a solution that duplicates what is already built in. It's just complex unnecessary code. see Damien's answer.Samson
D
1

There is a great amount of misinformation running around on MSMQ primarily because the Microsoft documentation is frighteningly sparse on how to design a message send receive properly. I have both of the MSMQ books published on this subject and I'm still searching for sensible designs on the internet.

So, neither of these references say that there is a one message type to a queue requirement. And that would make PeakMessage and variants unnecessary and even stupid. Microsoft is vague and difficult in its documentation, but I've worked there and they are never stupid.

There is a constant irritating suggestion to use a CLSID as an identifier, a practice that is annoyingly short sighted. How about trying to embed the message type in the LABEL??? Then use PeadMessage to run up the Queue until you find a message meant expressly for your particular queue and with a message type that you can use to format the message properties to receive the message properly on the first try???

I know this makes for a complex code set, but would you rather do without? Or would you actually try to implement the suggestion of the responder above who is implying that if you have a system of 200 users with 200 message types that they should create 80,000 queues to manage all the one to one requirements? Some people just don't think these things through.

Del answered 21/10, 2013 at 23:4 Comment(0)
R
0

As joocer notes in a comment: use a different queue for different message types.

Alternatively you could agree with the message senders that all messages will be XML (anything that doesn't parse as XML is rejected). Then also agree to some basics of the XML schema: a header element with a message type (and version).

Then process (either yourself of via a serialiser) into the internal type.

Of course in many cases – where there is no real benefit to a deserialisation – just read the content of the XML as required.

Raving answered 29/5, 2012 at 12:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.