public async Task <CompositeResult> PostAsync(ICompositeRequest request) { if (request == null || request.CompositeRequests.Count <= 0) { throw new ArgumentNullException(nameof(request)); } try { var resourceName = $"{request.Prefix}composite"; if (request.AllOrNone) { var requests = request.CompositeRequests; if (requests.Count > Dnf.CompositeLimit) { throw new ArgumentOutOfRangeException(nameof(request)); } if (requests.Count(c => IsQuery(c.ResponseType)) > Dnf.CompositeQueryLimit) { throw new ArgumentOutOfRangeException(nameof(request)); } var inputObject = new JObject { ["allOrNone"] = true, ["compositeRequest"] = JToken.FromObject(requests.Select(req => Dnf.Assign(JObject.FromObject(req), new JObject { ["url"] = DecodeReference($"/services/data/{ApiVersion}/{request.Prefix}{req.Url?.TrimStart('/')}") }))) }; var result = await JsonHttp.HttpPostAsync <CompositeResultBody>(inputObject, resourceName) .ConfigureAwait(false); var results = new CompositeResult(request.CompositeRequests, result?.CompositeResponse ?? new List <CompositeSubRequestResult>()); return(results); } else { var throttler = new SemaphoreSlim(Dnf.DefaultConcurrentLimit, Dnf.DefaultConcurrentLimit); var results = new CompositeResult(); var chunks = new List <IList <CompositeSubRequest> >(); IList <CompositeSubRequest>?chunk = null; foreach (var req in request.CompositeRequests) { var added = false; if (IsQuery(req.ResponseType)) { if (chunk != null && chunk.Count(c => IsQuery(c.ResponseType)) < Dnf.CompositeQueryLimit) { chunk.Add(req); added = true; } } else if (chunk?.Count < Dnf.CompositeLimit) { chunk.Add(req); added = true; } if (added) { continue; } chunk = new List <CompositeSubRequest> { req }; chunks.Add(chunk); } var tasks = new List <Task>(); foreach (var requests in chunks) { await throttler.WaitAsync() .ConfigureAwait(false); tasks.Add(Task.Run(async() => { try { var inputObject = new JObject { ["compositeRequest"] = JToken.FromObject(requests.Select(req => Dnf.Assign(JObject.FromObject(req), new JObject { ["url"] = DecodeReference($"/services/data/{ApiVersion}/{request.Prefix}{req.Url?.TrimStart('/')}") }))) }; var result = await JsonHttp.HttpPostAsync <CompositeResultBody>(inputObject, resourceName) .ConfigureAwait(false); results.Add(requests, result?.CompositeResponse ?? new List <CompositeSubRequestResult>()); } catch (Exception ex) { var body = new JArray { ex.Message }; var responses = requests.Select(req => new CompositeSubRequestResult { Body = body, ReferenceId = req.ReferenceId, HttpStatusCode = 500 }).ToList(); results.Add(requests, responses); } finally { throttler.Release(); } })); } await Task.WhenAll(tasks) .ConfigureAwait(false); return(results); } } catch (Exception ex) { var body = new JArray { ex.Message }; var responses = request.CompositeRequests.Select(req => new CompositeSubRequestResult { Body = body, ReferenceId = req.ReferenceId, HttpStatusCode = 500 }).ToList(); var results = new CompositeResult(request.CompositeRequests, responses); return(results); } bool IsQuery(string responseType) { return(responseType == "query" || responseType == "collections"); } }
public async Task <BatchResult> BatchAsync(IBatchRequest request) { if (request == null || request.BatchRequests.Count <= 0) { throw new ArgumentNullException(nameof(request)); } try { var resourceName = $"{request.Prefix}composite/batch"; if (request.HaltOnError) { if (request.BatchRequests.Count > Dnf.BatchLimit) { throw new ArgumentOutOfRangeException(nameof(request)); } var inputObject = new JObject { ["batchRequests"] = JToken.FromObject(request.BatchRequests.Select(req => Dnf.Assign(JObject.FromObject(req), new JObject { ["url"] = DecodeReference($"/services/data/{ApiVersion}/{request.Prefix}{req.Url?.TrimStart('/')}") }))), ["haltOnError"] = true }; var result = await JsonHttp.HttpPostAsync <BatchResultBody>(inputObject, resourceName) .ConfigureAwait(false); var results = new BatchResult(request.BatchRequests, result?.Results ?? new List <BatchSubRequestResult>()); return(results); } else { var throttler = new SemaphoreSlim(Dnf.DefaultConcurrentLimit, Dnf.DefaultConcurrentLimit); var results = new BatchResult(); var chunks = new List <IList <BatchSubRequest> >(); IList <BatchSubRequest>?chunk = null; foreach (var req in request.BatchRequests) { var added = false; if (chunk?.Count < Dnf.BatchLimit) { chunk.Add(req); added = true; } if (added) { continue; } chunk = new List <BatchSubRequest> { req }; chunks.Add(chunk); } var tasks = new List <Task>(); foreach (var requests in chunks) { await throttler.WaitAsync() .ConfigureAwait(false); tasks.Add(Task.Run(async() => { try { var inputObject = new JObject { ["batchRequests"] = JToken.FromObject(requests.Select(req => Dnf.Assign(JObject.FromObject(req), new JObject { ["url"] = DecodeReference($"/services/data/{ApiVersion}/{request.Prefix}{req.Url?.TrimStart('/')}") }))) }; var result = await JsonHttp.HttpPostAsync <BatchResultBody>(inputObject, resourceName) .ConfigureAwait(false); results.Add(requests, result?.Results ?? new List <BatchSubRequestResult>()); } catch (Exception ex) { var body = new JArray { ex.Message }; var responses = requests.Select(req => new BatchSubRequestResult { Result = body, StatusCode = 500 }).ToList(); results.Add(requests, responses); } finally { throttler.Release(); } })); } await Task.WhenAll(tasks) .ConfigureAwait(false); return(results); } } catch (Exception ex) { var body = new JArray { ex.Message }; var responses = request.BatchRequests.Select(req => new BatchSubRequestResult { Result = body, StatusCode = 500 }).ToList(); var results = new BatchResult(request.BatchRequests, responses); return(results); } }