simple-odata-client / Simple.OData.Client

MIT License
329 stars 191 forks source link

Dynamics 365 Business Central: Could not validate the client concurrency token required by the service. #667

Open Luukjn opened 5 years ago

Luukjn commented 5 years ago

Version: 5.9.1 Framework: .NET Standard 2.0, .NET Core, Xamarin OData Version: V4.0

Problem Description: I'm trying to update a record in Dynamics 365 Business Central using OData. I'm using the following code:

dynamic x = ODataDynamic.Expression;
await _client.For(x.Company)
        .Key(company);
        .NavigateTo(x.Article)
        .Key(article.ItemNo)
        .Set(
                 x.Description = article.Description,
                 x.Unit_Price = article.UnitPrice
         )
        .UpdateEntryAsync();

However I get the following error from Business Central:

{ 
   "error":{ 
      "code":"BadRequest_InvalidToken",
      "message":"Could not validate the client concurrency token required by the service. Please provide a valid token in the client request.  CorrelationId:  2c074c12-1644-4f2c-befb-efd681f5a595."
   }
}

I found out that this error means I'm missing the ETAG in the update request from this question in the Dynamics 365 community: https://community.dynamics.com/business/f/dynamics-365-business-central-forum/291240/put-patch-operation-failed-for-odata-web-api-in-d365-bc Is there a way to get the ETAG and set it using the simple OData client? Or is there something else I'm missing?

Luukjn commented 4 years ago

I managed to solve this issue by manually getting the ETag like follows:

dynamic x = ODataDynamic.Expression;
dynamic articleToUpdate = await GetCompanyQuery()
                   .NavigateTo(x.Article)
                   .Key(article.ItemNo)
                   .FindEntryAsync();

_currentETag = articleToUpdate.__annotations.ETag;

And then adding the following to the BeforeResponse segment in the client settings:

if (((message.Method.Method == "PATCH") ||
     (message.Method == HttpMethod.Delete) ||
     (message.Method == HttpMethod.Put)) &&
    (_currentETag != null))
{
    message.Headers.Add("If-Match", _currentETag);
}

This way I could make sure that the ETag is always correctly set for all Patch, Put and Delete requests.

Make sure that IncludeAnnotationsInResults is set to true in the client settings.

wintg-zns commented 4 years ago

This is a great workaround. However, there is a chance that this might run into concurrency issues if this were to be used multi-threaded application (you might get the wrong etag in BeforeResponse).

It would be better if there were a mechanism in the UpdateEntryAsync() methods to permit the specification of an ETag if it is needed for the If-Match header.

adam1010 commented 4 years ago

Thanks @Luukjn, checking the item's current etag and then putting it into the If-Match header worked beautifully!

FirewizzNL commented 2 years ago

I think I have the same issue?

https://github.com/simple-odata-client/Simple.OData.Client/issues/865

Unable to do the InsertEntryAsync. Other calls work, but unable to post a new item.