Introduction
Managing users in large organizations can be a daunting task, especially when it comes to adding multiple users to groups. Thankfully, the Microsoft Graph API and all SDKs offer a powerful solution for batch processing, making this task more efficient and less error-prone.
Batching
Batching allows you to send multiple requests to the Microsoft Graph API in a single HTTP request. This can help you improve the performance of your application by reducing the number of HTTP requests that need to be made.
Batching with the Microsoft Graph C# SDK
The Microsoft Graph C# SDK provides these classes to help you work with batch requests and responses:
BatchRequestContentCollection
- Represents a collection of all batch requestsBatchResponseContentCollection
- Represents a collection of all batch responses
As you may know, the Microsoft Graph API has a limit of 20 requests per batch. The BatchRequestContentCollection
class automatically splits the requests into multiple batches if the number of requests exceeds this limit of 20 requests.
Add requests to the batch
The SDK uses a builder pattern to easily construct a HTTP request. The request itself contains information about the URL, query parameters and selected HTTP method.
When working with batch requests, you need to create an instance of the RequestInformation
for a specific request and add it to the BatchRequestContentCollection
via the AddBatchRequestStepAsync
method.
The second parameter of the AddBatchRequestStepAsync
method is the requestId
which can be used to identify the response for a specific request.
// request info to make a GET request to the /me endpoint
var requestInfo = client.Me.ToGetRequestInformation();
await batchCollection.AddBatchRequestStepAsync(requestInfo, requestId: "me");
// request info to make a DELETE request to the /sites/{site-id}/lists/{list-id}/items/{item-id} endpoint
var requestInfo = client.Sites["{site-id}"].Lists["{list-id}"].Items["{item-id}"].ToDeleteRequestInformation();
await batchCollection.AddBatchRequestStepAsync(requestInfo, requestId: "removeListItem");
Once you have added all the requests to the BatchRequestContentCollection
object, you can send the batch request(s) to the Microsoft Graph API.
var batchResponse = await client.Batch.PostAsync(batchCollection);
If the batchCollection
contains more than 20 requests, the SDK will automatically split the requests into multiple batches, send them to the Microsoft Graph API and collect responses for all batches.
Process the batch response
The BatchResponseContentCollection
object contains the responses for all the requests in the batch.
You can access the response for a specific request id by using the GetResponseByIdAsync(requestId)
method.
If you want to only check the status codes of all responses, you can use the GetResponsesStatusCodesAsync()
method.
Process the error
What if one of the requests in the batch fails? Currently, the SDK does not provide any method to get the error. Just try to read the response and handle the ServiceException
exception.
If the ServiceException
exception is thrown, you need to deserialize the RawResponseBody
to the ODataError
object to get the error message.
try
{
var user = await batchResponse.GetResponseByIdAsync<User>("requestId");
}
catch (ServiceException error)
{
var odataError = await KiotaJsonSerializer.DeserializeAsync<ODataError>(error.RawResponseBody);
Console.WriteLine($"Error: {odataError.Message}");
}
If the request inside the batch doesn't return a response body, you need to use a different approach to get the error:
var responseMessage = await batchResponse.GetResponseByIdAsync("requestId");
var stringContent = await responseMessage.Content.ReadAsStringAsync();
var odataError = await KiotaJsonSerializer.DeserializeAsync<ODataError>(stringContent);
Console.WriteLine($"Error: {odataError.Message}");
Add users to a group in a batch
Let's try to create a collection of new users and add them to a group.
Create new users
The first step is to send a batch request to create new users.
// init a collection of batch requests
var batchCollection = new BatchRequestContentCollection(client);
var userIds = new List<string>();
for (int i = 1; i <= 55; i++)
{
// create a new instance of User model and set the required properties
var newUser = new User
{
AccountEnabled = true,
DisplayName = $"User {i}",
MailNickname = $"user{i}",
UserPrincipalName = $"user{i}@contoso.onmicrosoft.com",
PasswordProfile = new PasswordProfile
{
Password = $"Pa$$w0rd{i}",
ForceChangePasswordNextSignIn = false
}
};
// create request information about the new user
var requestInfo = client.Users.ToPostRequestInformation(newUser);
// add request info with unique request id to the batch collection
await batchCollection.AddBatchRequestStepAsync(requestInfo, requestId: $"user{i}");
}
// send all batch requests
var batchResponse = await client.Batch.PostAsync(batchCollection);
// read responses status codes
var responsesStatusCodes = await batchResponse.GetResponsesStatusCodesAsync();
foreach (var response in responsesStatusCodes)
{
Console.WriteLine($"Response {response.Key} with status code {response.Value}");
try
{
var user = await batchResponse.GetResponseByIdAsync<User>(response.Key);
userIds.Add(user.Id);
}
catch (ServiceException error)
{
var odataError = await KiotaJsonSerializer.DeserializeAsync<ODataError>(error.RawResponseBody);
Console.WriteLine($"Error: {odataError.Message}");
}
}
Add users to a group
The next step is to add the newly created users to a group. The Graph API provides two endpoints to add members to a group:
POST https://graph.microsoft.com/v1.0/groups/{group-id}/members/$ref
- allows to add a single memberPATCH https://graph.microsoft.com/v1.0/groups/{group-id}
- allows to add up to 20 members
In our case, we will use the second endpoint which allows us to add up to 20 members in a single request. When using the batch request, you can add up to 400 members in a single batch.
var batchCollection = new BatchRequestContentCollection(client);
for (int i = 0; i < userIds.Count; i += 20)
{
var selectedUserIds = userIds.Skip(i).Take(20);
var members = selectedUserIds.Select(id => $"https://graph.microsoft.com/v1.0/directoryObjects/{id}");
var requestBody = new Group
{
AdditionalData = new Dictionary<string, object>
{
{
"members@odata.bind" , members
}
}
};
var request = client.Groups[groupId].ToPatchRequestInformation(requestBody);
await batchCollection.AddBatchRequestStepAsync(request, $"batch{i}");
}
var batchResponse = await client.Batch.PostAsync(batchCollection);
var responsesStatusCodes = await batchResponse.GetResponsesStatusCodesAsync();
foreach (var response in responsesStatusCodes)
{
Console.WriteLine($"Response {response.Key} with status code {response.Value}");
if (response.Value != System.Net.HttpStatusCode.NoContent)
{
try
{
var responseMessage = await batchResponse.GetResponseByIdAsync(response.Key);
var stringContent = await responseMessage.Content.ReadAsStringAsync();
var odataError = await KiotaJsonSerializer.DeserializeAsync<ODataError>(stringContent);
Console.WriteLine($"Error: {odataError.Message}");
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
}
}
Now you can check that all users have been created and added to the group.
Conclusion
Batch processing with the Microsoft Graph API can significantly streamline user management tasks.
You can find the complete code sample in the GitHub repository