Microsoft Graph SDK を使用して要求をバッチ処理する

バッチ処理 は、複数の要求を 1 つの HTTP 要求に結合する方法です。 要求は 1 つの JSON ペイロードに結合され、POST 経由でエンドポイントに \$batch 送信されます。 Microsoft Graph SDK には、バッチ ペイロードを作成し、バッチ応答ペイロードを解析する方法を簡略化するための一連のクラスがあります。

重要

Microsoft Graph での JSON バッチ処理に関する現在の制限事項については、「 既知の問題」を参照してください。

バッチ要求の作成

Microsoft Graph SDK には、バッチ要求と応答を処理するための 3 つのクラスが用意されています。

  • BatchRequestStep - バッチ 内の 1 つの要求 (など GET /me) を表します。 これにより、要求に一意の識別子を割り当て、要求間の依存関係を指定できます。
  • BatchRequestContent - バッチ要求ペイロードの作成を簡略化します。 複数の BatchRequestStep オブジェクトが 含まれています。
  • BatchResponseContent - バッチ 要求からの応答の解析を簡略化します。 すべての応答を取得し、ID で特定の応答を取得し、存在する場合はプロパティを取得する機能を @odata.nextLink 提供します。

単純なバッチ処理の例

この例では、相互に依存しない複数の要求をバッチで送信する方法を示します。 要求は、任意の順序でサービスによって実行できます。 この例では、ユーザーを取得し、現在の日のユーザーの予定表ビューを取得します。

// Use the request builder to generate a regular
// request to /me
var userRequest = graphClient.Me.ToGetRequestInformation();

var today = DateTime.Now.Date;

// Use the request builder to generate a regular
// request to /me/calendarview?startDateTime="start"&endDateTime="end"
var eventsRequest = graphClient.Me.CalendarView
    .ToGetRequestInformation(requestConfiguration => 
        {
            requestConfiguration.QueryParameters.StartDateTime = today.ToString("yyyy-MM-ddTHH:mm:ssK");
            requestConfiguration.QueryParameters.EndDateTime = today.AddDays(1).ToString("yyyy-MM-ddTHH:mm:ssK");
        });

// Build the batch
var batchRequestContent = new BatchRequestContent(graphClient);

// Using AddBatchRequestStepAsync adds each request as a step
// with no specified order of execution
var userRequestId = await batchRequestContent.AddBatchRequestStepAsync(userRequest);
var eventsRequestId = await batchRequestContent.AddBatchRequestStepAsync(eventsRequest);

var returnedResponse = await graphClient.Batch.PostAsync(batchRequestContent);

// De-serialize response based on known return type
try
{
    var user = await returnedResponse
        .GetResponseByIdAsync<User>(userRequestId);
    Console.WriteLine($"Hello {user.DisplayName}!");
}
catch (ServiceException ex)
{
    Console.WriteLine($"Get user failed: {ex.Error.Message}");
}

// For collections, must use the *CollectionResponse class to deserialize
// The .Value property will contain the *CollectionPage type that the Graph client
// returns from GetAsync().
try
{
    var events = await returnedResponse
        .GetResponseByIdAsync<EventCollectionResponse>(eventsRequestId);
    Console.WriteLine($"You have {events.Value.Count} events on your calendar today.");
}
catch (ServiceException ex)
{
    Console.WriteLine($"Get calendar view failed: {ex.Error.Message}");
}

依存する要求を含むバッチ

この例では、相互に依存する複数の要求をバッチで送信する方法を示します。 要求は、依存関係によって指定された順序でサービスによって実行されます。 次の使用例は、現在の日の開始時刻を持つイベントをユーザーの予定表に追加し、現在の日のユーザーの予定表ビューを取得します。 返された予定表レビューに作成された新しいイベントが含まれていることを確認するために、予定表ビューの要求は、新しいイベントを追加する要求に依存するように構成されます。 これにより、最初にイベントの追加要求が実行されます。

注:

イベントの追加要求が失敗した場合、予定表ビューの取得要求はエラーで 424 Failed Dependency 失敗します。

var today = DateTime.Now.Date;

var newEvent = new Event
{
    Subject = "File end-of-day report",
    Start = new DateTimeTimeZone
    {
        // 5:00 PM
        DateTime = today.AddHours(17).ToString("yyyy-MM-ddTHH:mm:ss"),
        TimeZone = TimeZoneInfo.Local.StandardName
    },
    End = new DateTimeTimeZone
    {
        // 5:30 PM
        DateTime = today.AddHours(17).AddMinutes(30).ToString("yyyy-MM-ddTHH:mm:ss"),
        TimeZone = TimeZoneInfo.Local.StandardName
    }
};

// Use the request builder to generate a regular
// POST request to /me/events
var addEventRequest = graphClient.Me.Events.ToPostRequestInformation(newEvent);

// Use the request builder to generate a regular
// request to /me/calendarview?startDateTime="start"&endDateTime="end"
var calendarViewRequest = graphClient.Me.CalendarView.ToGetRequestInformation(
    requestConfiguration => {
        requestConfiguration.QueryParameters.StartDateTime = today.ToString("yyyy-MM-ddTHH:mm:ssK");
        requestConfiguration.QueryParameters.EndDateTime = today.AddDays(1).ToString("yyyy-MM-ddTHH:mm:ssK");
    });

// Build the batch
var batchRequestContent = new BatchRequestContent(graphClient);

// Force the requests to execute in order, so that the request for
// today's events will include the new event created.

// First request, no dependency
var addEventRequestId = await batchRequestContent.AddBatchRequestStepAsync(addEventRequest);

// Second request, depends on addEventRequestId
var eventsRequestId = Guid.NewGuid().ToString();
var eventsRequestMessage = await graphClient.RequestAdapter.ConvertToNativeRequestAsync<HttpRequestMessage>(calendarViewRequest);
batchRequestContent.AddBatchRequestStep(new BatchRequestStep(
    eventsRequestId,
    eventsRequestMessage,
    new List<string> { addEventRequestId }
));

var returnedResponse = await graphClient.Batch.PostAsync(batchRequestContent);

// De-serialize response based on known return type
try
{
    var createdEvent = await returnedResponse
        .GetResponseByIdAsync<Event>(addEventRequestId);
    Console.WriteLine($"New event created with ID: {createdEvent.Id}");
}
catch (ServiceException ex)
{
    Console.WriteLine($"Add event failed: {ex.Error.Message}");
}

// For collections, must use the *CollectionResponse class to deserialize
// The .Value property will contain the *CollectionPage type that the Graph client
// returns from GetAsync().
try
{
    var events = await returnedResponse
        .GetResponseByIdAsync<EventCollectionResponse>(eventsRequestId);
    Console.WriteLine($"You have {events.Value.Count} events on your calendar today.");
}
catch (ServiceException ex)
{
    Console.WriteLine($"Get calendar view failed: {ex.Error.Message}");
}

BatchRequestContent、BatchRequestStep、HttpRequestMessage を使用したバッチ処理の実装

次の例では、 をBatchRequestStep使用BatchRequestContentし、複数の要求をバッチで送信する方法と、HttpRequestMessageMicrosoft Graph API要求で 20 の制限を処理する方法を示します。 次の使用例は、指定したユーザー ID のエンドポイントを onlineMeetings/createOrGet 使用して会議リンクを作成します。 この例は、他の Microsoft Graph エンドポイントでも使用できます。

using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Graph;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

public async void GenerateBatchedMeetingLink(List<ItemCollections> meetingLinksToBeGenerated)
{
    List<string> _joinWebUrls = new List<string>();
    //Total number of items per batch supported is 20
    int maxNoBatchItems = 20;
    try
    {
        //valid GraphAccessToken is required to execute the call
        var graphClient = GetAuthenticatedClient(GraphAccessToken);
        var events = new List<OnlineMeeting>();
        foreach (var item in meetingLinksToBeGenerated)
        {
            var externalId = Guid.NewGuid().ToString();
            var @event = new OnlineMeeting
            {
                StartDateTime = item.StartTime,
                EndDateTime = item.EndTime,
                Subject = "Test Meeting",
                ExternalId = externalId,

            };
            events.Add(@event);
        }
        // if the requests are more than 20 limit, we need to create multiple batches of the BatchRequestContent
        List<BatchRequestContent> batches = new List<BatchRequestContent>();
        var batchRequestContent = new BatchRequestContent(graphClient);
        foreach (OnlineMeeting e in events)
        {
            //create online meeting for particular user or we can use /me as well
            var httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, $"https://graph.microsoft.com/v1.0/users/{userID}/onlineMeetings/createOrGet")
            {
                Content = new StringContent(JsonConvert.SerializeObject(e), Encoding.UTF8, "application/json")
            };
            BatchRequestStep requestStep = new BatchRequestStep(events.IndexOf(e).ToString(), httpRequestMessage, null);
            batchRequestContent.AddBatchRequestStep(requestStep);
            if (events.IndexOf(e) > 0 && ((events.IndexOf(e) + 1) % maxNoBatchItems == 0))
            {
                batches.Add(batchRequestContent);
                batchRequestContent = new BatchRequestContent(graphClient);
            }
        }
        if (batchRequestContent.BatchRequestSteps.Count < maxNoBatchItems)
        {
            batches.Add(batchRequestContent);
        }

        if (batches.Count == 0 && batchRequestContent != null)
        {
            batches.Add(batchRequestContent);
        }

        foreach (BatchRequestContent batch in batches)
        {
            BatchResponseContent response = null;
            response = await graphClient.Batch.Request().PostAsync(batch);
            Dictionary<string, HttpResponseMessage> responses = await response.GetResponsesAsync();
            foreach (string key in responses.Keys)
            {
                HttpResponseMessage httpResponse = await response.GetResponseByIdAsync(key);
                var responseContent = await httpResponse.Content.ReadAsStringAsync();
                JObject eventResponse = JObject.Parse(responseContent);
                //do something below
                Console.WriteLine(eventResponse["joinWebUrl"].ToString());
            }
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message + ex.StackTrace);
    }
}