You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Microsoft Graph和类Linq语法查询SharePoint未导入文件?

在Microsoft Graph中高效获取SharePoint未导入文件的C#实现

针对你遇到的全量遍历文件导致性能下降的问题,以下是三种可行的实现方案,根据已导入文件的规模选择合适的方式:

方案1:小批量已导入文件时,服务端直接过滤

当已导入文件数量较少(几百个以内),可以通过Graph的$filter拼接多个ne条件,直接在服务端过滤掉已导入的文件,减少本地处理的数据量。

// 从数据库获取已导入文件名列表
List<string> importedFileNames = await GetImportedFileNamesFromDbAsync();

// 构建Graph过滤条件,注意转义文件名中的单引号
string filter = string.Join(" and ", importedFileNames.Select(name => $"fileName ne '{EscapeSingleQuotes(name)}'"));

// 查询指定SharePoint文件夹下未导入的文件
var result = await graphClient.Sites["{site-id}"].Drives["{drive-id}"].Items["{folder-id}"].Children
    .Request()
    .Filter(filter)
    .Select("id, name, fileName, lastModifiedDateTime") // 仅获取必要字段,降低传输开销
    .GetAsync();

List<DriveItem> unimportedFiles = result.CurrentPage.ToList();
// 处理分页结果
while (result.NextPageRequest != null)
{
    result = await result.NextPageRequest.GetAsync();
    unimportedFiles.AddRange(result.CurrentPage);
}

// 辅助方法:转义单引号避免Graph查询语法错误
string EscapeSingleQuotes(string input) => input.Replace("'", "''");

方案2:大批量已导入文件时,内存高效过滤

当已导入文件数量较多,拼接$filter会触发Graph的请求长度限制,此时先拉取所有文件(仅必要字段),再用HashSet做O(1)时间复杂度的存在性判断,比List.Contains的O(n)效率提升显著。

// 从数据库获取已导入文件名并转为HashSet,提升查询性能
HashSet<string> importedFileNamesSet = new HashSet<string>(await GetImportedFileNamesFromDbAsync(), StringComparer.OrdinalIgnoreCase);

// 拉取文件夹下所有文件
var allFiles = new List<DriveItem>();
var result = await graphClient.Sites["{site-id}"].Drives["{drive-id}"].Items["{folder-id}"].Children
    .Request()
    .Select("id, name, fileName, lastModifiedDateTime")
    .GetAsync();

allFiles.AddRange(result.CurrentPage);
while (result.NextPageRequest != null)
{
    result = await result.NextPageRequest.GetAsync();
    allFiles.AddRange(result.CurrentPage);
}

// 过滤未导入的文件
List<DriveItem> unimportedFiles = allFiles.Where(file => !importedFileNamesSet.Contains(file.FileName)).ToList();

方案3:最优方案——用Delta Query跟踪文件变更

如果你的需求是定期爬取新增/修改的文件,推荐使用Graph的Delta Query,它会返回自上次查询以来的所有变更(新增、修改、删除),无需全量遍历对比,性能最优。

// 从数据库获取上次保存的delta链接(首次查询时为null)
string deltaLink = await GetLastDeltaLinkFromDbAsync();

IDriveItemDeltaCollectionPage deltaResult;

if (string.IsNullOrEmpty(deltaLink))
{
    // 首次查询:初始化delta请求
    deltaResult = await graphClient.Sites["{site-id}"].Drives["{drive-id}"].Items["{folder-id}"].Children
        .Delta()
        .Request()
        .Select("id, name, fileName, lastModifiedDateTime")
        .GetAsync();
}
else
{
    // 后续查询:使用上次的delta链接获取变更
    deltaResult = await graphClient.Sites["{site-id}"].Drives["{drive-id}"].Items["{folder-id}"].Children
        .Delta()
        .Request()
        .WithUrl(deltaLink)
        .GetAsync();
}

// 收集所有变更的文件
List<DriveItem> changedFiles = deltaResult.CurrentPage.ToList();
while (deltaResult.NextPageRequest != null)
{
    deltaResult = await deltaResult.NextPageRequest.GetAsync();
    changedFiles.AddRange(deltaResult.CurrentPage);
}

// 保存新的delta链接到数据库,供下次查询使用
if (deltaResult.AdditionalData.TryGetValue("@odata.deltaLink", out object newDeltaLink))
{
    await SaveDeltaLinkToDbAsync(newDeltaLink.ToString());
}

// changedFiles即为自上次爬取以来新增/修改的文件,可直接导入数据库
// 若需排除已导入的修改文件,可结合方案2的HashSet进行二次过滤

注意事项

  • 确保应用已配置Files.Read.All或Sites.Read.All权限(根据场景选择)
  • 始终使用Select指定必要字段,减少网络传输数据量
  • HashSet的字符串比较规则可根据需求调整大小写敏感性
  • Delta Query会返回删除的文件,若无需处理可过滤掉deleted状态的项

内容的提问来源于stack exchange,提问作者Andrew Casey

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 12:58:18