1

Here is my code for deleting a set of calendar entries:

public async Task<bool> DeleteCalendarEvents(SettingsBase oSettings)
{
    try
    {
        var oEvents = await _graphClient
                                .Me
                                .Calendars[oSettings.CalendarID]
                                .Events
                                .Request()
                                .Select("Start,Subject,Id")
                                .Top(50)
                                .Filter(oSettings.GetFilterString())
                                .OrderBy("start/DateTime")
                                .GetAsync();

        List<Event> listEvents = new List<Event>();

        listEvents.AddRange(oEvents);
        while (oEvents.NextPageRequest != null)
        {
            oEvents = await oEvents.NextPageRequest.GetAsync();
            listEvents.AddRange(oEvents);
        }

        foreach (Event oEvent in listEvents)
        { 
            await _graphClient.Me.Events[oEvent.Id].Request().DeleteAsync();
        }
    }
    catch (Exception ex)
    {
        SimpleLog.Log(ex);
        Console.WriteLine("DeleteCalendarEvents: See error log.");
        return false;
    }

    return true;
}

I then have a method that adds new events into the calendar:

public async Task<bool> AddEventsToCalendar(MWBData.MWBCalendarData oData)
{
    if (oData.SettingsMWB.CalendarEntryType != "CLM_MidweekMeeting")
    {
        SimpleLog.Log("AddEventsToCalendar: CalendarEntryType is not set to CLM_MidweekMeeting.", SimpleLog.Severity.Error);
        Console.WriteLine("AddEventsToCalendar: See error log.");
        return false;
    }

    try
    {
        // Now create the new events
        foreach (EventWeek oWeek in oData.Weeks)
        {
            bool bSuccess = await AddEventToCalendar(oWeek, oData.SettingsMWB);
            if(bSuccess)
            {
                // Now create any Weekend Meeting events
                if(oWeek.WeekendMeeting.Included)
                {
                    bSuccess = await AddEventToCalendar(oWeek.WeekendMeeting, oData.SettingsMWB);
                    if(!bSuccess)
                    {
                        Console.WriteLine("AddEventsToCalendar: See error log.");
                        return false;
                    }
                }
            }
            else
            {
                Console.WriteLine("AddEventToCalendar: See error log.");
                return false;
            }
        }
    }
    catch (Exception ex)
    {
        SimpleLog.Log(ex);
        Console.WriteLine("AddEventsToCalendar: See error log.");
        return false;
    }

    return true;
}

As you can see, for each event it calls AddEventToCalendar. That method, in part, creates the event like this:

// Add the event
Event createdEvent = await _graphClient.Me.Calendars[oSettings.CalendarID].Events.Request().AddAsync(new Event
{
    Subject = oEvent.GetSubject(),
    Body = body,
    Start = startTime,
    End = endTime,
    IsAllDay = oEvent.IsAllDayEvent(),
    IsReminderOn = bSetReminder,
    ReminderMinutesBeforeStart = bSetReminder ? iReminderMinutes : (int?)null,
    Location = location,
    SingleValueExtendedProperties = extendedProperties,
    Sensitivity = oSettings.SetCalendarPrivate ? Sensitivity.Private : Sensitivity.Normal
});

Now, I know that Microsoft Graph supports batch mode using JSON. But I am at a loss as to how to implement that with what I have written. It makes sense to try and convert my code into a list of batch operations to reduce the calls.

How do I do this?

Update

I have located this article but I am not sure if it is relevant and what I should do. So I would still appreciate any specific guidance with how to do this. I am sure that other potential users would benefit from this greatly - or be directed to an existing resource that I have missed. Thank you.

Community
  • 1
  • 1
Andrew Truckle
  • 17,769
  • 16
  • 66
  • 164

2 Answers2

1

Even though batch request is not yet supported you could consider the following proof-of-concept that demonstrates how to implement a basic support for retrieving entities on top of msgraph-sdk-dotnet:

//1.Construct a Batch request 
var batchRequest = new BatchRequest(); 
//2. Add sub queries. Two parameters needs to be specified: a request and a result object                 
batchRequest.AddQuery(graphClient.Users[loginName].Request(), typeof(Microsoft.Graph.User));
batchRequest.AddQuery(graphClient.Sites["root"].Request(),typeof(Microsoft.Graph.Site));
//3. Execute Batch request
var result = await graphClient.GetBatchAsync(batchRequest);  

where BatchRequest class introduces a support for Batch request

public class BatchRequest
{

    private Dictionary<string, IBaseRequest> _queriesTable = new Dictionary<string, IBaseRequest>();
    private Dictionary<string,Type> _resultsTable = new Dictionary<string, Type>();

    public KeyValuePair<IBaseRequest, Type> this[string queryId]
    {
        get
        {
            return new KeyValuePair<IBaseRequest,Type>(_queriesTable[queryId], _resultsTable[queryId]);
        }
    }


    public void AddQuery(IBaseRequest query, Type entityType)
    {
        var queryId = Guid.NewGuid().ToString();
        _resultsTable[queryId] = entityType;
        _queriesTable[queryId] = query;
    } 


    /// <summary>
    /// Construct JSON batch request https://developer.microsoft.com/en-us/graph/docs/concepts/json_batching
    /// </summary>
    /// <param name="client"></param>
    /// <returns></returns>
    public HttpRequestMessage ToMessage(GraphServiceClient client)
    {
        var batchMessage = new HttpRequestMessage();
        batchMessage.RequestUri = new Uri("https://graph.microsoft.com/v1.0/$batch");
        batchMessage.Method = HttpMethod.Post;
        batchMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
        dynamic payload = new ExpandoObject();
        payload.requests = _queriesTable.Select(kv =>
        {
            var message = kv.Value.GetHttpRequestMessage();
            dynamic request = new ExpandoObject();
            request.id = kv.Key; 
            request.method = message.Method.ToString();
            request.url = message.RequestUri.AbsoluteUri.Replace(client.BaseUrl,string.Empty);
            if(message.Content != null)
                request.body = message.Content;

            request.headers = message.Headers.ToDictionary(x => x.Key, x => x.Value.FirstOrDefault());
            return request;
        });
        var jsonPayload = client.HttpProvider.Serializer.SerializeObject(payload);
        batchMessage.Content = new StringContent(jsonPayload,Encoding.UTF8,"application/json");
        return batchMessage;
    }

}

and GetBatchAsync is extension method to execute a batch request:

public static class GraphServiceClientExtensions
{
    public static async Task<List<object>> GetBatchAsync(this GraphServiceClient client, BatchRequest request)
    {
        var batchMessage = request.ToMessage(client);
        await client.AuthenticationProvider.AuthenticateRequestAsync(batchMessage);
        var response = await client.HttpProvider.SendAsync(batchMessage);

        var content = await response.Content.ReadAsStringAsync();
        var json = JObject.Parse(content);
        var entities = json["responses"].Select(item =>
        {
            var queryId = (string)item["id"];
            var entityPayload = JsonConvert.SerializeObject(item["body"]);
            var subRequest = request[queryId];
            var entity = JsonConvert.DeserializeObject(entityPayload, subRequest.Value);
            return entity;
        }); 
        return entities.ToList();
    }
}

Gist: Microsoft.Graph.BatchSupport.cs

Like i said it is just a proof-of-concept and only retrieving of entities are supported in this implementation, so if you want to implement another operations like delete you could utilize it as a starting point

Results

enter image description here

Vadim Gremyachev
  • 57,952
  • 20
  • 129
  • 193
  • 1
    Thanks. In my case I am creating calendar entries as opposed to retrieving. But I really appreciate your thorough answer. – Andrew Truckle Nov 06 '18 at 19:40
0

This is not supported in the graph library now.

https://github.com/microsoftgraph/msgraph-sdk-dotnet/issues/136

A workaround for you, use HttpClient to request batch endpoint,

Mock jsonObject(You need to use StringBuilder or such to create the string, use for/foreach to iterate over the event list and then append item info to the json object, just remember to increment the id value):

Note: Nested json seems doesn't work on Graph, so this workground still need test.

    {
      "requests": [
        {
          "id": "1",
         "url": "/me",
          "method": "POST",
          "body": {
  "subject": "Let's go for lunch",
  "body": {
    "contentType": "HTML",
    "content": "Does late morning work for you?"
  },
  "start": {
      "dateTime": "2017-04-15T12:00:00",
      "timeZone": "Pacific Standard Time"
  },
  "end": {
      "dateTime": "2017-04-15T14:00:00",
      "timeZone": "Pacific Standard Time"
  },
  "location":{
      "displayName":"Harry's Bar"
  },
  "attendees": [
    {
      "emailAddress": {
        "address":"samanthab@contoso.onmicrosoft.com",
        "name": "Samantha Booth"
      },
      "type": "required"
    }
  ]
},
          "headers": {
            "Content-Type": "application/json"
          }
        },
        {
          "id": "2",
         "url": "/me",
          "method": "POST",
          "body": {
  "subject": "Let's go for lunch",
  "body": {
    "contentType": "HTML",
    "content": "Does late morning work for you?"
  },
  "start": {
      "dateTime": "2017-04-15T12:00:00",
      "timeZone": "Pacific Standard Time"
  },
  "end": {
      "dateTime": "2017-04-15T14:00:00",
      "timeZone": "Pacific Standard Time"
  },
  "location":{
      "displayName":"Harry's Bar"
  },
  "attendees": [
    {
      "emailAddress": {
        "address":"samanthab@contoso.onmicrosoft.com",
        "name": "Samantha Booth"
      },
      "type": "required"
    }
  ]
},
          "headers": {
            "Content-Type": "application/json"
          }
        },
        {
          "id": "3", 
          "url": "/me",
          "method": "POST",
          "body": {
  "subject": "Let's go for lunch",
  "body": {
    "contentType": "HTML",
    "content": "Does late morning work for you?"
  },
  "start": {
      "dateTime": "2017-04-15T12:00:00",
      "timeZone": "Pacific Standard Time"
  },
  "end": {
      "dateTime": "2017-04-15T14:00:00",
      "timeZone": "Pacific Standard Time"
  },
  "location":{
      "displayName":"Harry's Bar"
  },
  "attendees": [
    {
      "emailAddress": {
        "address":"samanthab@contoso.onmicrosoft.com",
        "name": "Samantha Booth"
      },
      "type": "required"
    }
  ]
},
          "headers": {
            "Content-Type": "application/json"
          }
        }
      ]
    }

Execute code:

var content = new StringContent(jsonObject.ToString(), Encoding.UTF8, "application/json"); 
var result = await client.PostAsync(url, content);
Seiya Su
  • 1,836
  • 1
  • 7
  • 10