Write a query using LINQ to retrieve messages using the System.Messaging.MessageQueue
type:
// open an existing message queue string queuePath = @".\private$\LINQMQ"; MessageQueue messageQueue = new MessageQueue(queuePath); BinaryMessageFormatter messageFormatter = new BinaryMessageFormatter(); var query = from Message msg in messageQueue // The first assignment to msg.Formatter is so that we can touch the // Message object. It assigns the BinaryMessageFormatter to each message // instance so that it can be read to determine if it matches the criteria. // Next, a check is performed that the formatter was correctly assigned // by performing an equality check, which satisfies the Where clause's need // for a boolean result while still executing the assignment of the formatter. where ((msg.Formatter = messageFormatter) == messageFormatter) && int.Parse(msg.Label) > 5 && msg.Body.ToString().Contains('D') orderby msg.Body.ToString() descending select msg; // Check our results for messages with a label > 5 and containing a 'D' in the name foreach (var msg in query) { Console.WriteLine("Label: " + msg.Label + " Body: " + msg.Body); }
The query retrieves the data from the MessageQueue
by selecting the messages where the Label is a number greater than 5 and the message body contains a capital letter "D". These messages are then returned sorted by the message body in descending order.
There are a number of new keywords in this code using LINQ that were not previously used to access a message queue:
var
Instructs the compiler to infer the type of the variable from the right side of the statement. In essence, the type of the variable is determined by what is on the right side of the operator separating the
var
keyword and the expression. This allows for implicitly typed local variables.from
The
from
keyword sets out the source collection to query against and a range variable to represent a single element from that collection. It is always the first clause in a query operation. This may seem counterintuitive if you are used to SQL and expect select to be first, but if you consider that first we need what to work on before we determine what to return, it makes sense. If we weren't used to how SQL does this already, it would be SQL that seems counterintuitive.where
The
where
keyword specifies the constraints by which the elements to return are filtered. Each condition must evaluate to a Boolean result, and when all expressions evaluate to true, the element of the collection is allowed to be selected.orderby
This keyword indicates that the result set should be sorted according to the criteria specified. The default order is ascending, and elements use the default comparer.
select
Allows the projection of an entire element from the collection, the construction of a new type with parts of that element and other calculated values, or a sub-collection of items into the result.
The messageQueue
collection is of type System.Messaging.MessageQueue
, which implements the IEnumerable
interface. This is important, as the LINQ methods provided need a set or collection to implement at least IEnumerable
for it to work with that set or collection. It would be possible to implement a set of extension methods that did not need IEnumerable, but most people will not have the need to. It is even better when the set or collection implements IEnumerable<T>
, as LINQ then knows the type of element in the set or collection that it is working with, but in this case, MessageQueue
has been in the framework for a while and isn't likely to change, so the query provides the element type Message
, as shown in the "from" line:
var query = from Message
msg in messageQueue
For more about this, see Recipe 1.1.
In the Solution, the messages in the queue have been sent with the use of the BinaryFormatter
. To be able to query against them correctly, the Formatter
property must be set on each Message
before it is examined as part of the where
clause:
// The first assignment to msg.Formatter is so that we can touch the // Message object. It assigns the BinaryMessageFormatter to each message // instance so that it can be read to determine if it matches the criteria. // This is done, and then it checks that the formatter was correctly assigned // by performing an equality check, which satisfies the Where clause's need // for a boolean result, while still executing the assignment of the formatter. where ((msg.Formatter = messageFormatter) == messageFormatter) &&
There are two uses of the var
keyword in the solution code:
var query = from Message msg in messageQueue ... foreach (var msg in query) ...
The first usage infers that an IEnumerable<Message>
will be returned and assigned to the query
variable. The second usage infers that the type of msg
is Message
because the query
variable is of type IEnumerablew<Message>
and the msg
variable is an element from that IEnumerable
.
It is also worth noting that when performing operations in a query, actual C# code can be used to determine the conditions, and there is more than just the predetermined set of operators. In the where
clause of this query, both int.Parse
and string
. Contains
are used to help filter messages:
int.Parse(msg.Label) > 5 && msg.Body.ToString().Contains('D')
Recipe 1.9, and the "MessageQueue class," "Implicitly typed local variable," "from keyword," "where keyword," "orderby keyword," and "select keyword" topics in the MSDN documentation.
Get C# 3.0 Cookbook, 3rd Edition now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.