Skip to content

Commit 50b59f3

Browse files
author
ryancrawcour
committed
added samples for conditional operations with ETags
1 parent e9766c1 commit 50b59f3

File tree

1 file changed

+91
-2
lines changed

1 file changed

+91
-2
lines changed

samples/code-samples/DocumentManagement/Program.cs

Lines changed: 91 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
using System.Configuration;
1111
using System.IO;
1212
using System.Linq;
13+
using System.Net;
1314
using System.Text;
1415
using System.Threading.Tasks;
1516

@@ -32,9 +33,12 @@
3233
// 1.5 - Replace a document
3334
// 1.6 - Upsert a document
3435
// 1.7 - Delete a document
35-
// 1.8 - Use Optimistic concurrency when doing a replace
3636
//
3737
// 2. Work with dynamic objects
38+
//
39+
// 3. Using ETags to control execution
40+
// 3.1 - Use ETag with ReplaceDocument for optimistic concurrency
41+
// 3.2 - Use ETag with ReadDocument to only return a result if the ETag of the request does not match
3842
//-----------------------------------------------------------------------------------------------------------
3943
// See Also -
4044
//
@@ -100,6 +104,8 @@ private static async Task RunDocumentsDemo()
100104
await BasicCRUDAsync();
101105

102106
await UseDynamics();
107+
108+
await UseETags();
103109
}
104110

105111
/// <summary>
@@ -318,7 +324,7 @@ private static async Task BasicCRUDAsync()
318324
//******************************************************************************************************************
319325
// 1.7 - Delete a document
320326
//******************************************************************************************************************
321-
Console.WriteLine("\n1.7 - Upserting a document");
327+
Console.WriteLine("\n1.7 - Deleting a document");
322328

323329
response = await client.DeleteDocumentAsync(UriFactory.CreateDocumentUri(databaseId, collectionId, "POCO3"));
324330

@@ -380,6 +386,89 @@ private static async Task UseDynamics()
380386
Console.WriteLine("shippedDate: {0} and foo: {1} of replaced document", replaced.GetPropertyValue<DateTime>("shippedDate"), replaced.GetPropertyValue<string>("foo"));
381387
}
382388

389+
/// <summary>
390+
/// 3. Using ETags to control execution of operations
391+
/// 3.1 - Use ETag to control if a ReplaceDocument operation should check if ETag of request matches Document
392+
/// 3.2 - Use ETag to control if ReadDocument should only return a result if the ETag of the request does not match the Document
393+
/// </summary>
394+
/// <returns></returns>
395+
private static async Task UseETags()
396+
{
397+
398+
//******************************************************************************************************************
399+
// 3.1 - Use ETag to control if a replace should succeed, or not, based on whether the ETag on the requst matches
400+
// the current ETag value of the persisted Document
401+
//
402+
// All documents in DocumentDB have an _etag field. This gets set on the server every time a document is updated.
403+
//
404+
// When doing a replace of a document you can opt-in to having the server only apply the Replace if the ETag
405+
// on the request matches the ETag of the document on the server.
406+
// If someone did an update to the same document since you read it, then the ETag on the server will not match
407+
// and the Replace operation can be rejected.
408+
//******************************************************************************************************************
409+
Console.WriteLine("\n3.1 - Using optimistic concurrency when doing a ReplaceDocumentAsync");
410+
411+
//read a document
412+
Document readDoc = await client.ReadDocumentAsync(UriFactory.CreateDocumentUri(databaseId, collectionId, "POCO1"));
413+
Console.WriteLine("ETag of read document - {0}", readDoc.ETag);
414+
415+
//take advantage of the dynamic nature of Document and set a new property on the document we just read
416+
readDoc.SetPropertyValue("foo", "bar");
417+
418+
//persist the change back to the server
419+
Document updatedDoc = await client.ReplaceDocumentAsync(readDoc);
420+
Console.WriteLine("ETag of document now that is has been updated - {0}", updatedDoc.ETag);
421+
422+
//now, using the originally retrieved document do another update
423+
//but set the AccessCondition class with the ETag of the originally read document and also set the AccessConditionType
424+
//this tells the service to only do this operation if ETag on the request matches the current ETag on the document
425+
//in our case it won't, because we updated the document and therefore gave it a new ETag
426+
try
427+
{
428+
var ac = new AccessCondition { Condition = readDoc.ETag, Type = AccessConditionType.IfMatch };
429+
readDoc.SetPropertyValue("foo", "the updated value of foo");
430+
updatedDoc = await client.ReplaceDocumentAsync(readDoc, new RequestOptions { AccessCondition = ac });
431+
}
432+
catch (DocumentClientException dce)
433+
{
434+
// now notice the failure when attempting the update
435+
// this is because the ETag on the server no longer matches the ETag of doc (b/c it was changed in step 2)
436+
if (dce.StatusCode == HttpStatusCode.PreconditionFailed)
437+
{
438+
Console.WriteLine("As expected, we have a pre-condition failure exception\n");
439+
}
440+
}
441+
442+
//*******************************************************************************************************************
443+
// 3.2 - ETag on a ReadDcoumentAsync request can be used to tell the server whether it should return a result, or not
444+
//
445+
// By setting the ETag on a ReadDocumentRequest along with an AccessCondition of IfNoneMatch instructs the server
446+
// to only return a result if the ETag of the request does not match that of the persisted Document
447+
//*******************************************************************************************************************
448+
Console.WriteLine("\n3.2 - Using ETag to do a conditional ReadDocumentAsync");
449+
450+
//get a document
451+
var response = await client.ReadDocumentAsync(UriFactory.CreateDocumentUri(databaseId, collectionId, "POCO2"));
452+
readDoc = response.Resource;
453+
Console.WriteLine("Read doc with StatusCode of {0}", response.StatusCode);
454+
455+
//get the document again with conditional access set, no document should be returned
456+
var accessCondition = new AccessCondition
457+
{
458+
Condition = readDoc.ETag,
459+
Type = AccessConditionType.IfNoneMatch
460+
};
461+
462+
response = await client.ReadDocumentAsync(UriFactory.CreateDocumentUri(databaseId, collectionId, "POCO2"), new RequestOptions { AccessCondition = accessCondition });
463+
Console.WriteLine("Read doc with StatusCode of {0}", response.StatusCode);
464+
465+
//now change something on the document, then do another get and this time we should get the document back
466+
readDoc.SetPropertyValue("foo", "updated");
467+
response = await client.ReplaceDocumentAsync(readDoc);
468+
469+
response = await client.ReadDocumentAsync(UriFactory.CreateDocumentUri(databaseId, collectionId, "POCO2"), new RequestOptions { AccessCondition = accessCondition });
470+
Console.WriteLine("Read doc with StatusCode of {0}", response.StatusCode);
471+
}
383472
private static void Cleanup()
384473
{
385474
client.DeleteDatabaseAsync(UriFactory.CreateDatabaseUri(databaseId)).Wait();

0 commit comments

Comments
 (0)