We have a program that keeps a local copy of a potentially large number of mailboxes by monitoring configurable journal streams through the Exchange Web Service Managed API. These journal streams give us an efficient way to ingest the incoming/outgoing mail for thousands of users.
Periodically we need to check if specific mail messages are still present in Exchange.
We currently do this by using FindItems
filtered by the InternetMessageID (which we can get from the envelope in the journal):
var internetMessageIds = new [] {
"<8ADA5FF6B7C3AB4B8F9BC5AF78206B42010AD1A3@....>",
"<04A2D0BEB32B69458296B3E48F75732D014F601E@...>",
"<F04A6B3F2A8AFC42BF4AC18C596A9810749CC4@...>"
};
var filter = new SearchFilter.SearchFilterCollection(
LogicalOperator.Or,
internetMessageIds.Select(imid =>
new SearchFilter.IsEqualTo(EmailMessageSchema.InternetMessageId, imid)
)
);
var exchange = new ExchangeService(PreAuthenticate = true, Credentials = ...);
exchange.ImpersonatedUserId = new ImpersonatedUserId(ConnectingIdType.SmtpAddress, mailbox);
var folderView = new FolderView(1000) {
Traversal=FolderTraversal.Deep,
PropertySet=BasePropertySet.IdOnly
};
var folders = exchange.FindFolders(WellKnownFolderName.Root, folderView)
.Where(f => f.DisplayName == "AllItems" || f.DisplayName == "Deletions" || f.DisplayName == "Purges");
if (folders.Count() != 3) {
// fall back to searching all folders
folders = exchange.FindFolders(WellKnownFolderName.Root, folderView);
}
foreach (var folder in folders) {
var findItemsResult = exchange.FindItems(folder.Id, filter, new ItemView(10));
foreach (var itemResult in findItemsResult) {
Console.WriteLine(
"InternetMessageID={0}, UniqueId={1}",
((EmailMessage)itemResult).InternetMessageId,
itemResult.Id.UniqueId
);
}
}
As you can see we loop through multiple folders and call FindItems
on each.
Initially we always looped through all folders, but we recently optimized the code to use the All Items search folder (if it exists).
That optimization made our "does this message still exist in Exchange?" check faster, but it is still too expensive to run against any non-trivial number of messages.
- Checking 3 messages takes about 3 seconds (after establishing the connection and impersonation) when the All Items folder exists.
- Compare that to using
BindItems
with the UniqueID, which takes less than half a second in my tests.- But the UniqueID of a message is not available from the envelope in the journal stream.
- And even if it were, the UniqueID of a message changes when the user moves it to a different folder in their mailbox.
Is there a more efficient way to check if a message we've captured from the journal still exists in the recipient's mailbox in Exchange?