I'm writing an app to process email attachments, using Exchange Web Services.
The general structure of my problem area is as follows:
public static void Main()
{
FindItemsResults<Item> findResults = FindItems();
foreach (Item item in findResults)
{
DoSomethingWithItem(item);
}
}
public static FindItemsResults<Item> FindItems()
{
FindItemsResults<Item> findResults;
ItemView view = new ItemView(10); //batching 10 at a time
view.OrderBy.Add(ItemSchema.DateTimeReceived, SortDirection.Ascending);
view.PropertySet = new PropertySet(
BasePropertySet.IdOnly,
ItemSchema.Subject,
ItemSchema.DateTimeReceived);
findResults = service.FindItems(
WellKnownFolderName.Inbox,
new SearchFilter.SearchFilterCollection(
LogicalOperator.Or,
new SearchFilter.ContainsSubstring(ItemSchema.Subject, Properties.Settings.Default.EmailSubject)),
view);
//return set of emails
return findResults;
}
At first, this looked OK - it processed my earlier test examples perfectly well. But when I start testing with bulk loads, I realised that it was only processing the first 10 items, since I was specifying a batch size of 10 items (ItemView view = new ItemView(10
)), but I wasn't checking for further batches.
I could have simply increased the batch size, but a quick google later, I found a better example:
public static FindItemsResults<Item> FindItems()
{
FindItemsResults<Item> findResults;
ItemView view = new ItemView(10, 0, OffsetBasePoint.Beginning);
view.OrderBy.Add(ItemSchema.DateTimeReceived, SortDirection.Ascending);
view.PropertySet = new PropertySet(
BasePropertySet.IdOnly,
ItemSchema.Subject,
ItemSchema.DateTimeReceived);
do
{
findResults = service.FindItems(
WellKnownFolderName.Inbox,
new SearchFilter.SearchFilterCollection(
LogicalOperator.Or,
new SearchFilter.ContainsSubstring(ItemSchema.Subject, Properties.Settings.Default.EmailSubject)),
view);
//any more batches?
if (findResults.NextPageOffset.HasValue)
{
view.Offset = findResults.NextPageOffset.Value;
}
}
while (findResults.MoreAvailable);
return findResults;
}
This loops through as many emails as I care to throw at it, but for reasons I can't yet understand, the foreach loop now only processes the first item in findResults.
Even though findResults contains more than one item (findResults.Items.Count
> 1), with my second example, findResults.MoreAvailable
= false. Fair enough, I've looped through the batches earlier, so it makes sense that I'm looking at the end of the last batch.
But how do I reset findResults so that it will process the whole lot? I tried setting findResults.MoreAvailable but it is readonly... What am I missing?
CONCLUSION:
OK, so I can either process the items one batch at a time, or I can add each item in each batch to a List and process them later, as I currently do.
There is not a lot between them; I've initially started using a List, but I'll consider the choice further.
It seems to my that your main problem is that you try define a function which returns FindItemsResults<Item>
. If you use paring of results you will have not one object of this type. Instead of that on retrieving of every new page the FindItemsResults<Item>
will be overwritten. Following example display subjects of all items from the Inbox
:
ItemView view = new ItemView(10, 0, OffsetBasePoint.Beginning);
view.OrderBy.Add(ItemSchema.DateTimeReceived, SortDirection.Descending);
view.PropertySet = new PropertySet(
BasePropertySet.IdOnly,
ItemSchema.Subject,
ItemSchema.DateTimeReceived);
// save the folder where we will make searching to do this one time
Folder myInbox = Folder.Bind(service, WellKnownFolderName.Inbox);
FindItemsResults<Item> findResults;
do
{
findResults = myInbox.FindItems(
new SearchFilter.ContainsSubstring(ItemSchema.Subject,
Properties.Settings.Default.EmailSubject)),
view);
foreach (Item item in findResults)
{
// Do something with the item.
Console.WriteLine();
if (item is EmailMessage)
{
EmailMessage em = item as EmailMessage;
Console.WriteLine("Subject: \"{0}\"", em.Subject);
}
else if (item is MeetingRequest)
{
MeetingRequest mr = item as MeetingRequest;
Console.WriteLine("Subject: \"{0}\"", mr.Subject);
}
else
{
// we can handle other item types
}
}
//any more batches?
if (findResults.NextPageOffset.HasValue)
{
view.Offset = findResults.NextPageOffset.Value;
}
}
while (findResults.MoreAvailable);
The code display the subjects on the console output. If you want to use EmailMessage
or MeetingRequest
in another way you should modify the code correspondent. You can also define a delegate which do something with the found EmailMessage
or MeetingRequest
and call the delegate on the place of Console.WriteLine
. If you do need to same all items somewhere, then you will have to create some collection like List<Item>
, fill there in the function and return instead of FindItemsResults<Item>
which you currently do.