I'm using the EWS Managed API in C# to download a bunch of messages from my company's exchange server. Loading the messages themselves takes a long time considering that service.FindItems()
only fetches limited information about the messages, but it's not a huge deal. The serious problem I'm facing is how long it takes to load attachments.
The program is supposed to display an email and its image attachment side-by-side. When loading a new email, it can take well over a minute for the attachment to load. I initially fetched the attachments for each message when the message was loaded, but I thought it would be better to try to load them all at once into a List<EmailMessage>
so the program wouldn't have to fetch the attachments when loading individual messages.
Here's the code I used to do that:
fetchView.PropertySet = new PropertySet(BasePropertySet.FirstClassProperties);
fetchView.Traversal = FolderTraversal.Deep;
//create itemView for actual message query since we finally found the damn folder
ItemView iView = new ItemView(int.MaxValue);
FolderId sharedInboxFolder = new FolderId(WellKnownFolderName.Root, sharedMailbox);
FolderId targetFolder = new FolderId(WellKnownFolderName.Root, sharedMailbox);
FindFoldersResults inboxFolders = service.FindFolders(sharedInboxFolder, fetchView);
bool folderFound = false;
//look through the folders in the inbox to find the user-specified one by name
foreach(Folder f in inboxFolders)
{
if (f.DisplayName == Properties.Settings.Default.InboxFolder)
{
targetFolder = f.Id;
folderFound = true;
}
}
// Set itemview properties for FindItems() operation
fullProperties.Add(ItemSchema.Body);
fullProperties.Add(ItemSchema.Attachments);
fullProperties.Add(ItemSchema.DateTimeReceived);
fullProperties.Add(ItemSchema.Subject);
if (!folderFound)
{
MessageBox.Show("Folder not found!");
} else {
SearchFilter greaterthanfilter = new SearchFilter.IsGreaterThanOrEqualTo(ItemSchema.DateTimeReceived, searchDate);
SearchFilter lessthanfilter = new SearchFilter.IsLessThan(ItemSchema.DateTimeReceived, searchDate.AddDays(1));
SearchFilter dayFilter = new SearchFilter.SearchFilterCollection(LogicalOperator.And, greaterthanfilter, lessthanfilter);
FindItemsResults<Item> fetchedMessages = service.FindItems(targetFolder, dayFilter, iView);
foreach (Item i in fetchedMessages.Items)
{
EmailMessage msg = EmailMessage.Bind(service, i.Id, fullProperties);
emails.Add(msg);
}
}
}
I then save all the attachments to disk with
for (int i = 0; i < message.Attachments.Count; i++)
{
if (message.Attachments[i] is FileAttachment)
{
FileAttachment att = message.Attachments[i] as FileAttachment;
att.Load();
using (FileStream attStream = new FileStream(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + @"\Program\images\" + i.ToString(), FileMode.Create, FileAccess.ReadWrite))
{
att.Load(attStream);
attStream.Close();
attStream.Dispose();
}
}
else
{
MessageBox.Show("Not FileAttachment!");
}
}
Then, to load an image and it's attachment, I do
imgViewer.Source = new BitmapImage(new Uri(/some/path/to/image/))
I suspect the hangup is during the save attachments phase. I found this post that indicated TraceFlags should be disabled, so I did service.TraceFlags = TraceFlags.None
but that didn't seem to help at all. I'm contemplating just downloading all the attachments up front, or figuring out some kind of caching mechanism where I download the attachments of message[n+1...x] in the background while the user works on message[n], but this has limited usefulness, because the program should also let the user select an image and load it relatively instantly (i.e. much less than a minute).
Any suggestions are much appreciated.
Your loading the attachments twice for no apparent reason.
Remove the att.Load();
from your if (message.Attachments[i] is FileAttachment)
statement.
Maybe have a look at implementing paging and in turn consider reducing your ItemView
pageSize
to batches of 1000 or less, instead of MaxValue
Also make sure you are only returning what you need in your PropertySet.
BasePropertySet.FirstClassProperties
Consider changing this to just return what you need.
Side Note: A good way to identify slow performing code is to use the .Net Stopwatch class.
Stopwatch sw = new Stopwatch();
sw.Start();
// code
sw.Stop();
StopWatchLog stopWatchLog = new StopWatchLog();
stopWatchLog.CreateXMLTextFile("MethodName()" + " took" + sw.Elapsed.Seconds + " seconds.");