When playing with the mail services, the first and most common thing to do is to enumerate the email messages that you have in a mailbox. In Listing 5-6, you can see a code excerpt about how to retrieve the list of mail folders in the current user ’s mailbox.
LISTING 5-6 Code excerpt to enumerate the email folders in a mailbox
Click he re to vie w code imag e
/// <summary>
/// This method retrieves the email folders of the current user /// </summary>
/// <param name="startIndex">The startIndex (0 based) of the folders to retrieve</param>
/// <returns>A page of up to 10 email folders</returns>
public static List<MailFolder> ListFolders(Int32 startIndex = 0) { String jsonResponse = MicrosoftGraphHelper.MakeGetRequestForString(
String.Format("{0}me/mailFolders?$skip={1}", MicrosoftGraphHelper.MicrosoftGraphV1BaseUri, startIndex));
var folders = JsonConvert.DeserializeObject<MailFolderList>(jsonResponse);
return (folders.Folders);
}
As you can see, the method leverages the helper method MakeGetRequestForString, which is illustrated in Listing 5-5. It uses the JsonConvert type of Newtonsoft.Json to convert the JSON response string into a custom type (MailFolderList) that holds a collection of objects of type MailFolder. These types are defined in Listing 5-7.
Moreover, as you can see in the code highlighted in bold, the URL of the request handles paging of responses by leveraging the OData $skip query string parameter. By default, the Microsoft Graph API will return results in chunks of no more than 10 elements, skipping a number of elements based on the $skip parameter value. Thanks to this out-of-box behavior of the Microsoft Graph API, you easily can do paging of every result set with pages of up to 10 elements each.
LISTING 5-7 Code excerpt that defines the MailFolderList and the MailFolder model types
Click he re to vie w code imag e
/// <summary>
/// Defines a list of email message folders /// </summary>
public class MailFolderList { /// <summary>
/// The list of email message folders /// </summary>
[JsonProperty("value")]
public List<MailFolder> Folders { get; set; }
}
/// <summary>
/// Defines an email Folder /// </summary>
public class MailFolder : BaseModel { /// <summary>
/// The display name of the email folder /// </summary>
[JsonProperty("displayName")]
public String Name { get; set; } /// <summary>
/// Total number of items /// </summary>
public Int32 TotalItemCount { get; set; } /// <summary>
/// Number of unread items /// </summary>
public Int32 UnreadItemCount { get; set; } }
Notice that the MailFolder type provides just a subset of the properties defined in a mail folder, but the JsonConvert engine will handle that, including any property remapping, by leveraging the JsonProperty attribute. Shaping the MailFolder type and any other domain model type is a task you must perform based on your real business requirements if you want to consume the Microsoft Graph API manually and at low level, with pure HTTP, REST, and JSON.
Once you have the list of folders for the current user, you can access the email messages of a specific folder by making a REST request for a URL like the following:
https://graph.microsoft.com/v1.0/me/mailFolders/<FolderID>/messages
In Listing 5-8, you can see a code excerpt of a method that retrieves such a list of email messages.
LISTING 5-8 Code excerpt of a method that retrieves the email messages of a mail folder
Click he re to vie w code imag e
/// <summary>
/// This method retrieves the email messages from a folder in the current user's mailbox
/// </summary>
/// <param name="folderId">The ID of the target folder, optional</param>
/// <param name="startIndex">The startIndex (0 based) of the messages to retrieve</param>
/// <param name="includeAttachments">Defines whether to include attachments</param>
/// <returns>A page of up to 10 email messages in the folder</returns>
public static List<MailMessage> ListMessages(String folderId = null, Int32 startIndex = 0,
Boolean includeAttachments = false) {
String targetUrl = null;
if (!String.IsNullOrEmpty(folderId)) {
targetUrl = String.Format("{0}me/mailFolders/{1}/messages?$skip={2}", MicrosoftGraphHelper.MicrosoftGraphV1BaseUri,
folderId, startIndex);
} else {
targetUrl = String.Format("{0}me/messages?$skip={1}", MicrosoftGraphHelper.MicrosoftGraphV1BaseUri, startIndex);
}
String jsonResponse =
MicrosoftGraphHelper.MakeGetRequestForString(targetUrl);
var messages = JsonConvert.DeserializeObject<MailMessageList>(jsonResponse);
if (includeAttachments)
foreach (var message in messages.Messages.Where(m => m.HasAttachments)) {
message.LoadAttachments();
} }
return (messages.Messages);
}
The logic of the ListMessages method illustrated in Listing 5-8 is similar to that of the ListFolders method illustrated in Listing 5-6, including how the paging of results is handled.
However, in the ListMessages method there is also some business logic to retrieve the
attachments of the messages, if requested with the includeAttachments Boolean argument, by leveraging an extension method called LoadAttachments that extends the MailMessage custom type. In Listing 5-9, you can see how the MailMessageList and the MailMessage types are defined.
LISTING 5-9 Code excerpt that defines the MailMessageList and the MailMessage types
Click he re to vie w code imag e
/// <summary>
/// Defines a list of email messages /// </summary>
public class MailMessageList { /// <summary>
/// The list of messages /// </summary>
[JsonProperty("value")]
public List<MailMessage> Messages { get; set; } }
/// <summary>
/// Defines an email message /// </summary>
public class MailMessage : BaseModel {
public MailMessage() {
this.Attachments = new List<MailAttachment>();
}
/// <summary>
/// The importance of the email message /// </summary>
[JsonConverter(typeof(StringEnumConverter))]
public ItemImportance Importance { get; set; } /// <summary>
/// The sender email address /// </summary>
[JsonProperty("from")
public MailMessageRecipient From { get; set; } /// <summary>
/// The list of email address TO recipients /// </summary>
[JsonProperty("toRecipients")]
public List<MailMessageRecipient> To { get; set; } /// <summary>
/// The list of email address CC recipients /// </summary>
[JsonProperty("ccRecipients")]
public List<MailMessageRecipient> CC { get; set; } /// <summary>
/// The list of email address BCC recipients /// </summary>
[JsonProperty("bccRecipients")]
public List<MailMessageRecipient> BCC { get; set; } /// <summary>
/// The subject of the email message /// </summary>
public String Subject { get; set; } /// <summary>
/// The body of the email message /// </summary>
public ItemBody Body { get; set; } /// <summary>
/// The UTC sent date and time of the email message /// </summary>
public Nullable<DateTime> SentDateTime { get; set; } /// <summary>
/// The UTC received date and time of the email message /// </summary>
public Nullable<DateTime> ReceivedDateTime { get; set; } /// <summary>
/// Defines whether the email message is read on unread /// </summary>
public Boolean IsRead { get; set; }
/// <summary>
/// Defines whether the email message is a draft /// </summary>
public Boolean IsDraft { get; set; } /// <summary>
/// Defines whether the email has attachments /// </summary>
public Boolean HasAttachments { get; set; } /// <summary>
/// The list of email message attachments, if any /// </summary>
public List<MailAttachment> Attachments { get; private set; } }
/// <summary>
/// Defines the importance of an email message /// </summary>
public enum ItemImportance { /// <summary>
/// Low importance /// </summary>
Low,
/// <summary>
/// Normal importance, default value /// </summary>
Normal,
/// <summary>
/// High importance /// </summary>
High, }
/// <summary>
/// Defines a recipient of an email message/meeting /// </summary>
public class UserInfoContainer { /// <summary>
/// The email address of the recipient /// </summary>
[JsonProperty("emailAddress")]
public EmailAddress Recipient { get; set; } }
/// <summary>
/// Defines a user info /// </summary>
public class UserInfo { /// <summary>
/// The email address /// </summary>
public String Address { get; set; } /// <summary>
/// The description of the email address /// </summary>
public String Name { get; set; } }
The layouts of the custom MailMessage and MailMessageList types are defined to make it easier to deserialize the JSON response retrieved from the Microsoft Graph API into .NET complex types. You can also think about using some custom JsonConvert types to customize the out-of-box behavior of the Newtonsoft.Json library, transforming the results into
something different from the JSON response structure. For example, within the definition of the MailMessage type there is the custom converter of type StringEnumConverter applied to the Importance property of type ItemImportance, which is highlighted in bold in Listing 5-9, to serialize the value of the enum type as a JSON string instead of using a number. Moreover, the properties called SentDateTime and ReceivedDateTime are of type Nullable<DateTime> to customize the behavior of the Newtonsoft.Json library while serializing an email message instance. These settings will be helpful later in this chapter in the section “Sending an email message,” when we will send email messages through the Microsoft Graph API.
In Listing 5-10, you can see how the LoadAttachments extension method is defined.
LISTING 5-10 Code excerpt of the LoadAttachments extension method to retrieve the attachments of an email message
Click he re to vie w code imag e
/// <summary>
/// Extension method to load the attachments of an email message /// </summary>
/// <param name="message">The target email message</param>
public static void LoadAttachments(this MailMessage message) { if (message.HasAttachments) {
String jsonResponse = MicrosoftGraphHelper.MakeGetRequestForString(
String.Format("{0}me/messages/{1}/attachments", MicrosoftGraphHelper.MicrosoftGraphV1BaseUri, message.Id));
var attachments = JsonConvert.DeserializeObject<MailAttachmentList>
(jsonResponse);
message.Attachments.AddRange(attachments.Attachments);
foreach (var attachment in message.Attachments) { attachment.ParentMessageId = message.Id;
} } }
The business logic makes an HTTP request for the URL of the Microsoft Graph API that retrieves the attachments of the current message. Then, it deserializes the JSON response into a list of .NET complex types and loads each instance of the MailAttachment type into the collection of attachments of the target MailMessage instance. The binary content of every attachment is handled automatically by the Newtonsoft.Json library, and you will find it in the Byte array property with name Content of the custom type MailAttachment.
Unfortunately, if the email message for which you are downloading the attachments has one or more big files attached, executing the request illustrated in Listing 5-10 could become
expensive and slow, depending on the available bandwidth. It is better to leverage the OData querying capabilities and query for the list of attachments, including their size and excluding their binary content. Later, you can download just the necessary content of those attachments by accessing their URL endpoint directly. In Listing 5-11, you can see a revised sample according to these new requirements.
LISTING 5-11 Code excerpt of the LoadAttachments extension method to retrieve the attachments of an email message, with improved code quality
Click he re to vie w code imag e
/// <summary>
/// Extension method to load the attachments of an email message in a smart way /// </summary>
/// <param name="message">The target email message</param>
public static void LoadAttachments(this MailMessage message) { if (message.HasAttachments) {
String jsonResponse = MicrosoftGraphHelper.MakeGetRequestForString(
String.Format("{0}me/messages/{1}/attachments?
$select=contentType,id,name,size",
MicrosoftGraphHelper.MicrosoftGraphV1BaseUri, message.Id));
var attachments = JsonConvert.DeserializeObject<MailAttachmentList>
(jsonResponse);
message.Attachments.AddRange(attachments.Attachments);
foreach (var attachment in message.Attachments) { attachment.ParentMessageId = message.Id;
} } }
/// <summary>
/// Extension method to load the content of a specific attachment /// </summary>
/// <param name="message">The target email message</param>
public static void EnsureContent(this MailAttachment attachment) { String jsonResponse = MicrosoftGraphHelper.MakeGetRequestForString(
String.Format("{0}me/messages/{1}/attachments/{2}", MicrosoftGraphHelper.MicrosoftGraphV1BaseUri,
attachment.ParentMessageId, attachment.Id));
var result = JsonConvert.DeserializeObject<MailAttachment>(jsonResponse);
attachment.Content = result.Content;
}
The EnsureContent method, which is illustrated in Listing 5-11, takes care of downloading the binary content of a single attachment if it is needed.