So I spent two days finding out how to handle delta queries for messages in Microsoft Graph with the C# SDK. The handling of the delta token can be confusing and is not that well documented by Microsoft.
I have found an example of delta queries for Users, but it assumes that you can save both the delta link/token AND the last query’s page.
However, in my case, I want to persist the delta link/token in a database, so that the application doesn’t have to load all messages from scratch at each startup. Given that the last query is a C# object, storing it in a database is not an option, so the approach used above for the user delta query doesn’t work.
Below is a simplified example of my solution, where only the delta token needs to be kept between each refresh of mail messages. As a consequence, the delta token can be easily stored in a database table if required.
// Class to keep track of the delta tokens per folder
// Could be stored in a database to persist across application restarts
public class FolderInfo {
public string FolderId { get; set; }
public string MessageQueryODataDeltaLink { get; set; }
}
// Collection to keep track of delta tokens
private readonly Dictionary<string, FolderInfo> folderInfos = new Dictionary<string, FolderInfo>();
// Gets mail for a specific user and folder
private async Task<List<EmailInfo>> GetEmailInfo(
GraphServiceClient graphClient,
MailFolder mailFolder,
User user,
CancellationToken cancellationToken = default) {
var emails = new List<EmailInfo>();
// retrieve or instantiate FolderInfo for a particular folder
FolderInfo folderInfo;
if (!folderInfos.TryGetValue(mailFolder.Id, out folderInfo)) {
folderInfo = new FolderInfo {
FolderId = mailFolder.Id
};
folderInfos.Add(mailFolder.Id, folderInfo);
}
// If the delta token for a previous delta query exists, we add it to the query's options
var queryOptions = new List<Option>{};
if (folderInfo.MessageQueryODataDeltaLink != null) {
queryOptions.Add(new QueryOption("$deltatoken", folderInfo.MessageQueryODataDeltaLink));
}
// Create the delta query...
var messagePage = await graphClient
.Users[user.Id]
.MailFolders[mailFolder.Id].Messages
.Delta()
.Request(queryOptions)
.GetAsync(cancellationToken);
// Process all request pages...
while (true) {
// Process the messages. In my case, I convert them to my own EmailInfo class.
foreach (var message in messagePage) {
emails.Add(MessageToEmailInfo(mailFolder, user, message));
}
if (messagePage.NextPageRequest != null) {
messagePage = await messagePage.NextPageRequest
.GetAsync(cancellationToken);
}
else {
break;
}
}
// Retrieve and save the delta token to be used in the next delta query
object oDataDeltaLink;
if (messagePage.AdditionalData.TryGetValue("@odata.deltaLink", out oDataDeltaLink)) {
string sDeltaLink = oDataDeltaLink.ToString();
var deltaLinkElements = sDeltaLink.Split(new string[] { "$deltatoken=" }, StringSplitOptions.None);
folderInfo.MessageQueryODataDeltaLink = deltaLinkElements.Last();
}
return emails;
}
Hope this helps. Good luck!
Leave a Reply