LINQ to IMAP Reference

Although it is entirely possible to create search and fetch commands by hand, the LINQ engine provides us with an opportunity to let the computer handle the rough parts.

In order to interact with the LINQ provider we need to access the Messages property published by the ImapClient class.
“Note that we need to select a mailbox before we can issue any search and/or fetch commands, be it through LINQ or manually.”

We will demonstrate the basic usage by creating a query, requesting Envelopes for all messages from last week only.

var since = DateTime.Now.AddDays(-7);
var query = client.Messages
    .Where(x => x.Date > since)
    .Select(x => x.Envelope);

Is is of course possible to request multiple items and map them into a custom object. You will however need to have some container class ready since there is no support for anonymous types yet.

var since = DateTime.Now.AddDays(-7);
var query = client.Messages
    .Where(x => x.Date > since);
    .Select(x => new SomeContainer { Envelope = x.Envelope, Uid = x.Uid} );


The query can then be resolved through iteration.

foreach(var envelope in query){
    // notify new envelope arrived

It is important to note that the client does not load requested messages prior to an iteration from the server. It is in fact the iterator itself reading each item from the socket stream one at a time.

This means we do not need to wait until all items have been fetched, instead we get an immediate response for each completed item once the loop starts.

Supported Operations

As it is with all LINQ capable collections the Messages property exposes the IQueryable<T> interface with T : IMessageQueryable.

The IMessageQueryable interface itself exposes methods and properties that can be used to create our query.

It is important to note that some of those members can only be used inside the Where clause, while others are bound to the Select clause.

You can find details about the common commands online at their associated RFC document sections.

Others cannot be used directly but require special handling in order to work properly, these are marked with a ring_black_16and explained below.

interface_15x7IMessageQueryable Where Select
Readonly PropertyIsNew : Boolean dot_black_16  
Readonly PropertyIsOld : Boolean dot_black_16  
Readonly PropertyKeywords : String ring_black_16  
Readonly PropertyFrom : IEnumerable<EmailContact> ring_black_16 dot_black_16
Readonly PropertyTo : IEnumerable<EmailContact> ring_black_16 dot_black_16
Readonly PropertyBcc : IEnumerable<EmailContact> ring_black_16 dot_black_16
Readonly PropertyCc : IEnumerable<EmailContact> ring_black_16 dot_black_16
Readonly PropertyDate : DateTime dot_black_16 dot_black_16
Readonly PropertySubject : String   dot_black_16
Readonly PropertyEnvelope : Envelope   dot_black_16
Readonly PropertyBodyStructure : BodyStructure   dot_black_16
readonly_property_12x11Flags : MessageFlags dot_black_16 dot_black_16
readonly_property_12x11Uid : Int32 dot_black_16 dot_black_16
readonly_property_12x11Headers : IEnumerable<HeaderField> ring_black_16 dot_black_16
readonly_property_12x11Text : String ring_black_16  
readonly_property_12x11SequenceNumber : Int32 dot_black_16 dot_black_16
readonly_property_12x11Size : Size dot_black_16 dot_black_16
readonly_property_12x11InternalDate : DateTime dot_black_16 dot_black_16
method Parts(String)   dot_black_16

While some members are self explanatory others require some inside knowledge of the IMAP protocol in order to understand their function.

readonly_property_12x11 Keywords

Keywords are similar to flags, they can be applied to messages using the STORE command. Using the Keywords property we can search for messages with special keywords attached.

The difference between regular flags and keywords is the fact that the user has the opportunity to tag messages with arbitrary values if the server allows it. The following code shows how to access messages tagged with the keyword “MyTag”.

var query = client.Messages(x => x.Keywords.Contains("MyTag")).Select(x => x.Envelope)

readonly_property_12x11  Headers

The Headers property can be used to query for specific headers and its values. The following query will return all messages that have a header with the name “Priority” and a corresponding value of “high”.

var query = client.Messages.Where(x => x.Headers.Any(y => y.Name.Contains("Priority") && y.Value.Contains("high"))).Select(x => x.Envelope);

readonly_property_12x11  From, To, Bcc, Cc

All four collection properties can be used to perform a full text search on the messages contact lists. The following query will return all messages that have been sent from “Peter” to “Mary”.

var query = client.Messages.Where(x => x.From.Contains("Peter") && x.To.Contains("Mary")).Select(x => x.Envelope);

readonly_property_12x11  Text

Using the text property we can perform a full text search on the message’s body. The following query will return all messages that contain the string “blue whale” somewhere in their content.

var query = client.Messages.Where(x => x.Text.Contains("blue whale")).Select(x => x.Envelope);

method Parts

The Parts method is used to fetch separate body parts of a message. Although it is intended for internal use there’s nothing preventing you from using it outside the ImapClient class.

The method takes a part id and returns the requested MIME section usually as a string depending on the type of section that was requested.

Combining Multiple Operations

In addition to the operations above, the IMAP protocol allows us to combine multiple operations into a single query by using operators.

The supported operators are AND, OR and NOT. The following query makes use all of supported operators as an example.

var query = client.Messages.Where(x => !x.IsNew && (x.Flags.HasFlag(MessageFlags.Seen) || x.Flags.HasFlag(MessageFlags.Answered))).Select(x => x.Envelope);

Last edited Jul 2, 2011 at 5:42 PM by Krasshirsch, version 9


No comments yet.