Unfortunately it does not provide a sample code (in C#) for how to compose the batch request and handle respective response.
So here is the one that I have built for my project:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public async Task<List<HttpResponseMessage>> SendBatchRequestAsync(List<HttpMessageContent> httpContents) | |
{ | |
if (httpContents == null) | |
{ | |
throw new ArgumentNullException(nameof(httpContents)); | |
} | |
string batchName = $"batch_{Guid.NewGuid()}"; | |
MultipartContent batchContent = new MultipartContent("mixed", batchName); | |
string changesetName = $"changeset_{Guid.NewGuid()}"; | |
MultipartContent changesetContent = new MultipartContent("mixed", changesetName); | |
httpContents.ForEach((c) => changesetContent.Add(c)); | |
batchContent.Add(changesetContent); | |
return await SendBatchRequestAsync(batchContent); | |
} | |
public async Task<List<HttpResponseMessage>> SendBatchRequestAsync(MultipartContent batchContent) | |
{ | |
HttpRequestMessage batchRequest = new HttpRequestMessage | |
{ | |
Method = HttpMethod.Post, | |
RequestUri = new Uri(_httpClient.BaseAddress.AbsoluteUri.Trim() + "$batch") | |
}; | |
batchRequest.Content = batchContent; | |
batchRequest.Headers.Add("Prefer", Settings.ClientOptions.DefaultRequestHeaders["Prefer"]); | |
batchRequest.Headers.Add("OData-MaxVersion", "4.0"); | |
batchRequest.Headers.Add("OData-Version", "4.0"); | |
batchRequest.Headers.Add("Accept", "application/json"); | |
HttpResponseMessage response = await _httpClient.SendAsync(batchRequest); | |
MultipartMemoryStreamProvider body = await response.Content.ReadAsMultipartAsync(); | |
List<HttpResponseMessage> contents = await ReadHttpContents(body); | |
return contents; | |
} | |
private async Task<List<HttpResponseMessage>> ReadHttpContents(MultipartMemoryStreamProvider body) | |
{ | |
List<HttpResponseMessage> results = new List<HttpResponseMessage>(); | |
if (body?.Contents != null) | |
{ | |
foreach (HttpContent c in body.Contents) | |
{ | |
if (c.IsMimeMultipartContent()) | |
{ | |
results.AddRange(await ReadHttpContents((await c.ReadAsMultipartAsync()))); | |
} | |
else if (c.IsHttpResponseMessageContent()) | |
{ | |
HttpResponseMessage responseMessage = await c.ReadAsHttpResponseMessageAsync(); | |
if (responseMessage != null) | |
{ | |
results.Add(responseMessage); | |
} | |
} | |
else | |
{ | |
HttpResponseMessage responseMessage = DeserializeToResponse(await c.ReadAsStreamAsync()); | |
if (responseMessage != null) | |
{ | |
results.Add(responseMessage); | |
} | |
} | |
} | |
} | |
return results; | |
} | |
private HttpResponseMessage DeserializeToResponse(Stream stream) | |
{ | |
HttpResponseMessage response = new HttpResponseMessage(); | |
MemoryStream memoryStream = new MemoryStream(); | |
stream.CopyTo(memoryStream); | |
response.Content = new ByteArrayContent(memoryStream.ToArray()); | |
response.Content.Headers.Add("Content-Type", "application/http;msgtype=response"); | |
return response.Content.ReadAsHttpResponseMessageAsync().Result; | |
} |
- Execute a batch request that is composed of a list of individual requests, each of them can be in turn a change set, or
- Execute a list of requests that are grouped into a single change set.
Example 1:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
string batchName = $"batch_{Guid.NewGuid()}"; | |
MultipartContent batchContent = new MultipartContent("mixed", batchName); | |
string changesetName = $"changeset_{Guid.NewGuid()}"; | |
MultipartContent changesetContent = new MultipartContent("mixed", changesetName); | |
string taskJson1 = "{\"subject\":\"Task 1 in batch\",\"regardingobjectid_account_task@odata.bind\":\"[Organization URI]/api/data/v9.0/accounts(00000000-0000-0000-000000000001)\"}"; | |
changesetContent.Add(CreateHttpMessageContent(HttpMethod.Post, "tasks", 1, taskJson1)); | |
string taskJson2 = "{\"subject\":\"Task 2 in batch\",\"regardingobjectid_account_task@odata.bind\":\"[Organization URI]/api/data/v9.0/accounts(00000000-0000-0000-000000000001)\"}"; | |
changesetContent.Add(CreateHttpMessageContent(HttpMethod.Post, "tasks", 2, taskJson2)); | |
batchContent.Add(changesetContent); | |
batchContent.Add(CreateHttpMessageContent(HttpMethod.Get, "Account_Tasks?$select=subject")); | |
List<HttpResponseMessage> responses = await SendBatchRequestAsync(batchContent); |
Example 2:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
List<HttpMessageContent> httpContents = new List<HttpMessageContent>(); | |
for (int i = 1; i <= 5; i++) | |
{ | |
string taskJson = "{\"subject\":\"Task " + i.ToString() + " in batch\",\"regardingobjectid_account_task@odata.bind\":\"[Organization URI]/api/data/v9.0/accounts(00000000-0000-0000-000000000001)\"}"; | |
httpContents.Add(CreateHttpMessageContent(HttpMethod.Post, "tasks", i, taskJson)); | |
} | |
List<HttpResponseMessage> responses = await SendBatchRequestAsync(httpContents); |
Utility method to create HttpMessageContent:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public HttpMessageContent CreateHttpMessageContent(HttpMethod httpMethod, string requestUri, int contentId = 0, string content = null) | |
{ | |
string baseUrl = _httpClient.BaseAddress.AbsoluteUri.Trim(); | |
if (!requestUri.StartsWith(baseUrl)) | |
{ | |
requestUri = baseUrl + requestUri; | |
} | |
HttpRequestMessage requestMessage = new HttpRequestMessage(httpMethod, requestUri); | |
HttpMessageContent messageContent = new HttpMessageContent(requestMessage); | |
messageContent.Headers.Remove("Content-Type"); | |
messageContent.Headers.Add("Content-Type", "application/http"); | |
messageContent.Headers.Add("Content-Transfer-Encoding", "binary"); | |
// only GET request requires Accept header | |
if (httpMethod == HttpMethod.Get) | |
{ | |
requestMessage.Headers.Add("Accept", "application/json"); | |
} | |
else | |
{ | |
// request other than GET may have content, which is normally JSON | |
if (!string.IsNullOrEmpty(content)) | |
{ | |
StringContent stringContent = new StringContent(content); | |
stringContent.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json;type=entry"); | |
requestMessage.Content = stringContent; | |
} | |
messageContent.Headers.Add("Content-ID", contentId.ToString()); | |
} | |
return messageContent; | |
} |
Really appreciate your efforts. Well done.
ReplyDeleteThanks. Glad you find it helpful.
DeleteI have also created a working code at https://github.com/thucnguyen77/dynamics-365-web-api-batch-request-example.
ReplyDeleteThank you very much. you save my time :)
ReplyDeleteDoes this allows multiple files to be uploaded using the batch requests?
ReplyDelete