Synchronizing items by using the EWS Managed API 2.0

Last modified: April 07, 2014

Applies to: EWS Managed API | Exchange Server 2007 Service Pack 1 (SP1) | Exchange Server 2010

Note: This content applies to the EWS Managed API 2.0 and earlier versions. For the latest information about the EWS Managed API, see Web services in Exchange.

You can use the Exchange Web Services (EWS) Managed API to retrieve a list of changes that have occurred to items in a specified folder since a given point in time.

A client uses the SyncFolderItems method to keep its items in sync with the items on the server by doing the following:

  • Performing an initial synchronization operation (to retrieve a list of all items in a specified folder).

  • Periodically performing a subsequent synchronization operation to retrieve a list of item changes that have occurred since the prior synchronization.

To synchronize its items with the items on the server, a client processes the collection of changes that are returned by the SyncFolderItems method and applies the same changes to its local items.

The SyncFolderItems method enables one-way synchronization, server to client. To propagate client changes back up to the server, the client must use other EWS Managed API methods to create, update, and delete items as necessary. For more information about these methods, see Working with items by using the EWS Managed API 2.0.

Note

The SyncFolderItems method will return a maximum of 512 changes. Subsequent SyncFolderItems requests must be performed to get additional changes.

The SyncFolderItems method is similar to the FindItem method in that it cannot return properties such as Body or Attachments. If you need properties that cannot be returned by SyncFolderItems, we recommend that you specify the IdOnly property set when you call SyncFolderItems, and then use the LoadPropertiesForItems method to get the properties you require for the items that were returned by the SyncFolderItems method.

To perform the initial synchronization operation

  1. Call the SyncFolderItems method and specify the following: the folder that contains the data to synchronize, the set of properties to return for each item in the response, the set of IDs that correspond to the items for which no synchronization data should be returned, an integer that represents the maximum number of changes that should be returned, a SyncFolderItemsScope enumeration value that indicates the type of items for which synchronization data should be returned, and a null sync state. Specifying a null sync state causes the SyncFolderItems method to return a collection that represents all items in the specified folder that meet the criteria specified by the other input parameters. The following code shows how to request a list of all normal items that are contained in the Inbox folder (up to a maximum of 512 items); the first class property set will be returned for each folder in the response. Connection configuration information is provided by using an ExchangeService object named service.

    ChangeCollection<ItemChange> icc = service.SyncFolderItems(new FolderId(WellKnownFolderName.Inbox), PropertySet.FirstClassProperties, null, 512, SyncFolderItemsScope.NormalItems, null);
    
  2. Save the sync state for use in the subsequent SyncFolderItems call. The following code example shows how to access the SyncState property in the response; the client should store this value such that it persists and can be retrieved later to be used in the subsequent SyncFolderItems call.

    string sSyncState = icc.SyncState;
    
  3. If the SyncFolderItems method returns a list of item changes, iterate through the changes and process each change on the client. For the initial synchronization, all items in the list of changes will be of type ChangeType.Create.

    if (icc.Count == 0)
    {
        Console.WriteLine("There are no items to synchronize.");
    }
    else
    {
        foreach (ItemChange ic in icc)
        {
            Console.WriteLine("ChangeType: " + ic.ChangeType.ToString());
            Console.WriteLine("ItemId: " + ic.ItemId.UniqueId);
            Console.WriteLine("Subject: " + ic.Item.Subject);
            Console.WriteLine("===========");
    
            //TODO: Create item on the client.
        }
    }
    

To perform subsequent synchronization operations

  1. Call the SyncFolderItems method and specify the following: the folder that contains the data to synchronize, the set of properties to return for each item in the response, the set of IDs that correspond to the items for which no synchronization data should be returned, an integer that represents the maximum number of changes that should be returned, a SyncFolderItemsScope enumeration value that indicates the type of items for which synchronization data should be returned, and the sync state value from the prior synchronization response. The following code shows how to request a list of all changes to normal items that are contained in the Inbox folder (up to a maximum of 512 changes) that have occurred since the time that corresponds to the specified sync state; the first class property set will be returned for each folder in the response. Connection configuration information is provided by using an ExchangeService object named service.

    ChangeCollection<ItemChange> icc = service.SyncFolderItems(new FolderId(WellKnownFolderName.Inbox), PropertySet.FirstClassProperties, null, 512, SyncFolderItemsScope.NormalItems, sSyncState);
    

    Note

    The SyncFolderItems operation will return a maximum of 512 changes. Subsequent SyncFolderItems requests must be performed to get additional changes.

  2. Save the sync state for use in the subsequent SyncFolderItems call. The following code example shows how to access the SyncState property in the response; the client should store this value such that it persists and can be retrieved later to be used in the subsequent SyncFolderItems call.

    string sSyncState = fcc.SyncState;
    
  3. If the SyncFolderItems method returns a list of item changes, iterate through the changes and process each change on the client.

    if (icc.Count == 0)
    {
        Console.WriteLine("There are no item changes to synchronize.");
    }
    else
    {
        foreach (ItemChange ic in icc)
        {
            if (ic.ChangeType == ChangeType.Create)
            {
                //TODO: Create item on the client.
            }
            else if (ic.ChangeType == ChangeType.Update)
            {
                //TODO: Update item on the client.
            }
            else if (ic.ChangeType == ChangeType.Delete)
            {
                //TODO: Delete item on the client.
            }
            else if (ic.ChangeType == ChangeType.ReadFlagChange)
            {
                //TODO: Update the item's read flag on the client.
            }
    
            Console.WriteLine("ChangeType: " + ic.ChangeType.ToString());
            Console.WriteLine("ItemId: " + ic.ItemId.UniqueId);
            if (ic.Item != null)
            {
                Console.WriteLine("Subject: " + ic.Item.Subject);
            }
            Console.WriteLine("===========");
        }
    }
    

    Note

    When an item has been updated, the information that is contained in the corresponding ItemChange within the SyncFolderItems response will not specify how the item has changed. Therefore, the client is responsible for identifying what has changed by comparing item property values in the change list with its local item property values. The client must specify PropertySet in the SyncFolderItems call such that each ItemChange within the SyncFolderItems response will contain the properties that are of interest to the client.

Example

The following code example shows how to get a list of all changes to normal items in the Inbox folder that have occurred since the time that is represented by sSyncState. The item changes are retrieved in batches of five, by using successive calls to the SyncFolderItems method until no more changes remain. This example assumes that service is a valid ExchangeService binding and that sSyncState represents the sync state that was returned by a prior call to SyncFolderItems.

// Initialize the flag that will indicate when there are no more changes.
bool isEndOfChanges = false;

// Call SyncFolderItems repeatedly until no more changes are available.
// sSyncState represents the sync state value that was returned in the prior synchronization response.
do
{
    // Get a list of changes (up to a maximum of 5) that have occurred on normal items in the Inbox folder since the prior synchronization.
    ChangeCollection<ItemChange> icc = service.SyncFolderItems(new FolderId(WellKnownFolderName.Inbox), PropertySet.FirstClassProperties, null, 5, SyncFolderItemsScope.NormalItems, sSyncState);

    if (icc.Count == 0)
    {
        Console.WriteLine("There are no item changes to synchronize.");
    }
    else
    {
        foreach (ItemChange ic in icc)
        {
            if (ic.ChangeType == ChangeType.Create)
            {
                //TODO: Create item on the client.
            }
            else if (ic.ChangeType == ChangeType.Update)
            {
                //TODO: Update item on the client.
            }
            else if (ic.ChangeType == ChangeType.Delete)
            {
                //TODO: Delete item on the client.
            }
            else if (ic.ChangeType == ChangeType.ReadFlagChange)
            {
                //TODO: Update the item's read flag on the client.
            }

            Console.WriteLine("ChangeType: " + ic.ChangeType.ToString());
            Console.WriteLine("ItemId: " + ic.ItemId.UniqueId);
            if (ic.Item != null)
            {
                Console.WriteLine("Subject: " + ic.Item.Subject);
            }
            Console.WriteLine("===========");
        }
    }

    // Save the sync state for use in future SyncFolderHierarchy calls.
    sSyncState = icc.SyncState;

    if (!icc.MoreChangesAvailable)
    {
        isEndOfChanges = true;
    }
} while (!isEndOfChanges);

The following example shows the XML request that is sent by the client to the server when the client calls the SyncFolderItems method, as shown in the code example earlier in this topic. The value of SyncState has been shortened to preserve readability.

<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
               xmlns:m="https://schemas.microsoft.com/exchange/services/2006/messages"
               xmlns:t="https://schemas.microsoft.com/exchange/services/2006/types"
               xmlns:soap="https://schemas.xmlsoap.org/soap/envelope/">
  <soap:Header>
    <t:RequestServerVersion Version="Exchange2010" />
  </soap:Header>
  <soap:Body>
    <m:SyncFolderItems>
      <m:ItemShape>
        <t:BaseShape>AllProperties</t:BaseShape>
      </m:ItemShape>
      <m:SyncFolderId>
        <t:DistinguishedFolderId Id="inbox" />
      </m:SyncFolderId>
      <m:SyncState>H4sIAAA==</m:SyncState>
      <m:MaxChangesReturned>5</m:MaxChangesReturned>
      <m:SyncScope>NormalItems</m:SyncScope>
    </m:SyncFolderItems>
  </soap:Body>
</soap:Envelope>

The following example shows the XML response that is returned by the server after it processes the request from the client. This response indicates that one item was created, two items were deleted, and the read flag of one item was changed since the prior synchronization. The values of the SyncState element, Id attributes, and ChangeKey attributes have been shortened to preserve readability.

<s:Envelope xmlns:s="https://schemas.xmlsoap.org/soap/envelope/">
  <s:Header>
    <h:ServerVersionInfo MajorVersion="14" MinorVersion="0" MajorBuildNumber="639" MinorBuildNumber="21" Version="Exchange2010" 
                 xmlns:h="https://schemas.microsoft.com/exchange/services/2006/types" 
                 xmlns="https://schemas.microsoft.com/exchange/services/2006/types" 
                 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
                 xmlns:xsd="http://www.w3.org/2001/XMLSchema" />
  </s:Header>
  <s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <m:SyncFolderItemsResponse xmlns:m="https://schemas.microsoft.com/exchange/services/2006/messages" xmlns:t="https://schemas.microsoft.com/exchange/services/2006/types">
      <m:ResponseMessages>
        <m:SyncFolderItemsResponseMessage ResponseClass="Success">
          <m:ResponseCode>NoError</m:ResponseCode>
          <m:SyncState>H4sIAAAAAAAEAO29B2AcSZY==</m:SyncState>
          <m:IncludesLastItemInRange>true</m:IncludesLastItemInRange>
          <m:Changes>
            <t:Create>
              <t:Message>
                <t:ItemId Id="AQMkAD+" />
                <t:ParentFolderId Id="AQMkA==" ChangeKey="AQAAAA==" />
                <t:ItemClass>IPM.Note</t:ItemClass>
                <t:Subject>Message #10 Subject</t:Subject>
                <t:Sensitivity>Normal</t:Sensitivity>
                <t:DateTimeReceived>2009-10-13T23:46:04Z</t:DateTimeReceived>
                <t:Size>911</t:Size>
                <t:Importance>Normal</t:Importance>
                <t:IsSubmitted>false</t:IsSubmitted>
                <t:IsDraft>false</t:IsDraft>
                <t:IsFromMe>true</t:IsFromMe>
                <t:IsResend>false</t:IsResend>
                <t:IsUnmodified>true</t:IsUnmodified>
                <t:DateTimeSent>2009-10-13T23:46:03Z</t:DateTimeSent>
                <t:DateTimeCreated>2009-10-13T23:46:04Z</t:DateTimeCreated>
                <t:DisplayCc />
                <t:DisplayTo>User1First User1Last</t:DisplayTo>
                <t:HasAttachments>false</t:HasAttachments>
                <t:Culture>en-US</t:Culture>
                <t:EffectiveRights>
                  <t:CreateAssociated>false</t:CreateAssociated>
                  <t:CreateContents>false</t:CreateContents>
                  <t:CreateHierarchy>false</t:CreateHierarchy>
                  <t:Delete>true</t:Delete>
                  <t:Modify>true</t:Modify>
                  <t:Read>true</t:Read>
                </t:EffectiveRights>
                <t:LastModifiedName>User1First User1Last</t:LastModifiedName>
                <t:LastModifiedTime>2009-10-13T23:46:04Z</t:LastModifiedTime>
                <t:IsAssociated>false</t:IsAssociated>
                <t:WebClientReadFormQueryString>?ae=Item&amp;a=Open&amp;t=IPM.Note&amp;id=RgAVAAAJ&amp;exvsurl=1</t:WebClientReadFormQueryString>
                <t:ConversationId Id="AAQkADMzMj=" />
                <t:Sender>
                  <t:Mailbox>
                    <t:Name>User1First User1Last</t:Name>
                    <t:EmailAddress>user1@example.com</t:EmailAddress>
                    <t:RoutingType>SMTP</t:RoutingType>
                    <t:MailboxType>Mailbox</t:MailboxType>
                  </t:Mailbox>
                </t:Sender>
                <t:IsReadReceiptRequested>false</t:IsReadReceiptRequested>
                <t:IsDeliveryReceiptRequested>false</t:IsDeliveryReceiptRequested>
                <t:ConversationIndex>AcpMX1MXLhDTgifNRUa0pnqoH6rgyg==</t:ConversationIndex>
                <t:ConversationTopic>Message #10 Subject</t:ConversationTopic>
                <t:From>
                  <t:Mailbox>
                    <t:Name>User1First User1Last</t:Name>
                    <t:EmailAddress>user1@example.com</t:EmailAddress>
                    <t:RoutingType>SMTP</t:RoutingType>
                    <t:MailboxType>Mailbox</t:MailboxType>
                  </t:Mailbox>
                </t:From>
                <t:InternetMessageId>&lt;962143BAE7F3@example.com&gt;</t:InternetMessageId>
                <t:IsRead>true</t:IsRead>
                <t:ReceivedBy>
                  <t:Mailbox>
                    <t:Name>User1First User1Last</t:Name>
                    <t:EmailAddress>user1@example.com</t:EmailAddress>
                    <t:RoutingType>SMTP</t:RoutingType>
                    <t:MailboxType>Mailbox</t:MailboxType>
                  </t:Mailbox>
                </t:ReceivedBy>
                <t:ReceivedRepresenting>
                  <t:Mailbox>
                    <t:Name>User1First User1Last</t:Name>
                    <t:EmailAddress>user2@example.com</t:EmailAddress>
                    <t:RoutingType>SMTP</t:RoutingType>
                    <t:MailboxType>Mailbox</t:MailboxType>
                  </t:Mailbox>
                </t:ReceivedRepresenting>
              </t:Message>
            </t:Create>
            <t:Delete>
              <t:ItemId Id="AQMkADMzAAA==" ChangeKey="CQAAAA1==" />
            </t:Delete>
            <t:Delete>
              <t:ItemId Id="AQMkADIrEwAAAA==" ChangeKey="CQAAAA2==" />
            </t:Delete>
            <t:ReadFlagChange>
              <t:ItemId Id="AQMkADMzADIAAA==" ChangeKey="CQAAAA3==" />
              <t:IsRead>true</t:IsRead>
            </t:ReadFlagChange>
          </m:Changes>
        </m:SyncFolderItemsResponseMessage>
      </m:ResponseMessages>
    </m:SyncFolderItemsResponse>
  </s:Body>
</s:Envelope>

Compiling the code

For information about compiling this code, see Getting started with the EWS Managed API 2.0.

Robust programming

  • Write appropriate error handling code for common search errors.

  • Review the client request XML that is sent to the Exchange server.

  • Review the server response XML that is sent from the Exchange server.

  • Set the service binding as shown in Setting the Exchange service URL by using the EWS Managed API 2.0. Do not hard code URLs because if mailboxes move, they might be serviced by a different Client Access server. If the client cannot connect to the service, retry setting the binding by using the AutodiscoverUrl method.

  • Set the target Exchange Web Services schema version by setting the requestedServerVersion parameter of the ExchangeService constructor. For more information, see Versioning EWS requests by using the EWS Managed API 2.0.

Security

  • Use HTTP with SSL for all communication between client and server.

  • Always validate the server certificate that is used for establishing the SSL connections. For more information, see Validating X509 certificates by using the EWS Managed API 2.0.

  • Do not include user names and passwords in trace files.

  • Verify that Autodiscover lookups that use HTTP GET to find an endpoint always prompt for user confirmation; otherwise, they should be blocked.