在 Exchange 中使用 EWS 同步文件夹

了解如何使用 EWS 托管 API 或 EWS 获取文件夹列表或更改的文件夹列表,以便同步客户端。

Exchange 中的 EWS 使用项目同步和文件夹同步在客户端和服务器之间同步邮箱内容。 文件夹同步从根文件夹获取文件夹的初始列表,然后随着时间的推移,获取对这些文件夹所做的更改,并获取新文件夹。

如果使用 EWS 托管 API 执行文件夹同步,则首先使用 ExchangeService.SyncFolderHierarchy 方法获取根文件夹中文件夹的初始列表。 然后,在后续调用期间更新 cSyncState 参数的值,以获取新文件夹和已更改文件夹的列表。

若要使用 EWS 执行文件夹同步,请使用 SyncFolderHierarchy 操作请求根文件夹中文件夹的初始列表,分析响应,然后在将来的某个时候获取对根文件夹中的更改并分析响应。 客户端收到初始文件夹或更改的文件夹列表后,它会 在本地进行更新。 将来检索更改的方式和时间取决于应用程序使用的 同步设计模式

使用 EWS 托管 API 获取所有文件夹或更改的文件夹的列表

下面的代码示例演示如何获取根文件夹中的文件夹的初始列表,然后获取根文件夹中自上次同步以来发生的文件夹更改列表。 在对 ExchangeService.SyncFolderHierarchy 方法进行初始调用期间,将 cSyncState 值设置为 null。 方法完成后,在本地保存 cSyncState 值,以便在下一次 SyncFolderHierarchy 方法调用中使用。 在初始调用和后续调用中,通过使用 对 SyncFolderHierarchy 方法的连续调用,以 10 个批方式检索文件夹,直到不再保留任何更改。 此示例将 propertySet 参数设置为 IdOnly 以减少对 Exchange 数据库的调用,这是 同步的最佳做法。 在此示例中,我们假定 服务 是有效的 ExchangeService 对象绑定,并且 cSyncState 表示先前调用 SyncFolderHierarchy 返回的同步状态。

// Get a list of all folders in the mailbox by calling SyncFolderHierarchy.
// The folderId parameter must be set to the root folder to synchronize. 
// The propertySet parameter is set to IdOnly to reduce calls to the Exchange database
// because any additional properties result in additional calls to the Exchange database. 
// The syncState parameter is set to cSyncState, which should be null in the initial call, 
// and should be set to the sync state returned by the previous SyncFolderHierarchy call 
// in subsequent calls.
ChangeCollection<FolderChange> fcc = service.SyncFolderHierarchy(new FolderId(WellKnownFolderName.Root), PropertySet.IdOnly, cSyncState);
// If the count of changes is zero, there are no changes to synchronize.
if (fcc.Count == 0)
{
    Console.WriteLine("There are no folders to synchronize.");
}
// Otherwise, write all the changes included in the response 
// to the console. 
// For the initial synchronization, all the changes will be of type
// ChangeType.Create.
else
{
    foreach (FolderChange fc in fcc)
    {
        Console.WriteLine("ChangeType: " + fc.ChangeType.ToString());
        Console.WriteLine("FolderId: " + fc.FolderId);
        Console.WriteLine("===========");
    }
}
// Save the sync state for use in future SyncFolderItems requests.
// The sync state is used by the server to determine what changes to report
// to the client.
string fSyncState = fcc.SyncState;

在服务器上检索新文件夹或更改的文件夹列表后, 在客户端上创建或更新文件夹

使用 EWS 获取文件夹的初始列表

以下示例演示使用 SyncFolderHierarchy 操作获取初始文件夹层次结构的 XML 请求。 这也是 EWS 托管 API 在使用 SyncFolderHierarchy 方法检索初始文件夹列表时发送的 XML 请求。 SyncFolderHierarchy 操作的 SyncState 元素未包括在内,因为这是初始同步。 本示例将 BaseShape 元素设置为 IdOnly 以减少对 Exchange 数据库的调用,这是 同步的最佳做法

<?xml version="1.0" encoding="utf-8"?>
<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="Exchange2013" />
  </soap:Header>
  <soap:Body>
    <m:SyncFolderHierarchy>
      <m:FolderShape>
        <t:BaseShape>IdOnly</t:BaseShape>
      </m:FolderShape>
      <m:SyncFolderId>
        <t:DistinguishedFolderId Id="root" />
      </m:SyncFolderId>
    </m:SyncFolderHierarchy>
  </soap:Body>
</soap:Envelope>

以下示例演示服务器在处理 SyncFolderHierarchy 操作请求后返回的 XML 响应。 初始响应包括所有文件夹的 Create 元素,因为所有文件夹在初始同步期间都被视为新文件夹。 为了提高可读性,某些属性和元素的值已缩短,并且为简洁起见,删除了一些 Create 元素块。

<?xml version="1.0" encoding="utf-8"?>
<s:Envelope xmlns:s="https://schemas.xmlsoap.org/soap/envelope/">
  <s:Header>
    <h:ServerVersionInfo MajorVersion="15"
                         MinorVersion="0"
                         MajorBuildNumber="785"
                         MinorBuildNumber="6"
                         Version="V2_6"
                         xmlns:h="https://schemas.microsoft.com/exchange/services/2006/types"
                         xmlns="https://schemas.microsoft.com/exchange/services/2006/types"
                         xmlns:xsd="http://www.w3.org/2001/XMLSchema"
                         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
  </s:Header>
  <s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <m:SyncFolderHierarchyResponse xmlns:m="https://schemas.microsoft.com/exchange/services/2006/messages"
                                   xmlns:t="https://schemas.microsoft.com/exchange/services/2006/types">
      <m:ResponseMessages>
        <m:SyncFolderHierarchyResponseMessage ResponseClass="Success">
          <m:ResponseCode>NoError</m:ResponseCode>
          <m:SyncState>H4sIAA==</m:SyncState>
          <m:IncludesLastFolderInRange>true</m:IncludesLastFolderInRange>
          <m:Changes>
            <t:Create>
              <t:Folder>
                <t:FolderId Id="AAMkADM="
                            ChangeKey="AQAAABYA"/>
              </t:Folder>
            </t:Create>
            <t:Create>
              <t:Folder>
                <t:FolderId Id="AAMkADMzM="
                            ChangeKey="AQAAABY"/>
              </t:Folder>
            </t:Create>
            <t:Create>
              <t:Folder>
                <t:FolderId Id="AAMkAD/AAA="
                            ChangeKey="AQAAABYA"/>
              </t:Folder>
            </t:Create>
            <t:Create>
              <t:Folder>
                <t:FolderId Id="AAMkADBh="
                            ChangeKey="AQAAABYA"/>
              </t:Folder>
            </t:Create>
            ...
          </m:Changes>
        </m:SyncFolderHierarchyResponseMessage>
      </m:ResponseMessages>
    </m:SyncFolderHierarchyResponse>
  </s:Body>
</s:Envelope>

检索服务器上的新文件夹列表后,在 客户端上创建文件夹

使用 EWS 获取自上次同步以来的更改

以下示例演示使用 SyncFolderHierarchy 操作获取根文件夹中文件夹更改列表的 XML 请求。 这也是 EWS 托管 API 在 检索根文件夹更改列表时发送的 XML 请求。 本示例将 SyncState 元素值设置为在上一响应中返回的值。 出于演示目的,此示例将 BaseShape 元素设置为 AllProperties 而不是 IdOnly 以显示返回的其他属性。 将 BaseShape 元素设置为 IdOnly同步最佳做法SyncState 的值已缩短,以提高可读性。

<?xml version="1.0" encoding="utf-8"?>
<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="Exchange2013" />
  </soap:Header>
  <soap:Body>
    <m:SyncFolderHierarchy>
      <m:FolderShape>
        <t:BaseShape>AllProperties</t:BaseShape>
      </m:FolderShape>
      <m:SyncFolderId>
        <t:DistinguishedFolderId Id="root" />
      </m:SyncFolderId>
      <m:SyncState>H4sIAA==</m:SyncState>
    </m:SyncFolderHierarchy>
  </soap:Body>
</soap:Envelope>

以下示例显示了服务器在处理来自客户端的 SyncFolderHierarchy 操作 请求后返回的 XML 响应。 此响应指示更新了一个文件夹,创建了一个文件夹,并删除了一个文件夹。 为了提高可读性, SyncState 元素、 Id 属性和 ChangeKey 属性的值已缩短。

请记住,请求包含 AllPropertiesBaseShape。 这只是为了演示目的。 建议在生产环境中将 BaseShape 元素设置为 IdOnly

<?xml version="1.0" encoding="utf-8"?>
<s:Envelope xmlns:s="https://schemas.xmlsoap.org/soap/envelope/">
  <s:Header>
<h:ServerVersionInfo MajorVersion="15" MinorVersion="0" MajorBuildNumber="745" MinorBuildNumber="21" Version="V2_3" 
           xmlns:h="https://schemas.microsoft.com/exchange/services/2006/types" 
           xmlns="https://schemas.microsoft.com/exchange/services/2006/types" 
           xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" />
  </s:Header>
  <s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <m:SyncFolderHierarchyResponse xmlns:m="https://schemas.microsoft.com/exchange/services/2006/messages" 
            xmlns:t="https://schemas.microsoft.com/exchange/services/2006/types">
      <m:ResponseMessages>
        <m:SyncFolderHierarchyResponseMessage ResponseClass="Success">
          <m:ResponseCode>NoError</m:ResponseCode>
          <m:SyncState>H4sIAAA</m:SyncState>
          <m:IncludesLastFolderInRange>true</m:IncludesLastFolderInRange>
          <m:Changes>
            <t:Update>
              <t:Folder>
                <t:FolderId Id="AAMkADM=" ChangeKey="AQAAABY" />
                <t:ParentFolderId Id="AQMkADMzADI1==" ChangeKey="AQAAAA==" />
                <t:FolderClass>IPF.Note</t:FolderClass>
                <t:DisplayName>Meeting Notes</t:DisplayName>
                <t:TotalCount>3</t:TotalCount>
                <t:ChildFolderCount>0</t:ChildFolderCount>
                <t:EffectiveRights>
                  <t:CreateAssociated>true</t:CreateAssociated>
                  <t:CreateContents>true</t:CreateContents>
                  <t:CreateHierarchy>true</t:CreateHierarchy>
                  <t:Delete>true</t:Delete>
                  <t:Modify>true</t:Modify>
                  <t:Read>true</t:Read>
                  <t:ViewPrivateItems>true</t:ViewPrivateItems>
                </t:EffectiveRights>
                <t:UnreadCount>0</t:UnreadCount>
              </t:Folder>
            </t:Update>
            <t:Create>
              <t:Folder>
                <t:FolderId Id="AAMkADMzM=" ChangeKey="AQAAABYAA" />
                <t:ParentFolderId Id="AQMkO67A==" ChangeKey="AQAAAA==" />
                <t:FolderClass>IPF.Note</t:FolderClass>
                <t:DisplayName>Schedules</t:DisplayName>
                <t:TotalCount>0</t:TotalCount>
                <t:ChildFolderCount>0</t:ChildFolderCount>
                <t:EffectiveRights>
                  <t:CreateAssociated>true</t:CreateAssociated>
                  <t:CreateContents>true</t:CreateContents>
                  <t:CreateHierarchy>true</t:CreateHierarchy>
                  <t:Delete>true</t:Delete>
                  <t:Modify>true</t:Modify>
                  <t:Read>true</t:Read>
                  <t:ViewPrivateItems>true</t:ViewPrivateItems>
                </t:EffectiveRights>
                <t:UnreadCount>0</t:UnreadCount>
              </t:Folder>
            </t:Create>
            <t:Delete>
              <t:FolderId Id="AAMkAD/AAA=" ChangeKey="AQAAAA==" />
            </t:Delete>
          </m:Changes>
        </m:SyncFolderHierarchyResponseMessage>
      </m:ResponseMessages>
    </m:SyncFolderHierarchyResponse>
  </s:Body>
</s:Envelope>

更新客户端

如果使用 EWS 托管 API,在获取新文件夹或更改的文件夹列表后,请使用 Folder.Load 方法获取新项或已更改项的属性,将属性与本地值进行比较,并在客户端上更新或创建文件夹。

如果使用 EWS,请使用 GetFolder 操作 获取新文件夹或更改的文件夹的属性,并在客户端上更新或创建文件夹。

另请参阅