Perform conditional operations using the Web API
Updated: November 29, 2016
Applies To: Dynamics 365 (online), Dynamics 365 (on-premises), Dynamics CRM 2016, Dynamics CRM Online
Microsoft Dynamics 365 provides support for a set of conditional operations that rely upon the standard HTTP resource versioning mechanism known as ETags.
The HTTP protocol defines an entity tag, or ETag for short, for identifying specific versions of a resource. ETags are opaque identifiers whose exact values are implementation dependent. ETag values occur in two varieties: strong and weak validation. Strong validation indicates that a unique resource, identified by a specific URI, will be identical on the binary level if its corresponding ETag value is unchanged. Weak validation only guarantees that the resource representation is semantically equivalent for the same ETag value.
Dynamics 365 generates a weakly validating @odata.etag property for every entity instance, and this property is automatically returned with each retrieved entity record. For more information, see Retrieve an entity using the Web API.
Use If-Match andIf-None-Match headers with ETag values to check whether the current version of a resource matches the one last retrieved, matches any previous version or matches no version. These comparisons form the basis of conditional operation support. Dynamics 365 provides ETags to support conditional retrievals, optimistic concurrency, and limited upsert operations.
Warning |
|---|
Client code should not give any meaning to the specific value of an ETag, nor to any apparent relationship between ETags beyond equality or inequality. For example, an ETag value for a more recent version of a resource is not guaranteed to be greater than the ETag value for an earlier version. Also, the algorithm used to generate new ETag values may change without notice between releases of a service. |
Etags enable you to optimize record retrievals whenever you access the same record multiple times. If you have previously retrieved a record, you can pass the ETag value with the If-None-Match header to request data to be retrieved only if it has changed since the last time it was retrieved. If the data has changed, the request returns an HTTP status of 200 (OK) with the latest data in the body of the request. If the data hasn’t changed, the HTTP status code 304 (Not Modified) is returned to indicate that the entity hasn’t been modified. The following example message pair returns data for an account entity with the accountid equal to 00000000-0000-0000-0000-000000000001 when the data hasn’t changed since it was last retrieved.
- Request
-
GET [Organization URI]/api/data/v8.2/accounts(00000000-0000-0000-0000-000000000001)?$select=accountcategorycode,accountnumber,creditonhold,createdon,numberofemployees,name,revenue HTTP/1.1 Accept: application/json OData-MaxVersion: 4.0 OData-Version: 4.0 If-None-Match: W/"468026"
- Response
-
HTTP/1.1 304 Not Modified Content-Type: application/json; odata.metadata=minimal OData-Version: 4.0
An upsert ordinarily operates by creating an entity if it doesn’t exist; otherwise, it updates an existing entity. However, ETags can be used to further constrain upserts to either prevent creates or to prevent updates.
If you are updating data and there is some possibility that the entity was deleted intentionally, you will not want to re-create the entity. To prevent this, add an If-Match header to the request with a value of "*".
- Request
-
PATCH [Organization URI]/api/data/v8.2/accounts(00000000-0000-0000-0000-000000000001) HTTP/1.1 Content-Type: application/json OData-MaxVersion: 4.0 OData-Version: 4.0 If-Match: "*" { "name": "Updated Sample Account ", "creditonhold": true, "address1_latitude": 47.639583, "description": "This is the updated description of the sample account", "revenue": 6000000, "accountcategorycode": 2 } - Response
If the entity is found, you’ll get a normal response with status 204 (No Content). When the entity is not found, you’ll get the following response with status 404 (Not Found).
HTTP/1.1 404 Not Found OData-Version: 4.0 Content-Type: application/json; odata.metadata=minimal { "error": { "code": "", "message": "account With Id = 00000000-0000-0000-0000-000000000001 Does Not Exist", "innererror": { "message": "account With Id = 00000000-0000-0000-0000-000000000001 Does Not Exist", "type": "System.ServiceModel.FaultException`1[[Microsoft.Xrm.Sdk.OrganizationServiceFault, Microsoft.Xrm.Sdk, Version=8.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]", "stacktrace": <stack trace removed for brevity> } } }
If you’re inserting data, there is some possibility that a record with the same id value already exists in the system and you may not want to update it. To prevent this, add an If-None-Match header to the request with a value of "*".
- Request
-
PATCH [Organization URI]/api/data/v8.2/accounts(00000000-0000-0000-0000-000000000001) HTTP/1.1 Content-Type: application/json OData-MaxVersion: 4.0 OData-Version: 4.0 If-None-Match: "*" { "name": "Updated Sample Account ", "creditonhold": true, "address1_latitude": 47.639583, "description": "This is the updated description of the sample account", "revenue": 6000000, "accountcategorycode": 2 } - Response
If the entity isn’t found, you will get a normal response with status 204 (No Content). When the entity is found, you’ll get the following response with status 412 (Precondition Failed).
HTTP/1.1 412 Precondition Failed OData-Version: 4.0 Content-Type: application/json; odata.metadata=minimal { "error":{ "code":"", "message":"A record with matching key values already exists.", "innererror":{ "message":"Cannot insert duplicate key.", "type":"System.ServiceModel.FaultException`1[[Microsoft.Xrm.Sdk.OrganizationServiceFault, Microsoft.Xrm.Sdk, Version=8.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]", "stacktrace":<stack trace removed for brevity> } } }
You can use optimistic concurrency to detect whether an entity has been modified since it was last retrieved. If the entity you intend to update or delete has changed on the server since you retrieved it, you may not want to complete the update or delete operation. By applying the pattern shown here you can detect this situation, retrieve the most recent version of the entity, and apply any necessary criteria to re-evaluate whether to try the operation again.
The following delete request for an account with accountid of00000000-0000-0000-0000-000000000001 fails because the ETag value sent with the If-Match header is different from the current value. If the value had matched, a 204 (No Content) status is expected.
- Request
-
DELETE [Organization URI]/api/data/v8.2/accounts(00000000-0000-0000-0000-000000000001) HTTP/1.1 If-Match: W/"470867" Accept: application/json OData-MaxVersion: 4.0 OData-Version: 4.0
- Response
-
HTTP/1.1 412 Precondition Failed Content-Type: application/json; odata.metadata=minimal OData-Version: 4.0 { "error":{ "code":"","message":"The version of the existing record doesn't match the RowVersion property provided.", "innererror":{ "message":"The version of the existing record doesn't match the RowVersion property provided.", "type":"System.ServiceModel.FaultException`1[[Microsoft.Xrm.Sdk.OrganizationServiceFault, Microsoft.Xrm.Sdk, Version=8.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]", "stacktrace":" <stack trace details omitted for brevity> } } }
The following update request for an account with accountid of 00000000-0000-0000-0000-000000000001 fails because the ETag value sent with the If-Match header is different from the current value. If the value had matched, a 204 (No Content) status is expected.
- Request
-
PATCH [Organization URI]/api/data/v8.2/accounts(00000000-0000-0000-0000-000000000001) HTTP/1.1 If-Match: W/"470867" Accept: application/json OData-MaxVersion: 4.0 OData-Version: 4.0 {"name":"Updated Account Name"} - Response
-
HTTP/1.1 412 Precondition Failed Content-Type: application/json; odata.metadata=minimal OData-Version: 4.0 { "error":{ "code":"","message":"The version of the existing record doesn't match the RowVersion property provided.", "innererror":{ "message":"The version of the existing record doesn't match the RowVersion property provided.", "type":"System.ServiceModel.FaultException`1[[Microsoft.Xrm.Sdk.OrganizationServiceFault, Microsoft.Xrm.Sdk, Version=8.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]", "stacktrace":" <stack trace details omitted for brevity> } } }
Web API Conditional Operations Sample (C#)
Web API Conditional Operations Sample (Client-side JavaScript)
Perform operations using the Web API
Compose HTTP requests and handle errors
Query Data using the Web API
Create an entity using the Web API
Retrieve an entity using the Web API
Update and delete entities using the Web API
Associate and disassociate entities using the Web API
Use Web API functions
Use Web API actions
Execute batch operations using the Web API
Impersonate another user using the Web API
Microsoft Dynamics 365
© 2016 Microsoft. All rights reserved. Copyright
