6

I have the below code which returns all users based on a filter. The problem is it only returns 100 users but I know there are a lot more.

private List<User> GetUsersFromGraph()
{
    if (_graphAPIConnectionDetails == null) ReadParametersFromXML();
    if (graphServiceClient == null) graphServiceClient = CreateGraphServiceClient();

    var users = graphServiceClient
        .Users
        .Request()
        .Filter(_graphAPIConnectionDetails.UserFilter)
        .Select(_graphAPIConnectionDetails.UserAttributes)
        .GetAsync()
        .Result
        .ToList<User>();

    return users;
}

the method returns only 100 user objects. My Azure portal admin reports there should be closer to 60,000.

Marc LaFleur
  • 31,987
  • 4
  • 37
  • 63
gtrivedi
  • 435
  • 1
  • 7
  • 18

2 Answers2

16

Most of the endpoints in Microsoft Graph return data in pages, this includes /users.

In order to retrieve the rest of the results you need to look through the pages:

private async Task<List<User>> GetUsersFromGraph()
{
    if (_graphAPIConnectionDetails == null) ReadParametersFromXML();
    if (graphServiceClient == null) graphServiceClient = CreateGraphServiceClient();

    // Create a bucket to hold the users
    List<User> users = new List<User>();

    // Get the first page
    IGraphServiceUsersCollectionPage usersPage = await graphClient
        .Users
        .Request()
        .Filter("filter string")
        .Select("property string")
        .GetAsync();

    // Add the first page of results to the user list
    users.AddRange(usersPage.CurrentPage);

    // Fetch each page and add those results to the list
    while (usersPage.NextPageRequest != null)
    {
        usersPage = await usersPage.NextPageRequest.GetAsync();
        users.AddRange(usersPage.CurrentPage);
    }

    return users;
}

One super important note here, this method is the lest performant way to retrieve data from Graph (or any REST API really). Your app will be sitting there for a long time while it downloads all this data. The proper methodology here is to fetch each page and process just that page before fetching additional data.

Marc LaFleur
  • 31,987
  • 4
  • 37
  • 63
  • I agree. This is part of once a day batch job so I think I'd be ok. But I do take your point if I want to use this code on a user operated app. Then it would be a problem. – gtrivedi Jun 24 '19 at 13:52
  • @Marc LaFleur, i am using this approach, but as you said this takes huge amount of time... but i need to have the users in a list somehow, because when i go to create a collaborator i have this dropdown with azure AD users, i select them to create the user with that info... How can i achive this with best performance? – Ricardo Figueiredo Oct 13 '20 at 11:05
  • 1
    If you have enough users that this takes a large amount of time, they likely shouldn't be in a dropdown. You'd be better off with a search box that filters the user list. – Marc LaFleur Oct 21 '20 at 21:16
0

I had a similar use case where the return of my query was > 100.

    final GroupCollectionPage userGroups = _appClient.users({id})
            .memberOfAsGroup()
            .buildRequest(requestOptions)
            .select("displayName,id,mail")
            .filter("startswith(displayName, 'c')")
            .orderBy("displayName")
            .get();

So I could easily iterate over the result set

    // Output each Group details
    for (Group usergroup : userGroups.getCurrentPage()) {
        System.out.println("  User Group Name: " + usergroup.displayName);
        System.out.println("  ID: " + usergroup.id);
        System.out.println(" Email: " + usergroup.mail);
    }

Here's how to get the next page of the user groups

  public static void getGroups() {
    LinkedList<Option> requestOptions = new LinkedList<Option>();
    requestOptions.add(new HeaderOption("ConsistencyLevel", "eventual"));
    requestOptions.add(new QueryOption("$count", "true"));

    GroupCollectionPage userGroups = _appClient.users({id})
            .memberOfAsGroup()
            .buildRequest(requestOptions)
            .select("displayName,id,mail")
            .filter("startswith(displayName, 'c')")
            .orderBy("displayName")
            .get();

    List<Group> allGroupsList = new ArrayList<>();

    do {
        List<Group> currentPageGroup = userGroups.getCurrentPage();
        allGroupsList.addAll(currentPageGroup);
        GroupCollectionRequestBuilder nextPage = userGroups.getNextPage();
        userGroups = nextPage == null ? null : nextPage.buildRequest().get();
    } while (userGroups != null);

    System.out.println("Total Group count is :" + allGroupsList.size());

    for (Group usergroup : allGroupsList) {
        System.out.println("  User Group Name: " + usergroup.displayName);
        System.out.println("  ID: " + usergroup.id);
        System.out.println("  Email: " + usergroup.mail);
    }
}