【发布时间】:2021-07-27 04:52:24
【问题描述】:
我需要使用 EWS 从“Inbox\test\final”Exchange 文件夹中检索项目。该文件夹由如上所述的文字路径提供。我知道我可以将此字符串拆分为文件夹名称并递归搜索必要的文件夹,但是是否有更优化的方法可以将字符串路径转换为文件夹实例或文件夹 ID?
我正在使用最新的 EWS 2.0 程序集。这些程序集是否提供任何帮助,还是我被手动递归困住了?
【问题讨论】:
我需要使用 EWS 从“Inbox\test\final”Exchange 文件夹中检索项目。该文件夹由如上所述的文字路径提供。我知道我可以将此字符串拆分为文件夹名称并递归搜索必要的文件夹,但是是否有更优化的方法可以将字符串路径转换为文件夹实例或文件夹 ID?
我正在使用最新的 EWS 2.0 程序集。这些程序集是否提供任何帮助,还是我被手动递归困住了?
【问题讨论】:
您可以像本例一样使用扩展属性
private string GetFolderPath(ExchangeService service, FolderId folderId)
{
var folderPathExtendedProp = new ExtendedPropertyDefinition(26293, MapiPropertyType.String);
var folderPropSet = new PropertySet(BasePropertySet.FirstClassProperties) { folderPathExtendedProp };
var folder = Folder.Bind(service, folderId, folderPropSet);
string path = null;
folder.TryGetProperty(folderPathExtendedProp, out path);
return path?.Replace("\ufffe", "\\");
}
【讨论】:
由于 Exchange Server 喜欢将所有内容与 Folder.Id 映射在一起,因此找到所需路径的唯一方法是查看文件夹名称。
您需要创建一个递归函数来遍历文件夹集合中的所有文件夹,并在路径在电子邮件文件夹树中移动时对其进行跟踪。
需要另一个参数来跟踪您正在寻找的路径。
public static Folder GetPathFolder(ExchangeService service, FindFoldersResults results,
string lookupPath, string currentPath)
{
foreach (Folder folder in results)
{
string path = currentPath + @"\" + folder.DisplayName;
if (folder.DisplayName == "Calendar")
{
continue;
}
Console.WriteLine(path);
FolderView view = new FolderView(50);
SearchFilter filter = new SearchFilter.IsEqualTo(FolderSchema.Id, folder.Id);
FindFoldersResults folderResults = service.FindFolders(folder.Id, view);
Folder result = GetPathFolder(service, folderResults, lookupPath, path);
if (result != null)
{
return result;
}
string[] pathSplitForward = path.Split(new[] { "/" }, StringSplitOptions.RemoveEmptyEntries);
string[] pathSplitBack = path.Split(new[] { @"\" }, StringSplitOptions.RemoveEmptyEntries);
string[] lookupPathSplitForward = lookupPath.Split(new[] { "/" }, StringSplitOptions.RemoveEmptyEntries);
string[] lookupPathSplitBack = lookupPath.Split(new[] { @"\" }, StringSplitOptions.RemoveEmptyEntries);
if (ArraysEqual(pathSplitForward, lookupPathSplitForward) ||
ArraysEqual(pathSplitBack, lookupPathSplitBack) ||
ArraysEqual(pathSplitForward, lookupPathSplitBack) ||
ArraysEqual(pathSplitBack, lookupPathSplitForward))
{
return folder;
}
}
return null;
}
“ArraysEqual”:
public static bool ArraysEqual<T>(T[] a1, T[] a2)
{
if (ReferenceEquals(a1, a2))
return true;
if (a1 == null || a2 == null)
return false;
if (a1.Length != a2.Length)
return false;
EqualityComparer<T> comparer = EqualityComparer<T>.Default;
for (int i = 0; i < a1.Length; i++)
{
if (!comparer.Equals(a1[i], a2[i])) return false;
}
return true;
}
我会进行所有额外的数组检查,因为有时我的客户会输入带有正斜杠、反斜杠、以斜杠开头的路径等。他们不精通技术,所以让我们确保程序每次都能正常工作!
当您浏览每个目录时,将所需路径与迭代路径进行比较。找到它后,将其当前所在的 Folder 对象冒泡。您需要为该文件夹的 id 创建一个搜索过滤器:
FindItemsResults<item> results = service.FindItems(foundFolder.Id, searchFilter, view);
在结果中循环浏览电子邮件!
foreach (Item item in results)
{
// do something with item (email)
}
【讨论】:
这是我的递归下降实现,它试图在到达目标文件夹的过程中获取尽可能少的信息:
private readonly FolderView _folderTraversalView = new FolderView(1) { PropertySet = PropertySet.IdOnly };
private Folder TraceFolderPathRec(string[] pathTokens, FolderId rootId)
{
var token = pathTokens.FirstOrDefault();
var matchingSubFolder = _exchangeService.FindFolders(
rootId,
new SearchFilter.IsEqualTo(FolderSchema.DisplayName, token),
_folderTraversalView)
.FirstOrDefault();
if (matchingSubFolder != null && pathTokens.Length == 1) return matchingSubFolder;
return matchingSubFolder == null ? null : TraceFolderPathRec(pathTokens.Skip(1).ToArray(), matchingSubFolder.Id);
}
对于以'/'分隔的路径,可以这样调用:
public Folder TraceFolderPath(string folderPath)
{ // Handle folder names with '/' in them
var tokens = folderPath
.Replace("\\/", "<slash>")
.Split('/')
.Select(t => t.Replace("<slash>", "/"))
.ToArray();
return TraceFolderPathRec(tokens, WellKnownFolderName.MsgFolderRoot);
}
【讨论】:
不,您不需要递归,您可以有效地直接进入文件夹。这使用与 Tom 相同的扩展属性,并使用它来应用搜索过滤器:
using Microsoft.Exchange.WebServices.Data; // from nuget package "Microsoft.Exchange.WebServices"
...
private static Folder GetOneFolder(ExchangeService service, string folderPath)
{
var propertySet = new PropertySet(BasePropertySet.IdOnly);
propertySet.AddRange(new List<PropertyDefinitionBase> {
FolderSchema.DisplayName,
FolderSchema.TotalCount
});
var pageSize = 100;
var folderView = new FolderView(pageSize)
{
Offset = 0,
OffsetBasePoint = OffsetBasePoint.Beginning,
PropertySet = propertySet
};
folderView.Traversal = FolderTraversal.Deep;
var searchFilter = new SearchFilter.IsEqualTo(ExchangeExtendedProperty.FolderPathname, folderPath);
FindFoldersResults findFoldersResults;
var baseFolder = new FolderId(WellKnownFolderName.MsgFolderRoot);
var localFolderList = new List<Folder>();
do
{
findFoldersResults = service.FindFolders(baseFolder, searchFilter, folderView);
localFolderList.AddRange(findFoldersResults.Folders);
folderView.Offset += pageSize;
} while (findFoldersResults.MoreAvailable);
return localFolderList.SingleOrDefault();
}
...
public static class ExchangeExtendedProperty
{
/// <summary>PR_FOLDER_PATHNAME String</summary>
public static ExtendedPropertyDefinition FolderPathname { get => new ExtendedPropertyDefinition(0x66B5, MapiPropertyType.String); }
}
路径需要以反斜杠作为前缀,即。 \Inbox\test\final.
【讨论】: