Skip to content

Instantly share code, notes, and snippets.

@vgrem
Last active May 23, 2021 20:13
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save vgrem/37dbe4762851decda79e32f5943b4fe3 to your computer and use it in GitHub Desktop.
Save vgrem/37dbe4762851decda79e32f5943b4fe3 to your computer and use it in GitHub Desktop.
BatchRequest class for msgraph-sdk-dotnet
using Microsoft.Graph;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
namespace Microsoft.Graph.GraphClient.JsonBatchSupport
{
public class Query
{
public string Id { get; set; }
public IBaseRequest Request { get; set; }
public HttpMethod Method { get; set; }
public HttpContent Content { get; set; }
public bool IsJson { get; set; }
public List<string> DependsOn { get; set; }
}
public static class GraphServiceClientExtensionsAlt
{
public static async Task<List<object>> SendBatchAsync(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(data =>
{
var status = JsonConvert.DeserializeObject<Int32>((string)data["status"]);
if (status != 200)
return null;
var queryId = (string)data["id"];
var returnType = request[queryId];
if (returnType != null)
{
var entityPayload = JsonConvert.SerializeObject(data["body"]);
var entity = JsonConvert.DeserializeObject(entityPayload, returnType);
return entity;
}
return null;
});
return entities.ToList();
}
}
public class BatchRequest
{
private List<Query> _queries = new List<Query>();
private Dictionary<string, Type> _resultsTable = new Dictionary<string, Type>();
private Query _prevQuery = null;
public Type this[string queryId]
{
get
{
if(_resultsTable.ContainsKey(queryId))
return _resultsTable[queryId];
return null;
}
}
public void AddQuery(IBaseRequest request, HttpMethod method, HttpContent content = null, bool isJson = false,
Type returnEntityType = null)
{
var queryId = Guid.NewGuid().ToString();
if (returnEntityType != null)
_resultsTable[queryId] = returnEntityType;
var query = new Query()
{
Id = queryId,
Request = request,
Method = method,
Content = content,
IsJson = isJson,
};
if (_prevQuery != null)
query.DependsOn = new List<string>() { _prevQuery.Id};
_queries.Add(query);
_prevQuery = 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 = _queries.Select(qry =>
{
var message = qry.Request.GetHttpRequestMessage();
if (qry.Content != null)
{
var request = new
{
id = qry.Id,
method = qry.Method.ToString(),
url = message.RequestUri.AbsoluteUri.Replace(client.BaseUrl, string.Empty),
headers = qry.Content.Headers.ToDictionary(x => x.Key, x => x.Value.FirstOrDefault()),
body = qry.IsJson
? qry.Content.ReadAsAsync<object>().Result
: qry.Content.ReadAsByteArrayAsync().Result,
dependsOn = qry.DependsOn
};
if (!qry.IsJson && !request.headers.ContainsKey("Content-Type"))
request.headers["Content-Type"] = "application/octet-stream";
return request;
}
return new
{
id = qry.Id,
method = qry.Method.ToString(),
url = message.RequestUri.AbsoluteUri.Replace(client.BaseUrl, string.Empty),
headers = (Dictionary<string,string>)null,
body = (object) null,
dependsOn = qry.DependsOn
};
});
var jsonPayload = client.HttpProvider.Serializer.SerializeObject(payload);
batchMessage.Content = new StringContent(jsonPayload, Encoding.UTF8, "application/json");
return batchMessage;
}
}
}
@anomepani
Copy link

@vgrem
ReadAsAsync not available on HttpContent. Do I need to add aything?
qry.Content.ReadAsAsync().Result

Any demo or sample app available which describe the usage of this BatchRequest?

@anomepani
Copy link

@vgrem Is this code support batching with getting, Post, Put, Patch, Delete all this Http verb and For all Graph Endpoints?
Do you have the documentation or sample demo which describes how to use it?
If you could share details it will be very helpful.
Thanks in advance.

@vgrem
Copy link
Author

vgrem commented Oct 19, 2019

Greetings @anomepani.

SendBatchAsync is a custom extension method which was introduced before JSON Batch support was available in msgraph-sdk-dotnet
Nowadays (starting from version v1.15.0 or above) it is supported natively in msgraph-sdk-dotnet , refer this post for a more details.

Answering to your question, all Http methods are supported, but if you are experiencing some issues, consider to post a question on StackOverflow and i might be able to assist you.

Vadim

@anomepani
Copy link

Vadim, Thanks for the quick response.
For batching using, I didn't find any documentation for how to use different actions, limited resources or blog is available. I found this gist which looks somewhat helpful to me.
However, I will take a look for sure with the provided link and if require wil post question on stackoverflow

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment