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

在C#控制台应用中结合Microsoft Graph批量请求与分页获取超900个用户的实现方案咨询

在C#控制台应用中结合Microsoft Graph批量请求与分页获取超900个用户的实现方案咨询

没问题,你的思路完全可行!我们完全可以把每个分页的用户查询请求加入批量请求中,一次性或分批次发送,最后合并所有分页的结果得到完整的用户列表。下面给你两种实用的实现方案,你可以根据自己的业务场景选择:


方案一:基于$skip/$top预生成批量请求(高效优先)

这种方式适合数据变化不频繁的场景,我们可以直接预先生成多个分页请求(每个请求跳过前面的N个用户),把它们全部加入批量请求后一次性或分批次发送,最后合并结果。优点是减少网络请求次数,效率更高;缺点是如果请求过程中用户数据有新增/删除,可能会出现重复或遗漏的情况。

代码示例

using Microsoft.Graph;
using Microsoft.Graph.Models;
using Microsoft.Kiota.Abstractions;

// 初始化Graph客户端
var graphClient = new GraphServiceClient(interactiveCredential, scopes);
var allUsers = new List<User>();
const int pageSize = 100;
const int maxBatchRequests = 20; // Graph批量请求的单批次最大请求数限制

try
{
    // 第一步:先获取总用户数,确定需要多少个分页请求
    var countRequest = graphClient.Users.Count.ToGetRequestInformation();
    var totalUsers = await graphClient.RequestAdapter.SendAsync(countRequest, long.CreateFromDiscriminatorValue);
    var totalPages = (int)Math.Ceiling(totalUsers / (double)pageSize);

    // 第二步:把分页请求分批次加入批量集合
    var batchCollections = new List<BatchRequestContentCollection>();
    var currentBatch = new BatchRequestContentCollection(graphClient);

    for (int pageIndex = 0; pageIndex < totalPages; pageIndex++)
    {
        var skip = pageIndex * pageSize;
        // 创建当前分页的请求信息,建议只获取需要的属性减少数据量
        var pageRequest = graphClient.Users.ToGetRequestInformation(opt =>
        {
            opt.QueryParameters.Top = pageSize;
            opt.QueryParameters.Skip = skip;
            opt.QueryParameters.Select = new[] { "id", "displayName", "mail" };
        });

        // 加入当前批次
        await currentBatch.AddBatchRequestStepAsync(pageRequest);

        // 如果当前批次达到最大限制,就存入批次列表并新建批次
        if ((pageIndex + 1) % maxBatchRequests == 0)
        {
            batchCollections.Add(currentBatch);
            currentBatch = new BatchRequestContentCollection(graphClient);
        }
    }

    // 把最后一个未填满的批次也加入列表
    if (currentBatch.GetSteps().Any())
    {
        batchCollections.Add(currentBatch);
    }

    // 第三步:执行所有批次的批量请求,收集结果
    foreach (var batch in batchCollections)
    {
        var batchResponse = await graphClient.Batch.PostAsync(batch);
        foreach (var stepId in batchResponse.GetResponseIds())
        {
            var pageResponse = await batchResponse.GetResponseByIdAsync<UserCollectionResponse>(stepId);
            if (pageResponse?.Value != null)
            {
                allUsers.AddRange(pageResponse.Value);
            }
        }
    }

    Console.WriteLine($"成功获取所有用户,总数量:{allUsers.Count}");
}
catch (Exception ex)
{
    Console.WriteLine($"请求过程中出现错误:{ex.Message}");
}

方案二:基于nextLink的动态批量分页(准确性优先)

如果你的业务场景对数据准确性要求极高(比如不允许出现重复或遗漏用户),那建议用这种方式。因为Graph返回的@odata.nextLink包含了分页的令牌,能保证数据的一致性,避免因数据变更导致的问题。

实现思路是:先获取第一页用户并拿到nextLink,然后把后续每个nextLink对应的请求加入批量(可以一次加入多个),执行批量后收集结果,再继续处理剩余的nextLink,直到没有下一页为止。

代码示例

using Microsoft.Graph;
using Microsoft.Graph.Models;
using Microsoft.Kiota.Abstractions;

var graphClient = new GraphServiceClient(interactiveCredential, scopes);
var allUsers = new List<User>();
const int batchSize = 5; // 每次批量发送5个分页请求
string nextLink = null;

try
{
    // 第一步:获取第一页用户
    var firstPageRequest = graphClient.Users.ToGetRequestInformation(opt =>
    {
        opt.QueryParameters.Select = new[] { "id", "displayName", "mail" };
    });
    var firstPageResponse = await graphClient.RequestAdapter.SendAsync(firstPageRequest, UserCollectionResponse.CreateFromDiscriminatorValue);
    allUsers.AddRange(firstPageResponse.Value);
    nextLink = firstPageResponse.OdataNextLink;

    // 第二步:循环处理剩余的分页,批量发送请求
    while (!string.IsNullOrEmpty(nextLink))
    {
        var currentBatch = new BatchRequestContentCollection(graphClient);
        var tempNextLinks = new List<string>();
        tempNextLinks.Add(nextLink);

        // 收集足够数量的nextLink请求,或者直到没有下一页
        for (int i = 1; i < batchSize && !string.IsNullOrEmpty(nextLink); i++)
        {
            var tempRequest = new RequestInformation
            {
                HttpMethod = Method.GET,
                Uri = new Uri(nextLink)
            };
            var tempResponse = await graphClient.RequestAdapter.SendAsync(tempRequest, UserCollectionResponse.CreateFromDiscriminatorValue);
            tempNextLinks.Add(tempResponse.OdataNextLink);
            nextLink = tempResponse.OdataNextLink;
        }

        // 把收集到的nextLink请求加入批量
        foreach (var link in tempNextLinks.Where(l => !string.IsNullOrEmpty(l)))
        {
            var pageRequest = new RequestInformation
            {
                HttpMethod = Method.GET,
                Uri = new Uri(link)
            };
            await currentBatch.AddBatchRequestStepAsync(pageRequest);
        }

        // 执行批量请求并收集结果
        var batchResponse = await graphClient.Batch.PostAsync(currentBatch);
        foreach (var stepId in batchResponse.GetResponseIds())
        {
            var pageResponse = await batchResponse.GetResponseByIdAsync<UserCollectionResponse>(stepId);
            if (pageResponse?.Value != null)
            {
                allUsers.AddRange(pageResponse.Value);
            }
        }
    }

    Console.WriteLine($"成功获取所有用户,总数量:{allUsers.Count}");
}
catch (Exception ex)
{
    Console.WriteLine($"请求过程中出现错误:{ex.Message}");
}

额外提示

  • 注意Microsoft Graph的批量请求单批次最多支持20个请求,所以两种方案都做了分批次处理的逻辑。
  • 始终建议在请求中添加$select参数,只获取你需要的用户属性,这样可以大幅减少数据传输量,提升请求速度。
  • 如果不确定总用户数,方案一可以去掉获取总用户数的步骤,改成循环生成请求直到某个请求返回的用户数量小于pageSize为止。

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.08 07:33:12