Uso de los SDK de Microsoft Graph para las solicitudes por lotes

El procesamiento por lotes es una manera de combinar varias solicitudes en una sola solicitud HTTP. Las solicitudes se combinan en una única carga JSON, que se envía a través de POST al \$batch punto de conexión. Los SDK de Microsoft Graph tienen un conjunto de clases para simplificar la creación de cargas por lotes y el análisis de cargas de respuesta por lotes.

Importante

Para conocer las limitaciones actuales con el procesamiento por lotes JSON en Microsoft Graph, consulte Problemas conocidos.

Crear una solicitud por lotes

Los SDK de Microsoft Graph proporcionan tres clases para trabajar con solicitudes y respuestas por lotes.

  • BatchRequestStep : representa una única solicitud (como GET /me) dentro de un lote. Permite asignar un identificador único a la solicitud y especificar dependencias entre las solicitudes.
  • BatchRequestContent : simplifica la creación de la carga de la solicitud por lotes. Contiene varios objetos BatchRequestStep .
  • BatchResponseContent : simplifica el análisis de la respuesta de una solicitud por lotes. Proporciona la capacidad de obtener todas las respuestas, obtener una respuesta específica por identificador y obtener la @odata.nextLink propiedad si está presente.

Ejemplo de procesamiento por lotes simple

En este ejemplo se muestra cómo enviar varias solicitudes en un lote que no dependen entre sí. El servicio puede ejecutar las solicitudes en cualquier orden. En este ejemplo se obtiene el usuario y se obtiene la vista de calendario del usuario para el día actual.

// 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}");
}

Lotes con solicitudes dependientes

En este ejemplo se muestra cómo enviar varias solicitudes en un lote que dependen entre sí. El servicio ejecutará las solicitudes en el orden especificado por las dependencias. En este ejemplo se agrega un evento con una hora de inicio durante el día actual al calendario del usuario y se obtiene la vista de calendario del usuario para el día actual. Para asegurarse de que la revisión de calendario devuelta incluye el nuevo evento creado, la solicitud para la vista de calendario se configura como dependiente de la solicitud para agregar el nuevo evento. Esto garantiza que la solicitud de evento add se ejecutará primero.

Nota:

Si se produce un error en la solicitud de agregar eventos, se producirá un 424 Failed Dependency error en la solicitud obtener vista de calendario.

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}");
}

Implementación del procesamiento por lotes mediante BatchRequestContent, BatchRequestStep y HttpRequestMessage

En el ejemplo siguiente se muestra cómo usar BatchRequestContentyBatchRequestStepHttpRequestMessage enviar varias solicitudes en un lote y cómo controlar el límite de 20 con solicitudes de Microsoft Graph API. En este ejemplo se crean vínculos de reunión mediante el onlineMeetings/createOrGet punto de conexión para el identificador de usuario especificado. También puede usar este ejemplo con otros puntos de conexión de 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);
    }
}