Search code examples
microsoft-graph-apimicrosoft-graph-sdks

C# graph SDK - serializing the results of a batch request


I'm building a program that fetches calendar info from multiple users in AAD. I would like to do this as efficiently as possible, so I started looking into the Microsoft graph batching functionality. I'm able to successfully do a batching query, but I'm having issues to serialize the results:

//1. construct a Batch request 
var batchRequestContent = new BatchRequestContent();
var step = 1;
foreach (var userEmail in userEmails)
{
    var requestUrl = graphServiceClient
        .Users[userEmail]
        .Calendar.Events
        .Request(new List<QueryOption>
        {
            new QueryOption("startDateTime", start.ToString("o")),
            new QueryOption("endDateTime", end.ToString("o"))
        });

    var request = new HttpRequestMessage(HttpMethod.Get, requestUrl.RequestUrl);
    var requestStep = new BatchRequestStep(step.ToString(), request);
    batchRequestContent.AddBatchRequestStep(requestStep);
    step++;
}


//2. Submit request
var batchRequest = new HttpRequestMessage(HttpMethod.Post, "https://graph.microsoft.com/v1.0/$batch")
{
    Content = batchRequestContent
};
await graphServiceClient.AuthenticationProvider.AuthenticateRequestAsync(batchRequest);
var httpClient = new HttpClient();
var batchResponse = await httpClient.SendAsync(batchRequest);
//3. Process response
var batchResponseContent = new BatchResponseContent(batchResponse);
var responses = await batchResponseContent.GetResponsesAsync();
var responseHandler = new ResponseHandler(graphServiceClient.HttpProvider.Serializer);
foreach (var response in responses)
{
    if (response.Value.IsSuccessStatusCode)
    {

        var responsestring = await response.Value.Content.ReadAsStringAsync();

        var responseEvent = //?
    }
}

Above all works, but how do I serialize this result to a strongly typed list of Events?

EDIT I tried deserializing with the ResponseHandler like this:

var batchResponseContent = new BatchResponseContent(batchResponse);
        var responses = await batchResponseContent.GetResponsesAsync();
        var responseHandler = new ResponseHandler(new Serializer());
        foreach (var response in responses)
        {
            if (response.Value.IsSuccessStatusCode)
            {
                var events = responseHandler.HandleResponse<ICalendarEventsCollectionPage>(response.Value);
                //...
            }
        }

But this errors out and throws the following exception:

 Newtonsoft.Json.JsonSerializationException: Cannot populate JSON object onto type 'Microsoft.Graph.CalendarEventsCollectionPage'. Path '['@odata.context']', line 2, position 19.

It seems the @odata.context is responsible for the error, see image below for the actual response I get from above request:

enter image description here


Solution

  • In case someone wants the full solution I went with, I make batch requests like this and they are working fine now. There was a serialisation issue in the client library, but that's fixed since I asked this question (https://github.com/microsoftgraph/msgraph-sdk-dotnet/issues/587)

    //You need these packages/namespaces
    //<PackageReference Include="Microsoft.Extensions.Configuration" Version="2.2.0" />
    //<PackageReference Include="Microsoft.Graph" Version="1.20.0" />
    //<PackageReference Include="Microsoft.Graph.Auth" Version="1.0.0-preview.0" />
    //<PackageReference Include="Microsoft.Graph.Core" Version="1.19.0-preview.3" />
    //<PackageReference Include="Microsoft.Identity.Client" Version="4.3.0" />
    
    //using Microsoft.Graph;
    //using Microsoft.Graph.Extensions;
    //using Microsoft.Graph.Auth;
    //using Microsoft.Identity.Client;      
    var confidentialClientApplication = ConfidentialClientApplicationBuilder
                  .Create("YOUR CLIENT ID")
                  .WithTenantId("YOUR TENANT ID")
                  .WithClientSecret("YOUR CLIENT SECRET")
                  .Build();
    
            var forUser = "YOUR USER'S EMAIL ADDRESS";
    
            var authenticationProvider = new ClientCredentialProvider(confidentialClientApplication);
            var graphServiceClient = new GraphServiceClient(authenticationProvider);
    
            var eventRequest = graphServiceClient.Users[forUser].Calendar.CalendarView.Request(
                new List<QueryOption>
                  {
                      new QueryOption("startDateTime", DateTime.UtcNow.Date.AddMonths(-3).ToString("O")),
                      new QueryOption("endDateTime", DateTime.UtcNow.Date.ToString("O"))
                  }
               );
    
            var batchRequestContent = new BatchRequestContent();
    
            var reqId = batchRequestContent.AddBatchRequestStep(eventRequest);
            // add second, 3rd request here
    
            var returnedResponse = await graphServiceClient.Batch.Request().PostAsync(batchRequestContent);
            var batchEvts = await returnedResponse.GetResponseByIdAsync<CalendarEventsCollectionResponse>(reqId);
            // read second, 3rd responses here