Loading Properties for Multiple Items with One Call to Exchange Web Services

When you use the FindItems method or the FindAppointments method in Exchange Web Services (EWS), it’s important to realize that neither of these methods returns all properties for the items returned by the query. For example, FindItems will not return the Body property for a message item, and FindAppointments will not return the RequiredAttendees property for a calendar item. Therefore, if you are interested in additional properties that cannot be returned by FindItems or FindAppointments, you must explicitly load the additional properties to the items that are returned by the query.

Fortunately, the EWS Managed API provides an easy way to load additional properties for multiple items with a single call to EWS. The following code example shows you how to search the Inbox folder for items that match the specified search criteria, and then use the LoadPropertiesForItems method to load the Subject propertyand Body property for the items returned by FindItems.

// Specify the IdOnly response shape, and specify that
// FindItem results should be requested in batches of 25.
ItemView view = new ItemView(25);
view.PropertySet = new PropertySet(BasePropertySet.IdOnly);
view.Offset = 0;
view.OffsetBasePoint = OffsetBasePoint.Beginning;

// Specify search criteria.
SearchFilter searchFilter = new SearchFilter.ContainsSubstring(ItemSchema.Subject, "Message #");

// Declare findResults.
FindItemsResults<Item> findResults;

do
{
    // Call FindItems to search the Inbox folder.
    findResults = service.FindItems(WellKnownFolderName.Inbox, searchFilter, view);

    // Load additional properties for the current batch of items.
    service.LoadPropertiesForItems(findResults, new PropertySet(ItemSchema.Subject, ItemSchema.Body));

    // TODO: Process the current batch of items.

    // If more items are available, update the offset value so that the next
    // batch of items is returned by the next call to FindItems.
    if (findResults.NextPageOffset.HasValue)
    {
        view.Offset = findResults.NextPageOffset.Value;
    }
}
while (findResults.MoreAvailable);

Note: By specifying a page size of 25 when ItemView is instantiated, this example requests FindItems results (and subsequently calls LoadPropertiesForItems) for a maximum of 25 items at a time. Processing items in batches like this is helpful in that it reduces the likelihood that the server will be overloaded when the number of search results is large.

The following is the XML response that is returned by from FindItems in the above code example. A total of five messages that match the search criteria were found in the Inbox folder. Item identifiers and change keys have been shortened for readability.

<s:Body xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="https://www.w3.org/2001/XMLSchema">
    <m:FindItemResponse
                xmlns:m="https://schemas.microsoft.com/exchange/services/2006/messages"
                xmlns:t="https://schemas.microsoft.com/exchange/services/2006/types">
        <m:ResponseMessages>
            <m:FindItemResponseMessage ResponseClass="Success">
                <m:ResponseCode>NoError</m:ResponseCode>
                <m:RootFolder IndexedPagingOffset="5" TotalItemsInView="5" IncludesLastItemInRange="true">
                    <t:Items>
                        <t:Message>
                            <t:ItemId Id="AAT5QA=" ChangeKey="CQAAA" />
                        </t:Message>
                        <t:Message>
                            <t:ItemId Id="AAT5QB=" ChangeKey="CQAAB" />
                        </t:Message>
                        <t:Message>
                            <t:ItemId Id="AAT5QC=" ChangeKey="CQAAC" />
                        </t:Message>
                        <t:Message>
                            <t:ItemId Id="AAT5QD=" ChangeKey="CQAAD" />
                        </t:Message>
                        <t:Message>
                            <t:ItemId Id="AAT5QE=" ChangeKey="CQAAE" />                         
                        </t:Message>
                    </t:Items>
                </m:RootFolder>
            </m:FindItemResponseMessage>
        </m:ResponseMessages>
    </m:FindItemResponse>
</s:Body>

Note: In this example, the number of items that matched the specified search criteria was fewer than our specified paging size of 25; therefore, all items were returned by the first call to FindItems. If more than 25 items (the specified paging size) had matched the specified search criteria, FindItems (and LoadPropertiesForItems) would have executed multiple times — once for each batch of 25 items that matched the specified search criteria.

The following is the XML request that is generated by calling LoadPropertiesForItems in the above code example. As this XML request shows, LoadPropertiesForItems translates to a batch GetItem call under the covers — the Subject and Body property for all five items in findResults are requested in a single call GetItem call to EWS.

<m:GetItem>
    <m:ItemShape>
        <t:BaseShape>IdOnly</t:BaseShape>
        <t:AdditionalProperties>
            <t:FieldURI FieldURI="item:Subject" />
            <t:FieldURI FieldURI="item:Body" />
        </t:AdditionalProperties>
    </m:ItemShape>
    <m:ItemIds>
        <t:ItemId Id="AAT5QA=" ChangeKey="CQAAA" />
        <t:ItemId Id="AAT5QB=" ChangeKey="CQAAB" />
        <t:ItemId Id="AAT5QC=" ChangeKey="CQAAC" />
        <t:ItemId Id="AAT5QD=" ChangeKey="CQAAD" />
        <t:ItemId Id="AAT5QE=" ChangeKey="CQAAE" />
    </m:ItemIds>
</m:GetItem>

Now, you might assume that the same simple call to LoadPropertiesForItems can also be used to load properties for calendar items that are returned by FindAppointments. And it will work, with one small caveat: LoadPropertiesForItems takes an argument of type IEnumerable<Item>, whereas FindAppointments returns IEnumerable<Appointment> — so you must find a way to pass the results from FindAppointments to LoadPropertiesForItems as IEnumerable<Item>. Fortunately, this is easily accomplished by using Linq. The following code example shows you how search the Calendar folder for appointments that match the specified search criteria, and then use the LoadPropertiesForItems method to load the Subject property, the RequiredAttendees property,and the OptionalAttendees property for the calendar items returned by FindAppointments.

// Define search parameters and maximum number of items to return.
CalendarView calView = new CalendarView(DateTime.Now, DateTime.Now.AddDays(3), 25);

// Specify the IdOnly shape.
PropertySet props = new PropertySet(BasePropertySet.IdOnly);
calView.PropertySet = props;

// Call FindAppointments to search the Calendar folder.
FindItemsResults<Appointment> findResults = service.FindAppointments(WellKnownFolderName.Calendar, calView);

if (findResults.TotalCount > iMaxItemsReturned)
{
    string sMsg = "Total number of items that match search criteria exceeds specified maximum number of results to return.";
    sMsg += " To find all items that match the specified search criteria, either narrow the date range ";
    sMsg += "or increase the specified maximum number of items to return in CalendarView.";
    Exception e = new Exception(sMsg);
    throw e;
}
else
{
    // Call LoadPropertiesForItems to load the Subject, RequiredAttendees, and OptionalAttendees properties.
    service.LoadPropertiesForItems(from Item item in findResults select item,
                new PropertySet(BasePropertySet.IdOnly,
                        AppointmentSchema.Subject,
                        AppointmentSchema.RequiredAttendees,
                        AppointmentSchema.OptionalAttendees));
}

Note: To use Linq as shown in the above example, you must include the following directive in your program: using System.Linq;

Want to learn more about working with the EWS Managed API? Check out the code examples that are available in the Working with the EWS Managed API section of the Microsoft Exchange Web Services Managed API 1.0 SDK.