A vantagem deste tipo de operação é que podemos, em um único POST HTTP, enviar um LOTE de registros para serem, por exemplo, criados no CRM.
Abaixo segue trecho de código que desenvolvi para inserir dois Contatos (CTT1, CTT2) no Dynamics 365. O exemplo está inserido em um projeto do tipo ASP.NET MVC - WEB API.
[HttpGet]
public void CreateDyn365BatchContacts()
{
ConnectToCRM(null);
Task.WaitAll(Task.Run(async () => await RunBatchAsync()));
}
public async Task RunBatchAsync() { await getWebAPIVersion(); webApiURI = config.ServiceUrl + "api/data/" + getVersionedWebAPIPath() + "$batch"; var batchId = Guid.NewGuid().ToString(); var changeSetId = "BBB456"; var contact1 = new JObject(); var contact2 = new JObject(); contact1.Add("firstname", "CTT"); contact1.Add("lastname", "1"); contact2.Add("firstname", "CTT"); contact2.Add("lastname", "2"); var batchContent = new MultipartContent("mixed", "batch_" + batchId); // CTT1 var changeSetContent = new MultipartContent("mixed", "changeset_" + changeSetId); var httpRM1 = new HttpRequestMessage(HttpMethod.Post, config.ServiceUrl + "api/data/" + getVersionedWebAPIPath() + "contacts"); httpRM1.Content = new StringContent(JsonConvert.SerializeObject(contact1), Encoding.UTF8, "application/json"); var httpMC1 = new HttpMessageContent(httpRM1); httpMC1.Headers.Remove("Content-Type"); httpMC1.Headers.Add("Content-Type", "application/http"); httpMC1.Headers.Add("Content-Transfer-Encoding", "binary"); httpMC1.Headers.Add("Content-ID", "1"); httpMC1.Headers.Add("OData-MaxVersion", "4.0"); httpMC1.Headers.Add("OData-Version", "4.0"); changeSetContent.Add(httpMC1); // CTT2 var httpRM2 = new HttpRequestMessage(HttpMethod.Post, config.ServiceUrl + "api/data/" + getVersionedWebAPIPath() + "contacts"); httpRM2.Content = new StringContent(JsonConvert.SerializeObject(contact2), Encoding.UTF8, "application/json"); var httpMC2 = new HttpMessageContent(httpRM2); httpMC2.Headers.Remove("Content-Type"); httpMC2.Headers.Add("Content-Type", "application/http"); httpMC2.Headers.Add("Content-Transfer-Encoding", "binary"); httpMC2.Headers.Add("Content-ID", "2"); httpMC2.Headers.Add("OData-MaxVersion", "4.0"); httpMC2.Headers.Add("OData-Version", "4.0"); changeSetContent.Add(httpMC2); batchContent.Add(changeSetContent); var response = SendCrmRequestAsync(HttpMethod.Post, webApiURI, batchContent).Result; var text = await response.Content.ReadAsStringAsync(); }
private async Task<HttpResponseMessage> SendCrmRequestAsync(HttpMethod method, string query, MultipartContent mpContent = null, Boolean formatted = false, int maxPageSize = 10)
{
var request = new HttpRequestMessage();
request.Method = method;
request.RequestUri = new Uri(query);
request.Headers.Add("Prefer", "odata.maxpagesize=" + maxPageSize.ToString());
if (formatted)
request.Headers.Add("Prefer", "odata.include-annotations=OData.Community.Display.V1.FormattedValue");
if (mpContent != null) {
request.Content = mpContent;
}
return await httpClient.SendAsync(request);
}
O código fonte completo está disponnível no zip CrmWebApi_JavaScript_CSharp.rar
I am implementing the same batch request in console app for dynamics CRM 2016, I am getting error 'This operation is not supported for a relative URI', Can you please help me to resolved the issue asap.
ResponderExcluirHi Jasdeep,
ResponderExcluirWere you able to run my sample code attached?
In general, it appears that the full URI of the Web API endpoint may be missing from your HTTPRequestMessage.
It should be something like this:
[Organization URI]/api/data/v8.2/contacts
Actually I am trying to implement batch request in dynamics CRM 2016 on-premise and same request worked for dynamics 365 online when we register application in azure portal to get authenticate token but how we can implement same for on-premise, Is there any limitation or minimum version required to execute the request ?
ResponderExcluirIn your scenario, i think you must rely on ADFS server, in order to get the token.
ExcluirTake a look on this:
https://stackoverflow.com/questions/45467317/authorize-webapp-to-adfs-in-order-to-access-dynamics-crm-web-api
I keep getting this error:
ResponderExcluirThe 'Content-Type' header is missing. The 'Content-Type' header must be specified for each MIME part of a batch message.","ExceptionMessage":"The 'Content-Type' header is missing. The 'Content-Type' header must be specified for each MIME part of a batch message."
Any help will be greatly appriciated.
Hi,
ExcluirCan you tell me more about your issue.
Are you working with Dynamics 365 ONLINE? ONPREMISSE?
Are you sure you have the environment configured properly?
I am getting the below errors while executing the same batch request in dynamics CRM 2016 (on-premise):
ResponderExcluirAn error occurred while sending the request.
The underlying connection was closed:
An unexpected error occurred on a receive.
Unable to read data from the transport connection:
An existing connection was forcibly closed by the remote host
Any help on the above issue?
Hi Jasdeep,
ExcluirIn your case, I would try to check:
- if the system has the latest Rollup
- more logs, by enabling the Trace Logs
- if can open a Call to MS
Let me know if you could solve the issues.
Hi,
ResponderExcluirI am using the Dynamics CRM 2016 On-Premise (8.1.0.359) (DB 8.1.0.359) version.
I have implemented the code in console app, so don't have more logs info.
Thanks
Hi,
ExcluirWhat about the 3 items I mentioned earlier, could you check?
I am trying to do same operation but for update, I am getting error "Bad request". Can you please let me know if there is any change required for the update? I have also downloaded your code but don't know how to use it because of Azure AD, so can not verify if your code is working with my env. I am using CRM 9.0 and api version 9.1. your help is highly appreciated.
ResponderExcluirI get below error
ExcluirMy Request
{Method: POST, RequestUri: 'https://**.dynamics.com/api/data/v9.1/$batch', Version: 2.0, Content: System.Net.Http.MultipartContent, Headers:
{
Prefer: odata.maxpagesize=10
Content-Type: multipart/mixed; boundary="batch_40b52419-3847-4588-87d3-6498df275de2"
Content-Length: 784
}}
Response
[09/08/2019 14:27:52] System.Private.CoreLib: Exception while executing function: PersonUpdatedEventListner. MDC.Subscriber: Error In Updating CRM. Status Code : BadRequest,
Details:
{"Message":"The 'Content-Type' header value 'application/http; msgtype=request' is invalid. When this is the start of the change set,
the value must be 'multipart/mixed'; otherwise it must be 'application/http'.",
"ExceptionMessage":"The 'Content-Type' header value 'application/http; msgtype=request' is invalid. When this is the start of the change set,
the value must be 'multipart/mixed'; otherwise it must be 'application/http'.",
"ExceptionType":"Microsoft.OData.ODataException","StackTrace":"
at Microsoft.OData.MultipartMixed.ODataMultipartMixedBatchReaderStream.ValidatePartHeaders(ODataBatchOperationHeaders headers, Boolean& isChangeSetPart)\r\n
at Microsoft.OData.MultipartMixed.ODataMultipartMixedBatchReaderStream.ProcessPartHeader(String& contentId)\r\n
at Microsoft.OData.MultipartMixed.ODataMultipartMixedBatchReader.SkipToNextPartAndReadHeaders()\r\n
at Microsoft.OData.ODataBatchReader.ReadImplementation()\r\n
at Microsoft.OData.ODataBatchReader.InterceptException[T](Func`1 action)\r\n
at System.Web.OData.Batch.ODataBatchReaderExtensions.d__0.MoveNext()\r\n
--- End of stack trace from previous location where exception was thrown ---\r\n
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\r\n
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n
at Microsoft.Crm.Extensibility.OData.CrmODataBatchHandler.d__16.MoveNext()\r\n
--- End of stack trace from previous location where exception was thrown ---\r\n
Hi Hiren,
ExcluirTo help you if, could you send me your update batch code?
As additional information, take a look on this samples:
[see the section - Bulk Update]
https://community.dynamics.com/crm/b/scaleablesolutionsblog/posts/web-api-bulk-operations
Also, according the error message, [msgtype=request] is invalid.
ExcluirThe [Content-Type] for the PATCH must have only [application/http]
I have sent you code, But I am not able to understand from where it takes msgtype=request
ExcluirThe code I have sent you is not giving this error.
Excluir[12/08/2019 08:26:03] Update of Person failed, please verify json object. Exception message: Error In Updating CRM. Status Code : BadRequest,
Details:
{"Message":"The HTTP version 'HTTP/2.0' used in a batch operation request or response is not valid. The value must be 'HTTP/1.1'.",
"ExceptionMessage":"The HTTP version 'HTTP/2.0' used in a batch operation request or response is not valid. The value must be 'HTTP/1.1'.",
"ExceptionType":"Microsoft.OData.ODataException",
"StackTrace":" at Microsoft.OData.MultipartMixed.ODataMultipartMixedBatchReader.ParseRequestLine(String requestLine, String& httpMethod, Uri& requestUri)\r\n
at Microsoft.OData.MultipartMixed.ODataMultipartMixedBatchReader.CreateOperationRequestMessageImplementation()\r\n
at Microsoft.OData.ODataBatchReader.InterceptException[T](Func`1 action)\r\n
at Microsoft.OData.ODataBatchReader.CreateOperationRequestMessage()\r\n
at System.Web.OData.Batch.ODataBatchReaderExtensions.d__9.MoveNext()\r\n---
End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\r\n
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n
at System.Web.OData.Batch.ODataBatchReaderExtensions.d__0.MoveNext()\r\n
--- End of stack trace from previous location where exception was thrown ---\r\n
Kindly Ignore my message, the problem is resolved now.
ExcluirNice to hear that.
ExcluirHow did you figured out?
I had missed to add code to remove and add content type from request. And then the batch is not supported in http 2.0 so while creating request I had to change version of request to 1.1 and that worked well. its httpversion.
ExcluirEste comentário foi removido pelo autor.
ResponderExcluirI have one more question now. I want to update address of customer without fetch because that will be part of batch request. So I want to update address when addressnumber = 1 and Contact.alternatekey = "abc"
ResponderExcluirhow to make this type of update request?
Or else is there any way that, In batch request, I take output of first request and use it in second request?
ExcluirAs for as I know I think this is not possible, because you cannot use the batch code as a [sql stored procedure] that has this ability.
Excluir