From 3927100ea429d00b20b7ed6739487ff1dd238563 Mon Sep 17 00:00:00 2001
From: "David R. Williamson" <drwill@microsoft.com>
Date: Tue, 2 Jun 2020 17:07:34 -0700
Subject: [PATCH] samples(adt): add snippets for get and deserialize digital
 twin

---
 .../Azure.DigitalTwins.Core.netstandard2.0.cs |   2 +
 .../ComponentSamples.cs                       |  73 ++++++--
 .../CustomDigitalTwin.cs                      |   3 +
 .../Azure.DigitalTwins.Core/samples/Readme.md | 156 ++++++++++++------
 .../src/DigitalTwinsClient.cs                 |  28 +++-
 .../src/Serialization/BasicDigitalTwin.cs     |  36 +++-
 .../Serialization/UpdateOperationsUtility.cs  |   4 +-
 7 files changed, 219 insertions(+), 83 deletions(-)

diff --git a/sdk/digitaltwins/Azure.DigitalTwins.Core/api/Azure.DigitalTwins.Core.netstandard2.0.cs b/sdk/digitaltwins/Azure.DigitalTwins.Core/api/Azure.DigitalTwins.Core.netstandard2.0.cs
index 2e45f3395b5e8..faaa813f09fb0 100644
--- a/sdk/digitaltwins/Azure.DigitalTwins.Core/api/Azure.DigitalTwins.Core.netstandard2.0.cs
+++ b/sdk/digitaltwins/Azure.DigitalTwins.Core/api/Azure.DigitalTwins.Core.netstandard2.0.cs
@@ -125,6 +125,8 @@ namespace Azure.DigitalTwins.Core.Serialization
     public partial class BasicDigitalTwin : Azure.DigitalTwins.Core.Serialization.ModelProperties
     {
         public BasicDigitalTwin() { }
+        [System.Text.Json.Serialization.JsonPropertyNameAttribute("$etag")]
+        public string ETag { get { throw null; } set { } }
         [System.Text.Json.Serialization.JsonPropertyNameAttribute("$dtId")]
         public string Id { get { throw null; } set { } }
     }
diff --git a/sdk/digitaltwins/Azure.DigitalTwins.Core/samples/DigitalTwinsClientSample/ComponentSamples.cs b/sdk/digitaltwins/Azure.DigitalTwins.Core/samples/DigitalTwinsClientSample/ComponentSamples.cs
index bf7fe203a8dfc..345841b10913f 100644
--- a/sdk/digitaltwins/Azure.DigitalTwins.Core/samples/DigitalTwinsClientSample/ComponentSamples.cs
+++ b/sdk/digitaltwins/Azure.DigitalTwins.Core/samples/DigitalTwinsClientSample/ComponentSamples.cs
@@ -3,6 +3,7 @@
 
 using System;
 using System.Collections.Generic;
+using System.Net;
 using System.Text.Json;
 using System.Threading.Tasks;
 using Azure.DigitalTwins.Core.Serialization;
@@ -32,7 +33,7 @@ public async Task RunSamplesAsync()
 
             string componentModelId = await GetUniqueModelIdAsync(SamplesConstants.TemporaryComponentModelPrefix, DigitalTwinsClient).ConfigureAwait(false);
             string modelId = await GetUniqueModelIdAsync(SamplesConstants.TemporaryModelPrefix, DigitalTwinsClient).ConfigureAwait(false);
-            string dtId1 = await GetUniqueTwinIdAsync(SamplesConstants.TemporaryTwinPrefix, DigitalTwinsClient).ConfigureAwait(false);
+            string basicDtId = await GetUniqueTwinIdAsync(SamplesConstants.TemporaryTwinPrefix, DigitalTwinsClient).ConfigureAwait(false);
 
             string newComponentModelPayload = SamplesConstants.TemporaryComponentModelPayload
                 .Replace(SamplesConstants.ComponentId, componentModelId);
@@ -49,11 +50,11 @@ public async Task RunSamplesAsync()
 
             #region Snippet:DigitalTwinsSampleCreateBasicTwin
 
-            // Create digital twin with Component payload using the BasicDigitalTwin serialization helper
+            // Create digital twin with component payload using the BasicDigitalTwin serialization helper
 
             var basicDigitalTwin = new BasicDigitalTwin
             {
-                Id = dtId1
+                Id = basicDtId
             };
             basicDigitalTwin.Metadata.ModelId = modelId;
             basicDigitalTwin.CustomProperties.Add("Prop1", "Value1");
@@ -66,19 +67,45 @@ public async Task RunSamplesAsync()
 
             basicDigitalTwin.CustomProperties.Add("Component1", componentMetadata);
 
-            string dt1Payload = JsonSerializer.Serialize(basicDigitalTwin);
+            string basicDtPayload = JsonSerializer.Serialize(basicDigitalTwin);
 
-            Response<string> createDt1Response = await DigitalTwinsClient.CreateDigitalTwinAsync(dtId1, dt1Payload).ConfigureAwait(false);
-            Console.WriteLine($"Created digital twin {dtId1} with response {createDt1Response.GetRawResponse().Status}.");
+            Response<string> createBasicDtResponse = await DigitalTwinsClient.CreateDigitalTwinAsync(basicDtId, basicDtPayload).ConfigureAwait(false);
+            Console.WriteLine($"Created digital twin {basicDtId} with response {createBasicDtResponse.GetRawResponse().Status}.");
 
             #endregion Snippet:DigitalTwinsSampleCreateBasicTwin
 
+            // You can also get a digital twin and deserialize it into a BasicDigitalTwin.
+            // It works well for basic stuff, but as you can see it gets more difficult when delving into
+            // more complex properties, like components.
+
+            #region Snippet:DigitalTwinsSampleGetBasicDigitalTwin
+
+            Response<string> getBasicDtResponse = await DigitalTwinsClient.GetDigitalTwinAsync(basicDtId).ConfigureAwait(false);
+            if (getBasicDtResponse.GetRawResponse().Status == (int)HttpStatusCode.OK)
+            {
+                BasicDigitalTwin basicDt = JsonSerializer.Deserialize<BasicDigitalTwin>(getBasicDtResponse.Value);
+
+                // Must cast Component1 as a JsonElement and get its raw text in order to deserialize it as a dictionary
+                string component1RawText = ((JsonElement)basicDt.CustomProperties["Component1"]).GetRawText();
+                var component1 = JsonSerializer.Deserialize<IDictionary<string, object>>(component1RawText);
+
+                Console.WriteLine($"Retrieved and deserialized digital twin {basicDt.Id}  with ETag {basicDt.ETag} " +
+                    $"and Prop1 '{basicDt.CustomProperties["Prop1"]}', Prop2 '{basicDt.CustomProperties["Prop2"]}', " +
+                    $"ComponentProp1 '{component1["ComponentProp1"]}', ComponentProp2 '{component1["ComponentProp2"]}'");
+            }
+
+            #endregion Snippet:DigitalTwinsSampleGetBasicDigitalTwin
+
+            // Alternatively, you can create your own custom data types to serialize and deserialize your digital twins.
+            // By specifying your properties and types directly, it requires less code or knowledge of the type for
+            // interaction.
+
             #region Snippet:DigitalTwinsSampleCreateCustomTwin
 
-            string dtId2 = await GetUniqueTwinIdAsync(SamplesConstants.TemporaryTwinPrefix, DigitalTwinsClient).ConfigureAwait(false);
+            string customDtId = await GetUniqueTwinIdAsync(SamplesConstants.TemporaryTwinPrefix, DigitalTwinsClient).ConfigureAwait(false);
             var customDigitalTwin = new CustomDigitalTwin
             {
-                Id = dtId2,
+                Id = customDtId,
                 Metadata = new CustomDigitalTwinMetadata { ModelId = modelId },
                 Prop1 = "Prop1 val",
                 Prop2 = "Prop2 val",
@@ -91,11 +118,27 @@ public async Task RunSamplesAsync()
             };
             string dt2Payload = JsonSerializer.Serialize(customDigitalTwin);
 
-            Response<string> createDt2Response = await DigitalTwinsClient.CreateDigitalTwinAsync(dtId2, dt2Payload).ConfigureAwait(false);
-            Console.WriteLine($"Created digital twin {dtId2} with response {createDt2Response.GetRawResponse().Status}.");
+            Response<string> createCustomDtResponse = await DigitalTwinsClient.CreateDigitalTwinAsync(customDtId, dt2Payload).ConfigureAwait(false);
+            Console.WriteLine($"Created digital twin {customDtId} with response {createCustomDtResponse.GetRawResponse().Status}.");
 
             #endregion Snippet:DigitalTwinsSampleCreateCustomTwin
 
+            // Getting and deserializing a digital twin into a custom data type is extremely easy.
+            // Custom types provide the best possible experience.
+
+            #region Snippet:DigitalTwinsSampleGetCustomDigitalTwin
+
+            Response<string> getCustomDtResponse = await DigitalTwinsClient.GetDigitalTwinAsync(customDtId).ConfigureAwait(false);
+            if (getCustomDtResponse.GetRawResponse().Status == (int)HttpStatusCode.OK)
+            {
+                CustomDigitalTwin customDt = JsonSerializer.Deserialize<CustomDigitalTwin>(getCustomDtResponse.Value);
+                Console.WriteLine($"Retrieved and deserialized digital twin {customDt.Id} with ETag {customDt.ETag} " +
+                    $"and Prop1 '{customDt.Prop1}', Prop2 '{customDt.Prop2}', " +
+                    $"ComponentProp1 '{customDt.Component1.ComponentProp1}, ComponentProp2 '{customDt.Component1.ComponentProp2}'");
+            }
+
+            #endregion Snippet:DigitalTwinsSampleGetCustomDigitalTwin
+
             #region Snippet:DigitalTwinsSampleUpdateComponent
 
             // Update Component1 by replacing the property ComponentProp1 value
@@ -103,9 +146,9 @@ public async Task RunSamplesAsync()
             componentUpdateUtility.AppendReplaceOp("/ComponentProp1", "Some new value");
             string updatePayload = componentUpdateUtility.Serialize();
 
-            Response<string> response = await DigitalTwinsClient.UpdateComponentAsync(dtId1, "Component1", updatePayload);
+            Response<string> response = await DigitalTwinsClient.UpdateComponentAsync(basicDtId, "Component1", updatePayload);
 
-            Console.WriteLine($"Updated component for digital twin {dtId1}. Update response status: {response.GetRawResponse().Status}");
+            Console.WriteLine($"Updated component for digital twin {basicDtId}. Update response status: {response.GetRawResponse().Status}");
 
             #endregion Snippet:DigitalTwinsSampleUpdateComponent
 
@@ -113,7 +156,7 @@ public async Task RunSamplesAsync()
 
             #region Snippet:DigitalTwinsSampleGetComponent
 
-            response = await DigitalTwinsClient.GetComponentAsync(dtId1, SamplesConstants.ComponentPath).ConfigureAwait(false);
+            response = await DigitalTwinsClient.GetComponentAsync(basicDtId, SamplesConstants.ComponentPath).ConfigureAwait(false);
 
             Console.WriteLine($"Get component for digital twin: \n{response.Value}. Get response status: {response.GetRawResponse().Status}");
 
@@ -123,8 +166,8 @@ public async Task RunSamplesAsync()
 
             try
             {
-                await DigitalTwinsClient.DeleteDigitalTwinAsync(dtId1).ConfigureAwait(false);
-                await DigitalTwinsClient.DeleteDigitalTwinAsync(dtId2).ConfigureAwait(false);
+                await DigitalTwinsClient.DeleteDigitalTwinAsync(basicDtId).ConfigureAwait(false);
+                await DigitalTwinsClient.DeleteDigitalTwinAsync(customDtId).ConfigureAwait(false);
             }
             catch (RequestFailedException ex)
             {
diff --git a/sdk/digitaltwins/Azure.DigitalTwins.Core/samples/DigitalTwinsClientSample/CustomDigitalTwin.cs b/sdk/digitaltwins/Azure.DigitalTwins.Core/samples/DigitalTwinsClientSample/CustomDigitalTwin.cs
index 4a55f8c5e1d49..ff7386b9ee35b 100644
--- a/sdk/digitaltwins/Azure.DigitalTwins.Core/samples/DigitalTwinsClientSample/CustomDigitalTwin.cs
+++ b/sdk/digitaltwins/Azure.DigitalTwins.Core/samples/DigitalTwinsClientSample/CustomDigitalTwin.cs
@@ -15,6 +15,9 @@ internal class CustomDigitalTwin
         [JsonPropertyName("$dtId")]
         public string Id { get; set; }
 
+        [JsonPropertyName("$etag")]
+        public string ETag { get; set; }
+
         [JsonPropertyName("$metadata")]
         public CustomDigitalTwinMetadata Metadata { get; set; }
 
diff --git a/sdk/digitaltwins/Azure.DigitalTwins.Core/samples/Readme.md b/sdk/digitaltwins/Azure.DigitalTwins.Core/samples/Readme.md
index 2c68cd1672bd1..a6536b1274f73 100644
--- a/sdk/digitaltwins/Azure.DigitalTwins.Core/samples/Readme.md
+++ b/sdk/digitaltwins/Azure.DigitalTwins.Core/samples/Readme.md
@@ -1,16 +1,17 @@
-# Digital Twin Samples
+# Digital Twins Samples
+
 You can explore azure digital twin APIs (using the SDK) using the samples project. 
 Sample project demonstrates the following:
-* Create, get and decommision models
-* Create, query and delete a Digital Twin
+* Create, get, and decommission models
+* Create, query, and delete a Digital Twin
 * Get and update components for a Digital Twin
-* Create, get and delete relationships between Digital Twins
-* Create, get and delete event routes for Digital Twin
+* Create, get, and delete relationships between Digital Twins
+* Create, get, and delete event routes for Digital Twin
 * Publish telemetry messages to a Digital Twin and Digital Twin component
 
-## Creating Digital Twin Client
+## Creating the digital twins client
 
-To create a new Digital Twin Client, you need the endpoint to an Azure Digital Twin and credentials.
+To create a new digital twins Client, you need the endpoint to an Azure Digital Twin instance and credentials.
 In the sample below, you can set `AdtEndpoint`, `TenantId`, `ClientId`, and `ClientSecret` as command-line arguments.
 The client requires an instance of [TokenCredential](https://docs.microsoft.com/en-us/dotnet/api/azure.core.tokencredential?view=azure-dotnet).
 In these samples, we illustrate how to use two derived classes: ClientSecretCredential, InteractiveLogin.
@@ -52,9 +53,9 @@ var dtClient = new DigitalTwinsClient(
     clientOptions);
 ```
 
-## Create, List, Decommision and Delete Models
+## Create, list, decommission, and delete models
 
-### Create Models
+### Create models
 
 Let's create models using the code below. You need to pass in List<string> containing list of json models. Check out sample models [here](https://github.com/Azure/azure-sdk-for-net-pr/tree/feature/IoT-ADT/sdk/digitaltwins/Azure.DigitalTwins.Core/samples/DigitalTwinsClientSample/DTDL/Models "Models")
 
@@ -63,9 +64,9 @@ Response<IReadOnlyList<ModelData>> response = await DigitalTwinsClient.CreateMod
 Console.WriteLine($"Successfully created a model with Id: {newComponentModelId}, {sampleModelId}, status: {response.GetRawResponse().Status}");
 ```
 
-### List Models
+### List models
 
-Using `GetModelsAsync`, all created models are listed as AsyncPageable<ModelData>
+Using `GetModelsAsync`, all created models are returned as AsyncPageable<ModelData>
 
 ```C# Snippet:DigitalTwinsSampleGetModels
 AsyncPageable<ModelData> allModels = DigitalTwinsClient.GetModelsAsync();
@@ -81,9 +82,9 @@ Use `GetModelAsync` with model's unique identifier to get a specific model
 Response<ModelData> sampleModel = await DigitalTwinsClient.GetModelAsync(sampleModelId).ConfigureAwait(false);
 ```
 
-### Decommission Models
+### Decommission models
 
-To decommision a model, pass in a model id for the model you want to decommision
+To decommision a model, pass in a model Id for the model you want to decommision.
 
 ```C# Snippet:DigitalTwinsSampleDecommisionModel
 try
@@ -98,9 +99,9 @@ catch (Exception ex)
 }
 ```
 
-### Delete Models
+### Delete models
 
-To delete a model, pass in a model id for the model you want to delete
+To delete a model, pass in a model Id for the model you want to delete.
 
 ```C# Snippet:DigitalTwinsSampleDeleteModel
 try
@@ -115,9 +116,9 @@ catch (Exception ex)
 }
 ```
 
-## Create and delete Digital Twin
+## Create and delete digital twins
 
-### Create Digital Twin
+### Create digital twins
 
 For Creating Twin you will need to provide Id of a digital Twin such as `myTwin` and the application/json digital twin based on the model created earlier. You can look at sample application/json [here](https://github.com/Azure/azure-sdk-for-net-pr/tree/feature/IoT-ADT/sdk/digitaltwins/Azure.DigitalTwins.Core/samples/DigitalTwinsClientSample/DTDL/DigitalTwins "DigitalTwin").
 
@@ -125,11 +126,11 @@ One option is to use the provided class BasicDigitalTwin for serialization and d
 It uses functionality from the `System.Text.Json` library to maintain any unmapped json properties to a dictionary.
 
 ```C# Snippet:DigitalTwinsSampleCreateBasicTwin
-// Create digital twin with Component payload using the BasicDigitalTwin serialization helper
+// Create digital twin with component payload using the BasicDigitalTwin serialization helper
 
 var basicDigitalTwin = new BasicDigitalTwin
 {
-    Id = dtId1
+    Id = basicDtId
 };
 basicDigitalTwin.Metadata.ModelId = modelId;
 basicDigitalTwin.CustomProperties.Add("Prop1", "Value1");
@@ -142,20 +143,67 @@ componentMetadata.CustomProperties.Add("ComponentProp2", "ComponentValue2");
 
 basicDigitalTwin.CustomProperties.Add("Component1", componentMetadata);
 
-string dt1Payload = JsonSerializer.Serialize(basicDigitalTwin);
+string basicDtPayload = JsonSerializer.Serialize(basicDigitalTwin);
+
+Response<string> createBasicDtResponse = await DigitalTwinsClient.CreateDigitalTwinAsync(basicDtId, basicDtPayload).ConfigureAwait(false);
+Console.WriteLine($"Created digital twin {basicDtId} with response {createBasicDtResponse.GetRawResponse().Status}.");
+```
+
+Alternatively, you can create your own custom data types to serialize and deserialize your digital twins.
+By specifying your properties and types directly, it requires less code or knowledge of the type for
+interaction.
+
+```C# Snippet:DigitalTwinsSampleCreateCustomTwin
+string customDtId = await GetUniqueTwinIdAsync(SamplesConstants.TemporaryTwinPrefix, DigitalTwinsClient).ConfigureAwait(false);
+var customDigitalTwin = new CustomDigitalTwin
+{
+    Id = customDtId,
+    Metadata = new CustomDigitalTwinMetadata { ModelId = modelId },
+    Prop1 = "Prop1 val",
+    Prop2 = "Prop2 val",
+    Component1 = new Component1
+    {
+        Metadata = new Component1Metadata { ModelId = componentModelId },
+        ComponentProp1 = "Component prop1 val",
+        ComponentProp2 = "Component prop2 val",
+    }
+};
+string dt2Payload = JsonSerializer.Serialize(customDigitalTwin);
+
+Response<string> createCustomDtResponse = await DigitalTwinsClient.CreateDigitalTwinAsync(customDtId, dt2Payload).ConfigureAwait(false);
+Console.WriteLine($"Created digital twin {customDtId} with response {createCustomDtResponse.GetRawResponse().Status}.");
+```
+
+### Get and deserialize a digital twin
+
 
-Response<string> createDt1Response = await DigitalTwinsClient.CreateDigitalTwinAsync(dtId1, dt1Payload).ConfigureAwait(false);
-Console.WriteLine($"Created digital twin {dtId1} with response {createDt1Response.GetRawResponse().Status}.");
+You can get a digital twin and deserialize it into a BasicDigitalTwin.
+It works well for basic stuff, but as you can see it gets more difficult when delving into more complex properties, like components.
+
+```C# Snippet:DigitalTwinsSampleGetBasicDigitalTwin
+Response<string> getBasicDtResponse = await DigitalTwinsClient.GetDigitalTwinAsync(basicDtId).ConfigureAwait(false);
+if (getBasicDtResponse.GetRawResponse().Status == (int)HttpStatusCode.OK)
+{
+    BasicDigitalTwin basicDt = JsonSerializer.Deserialize<BasicDigitalTwin>(getBasicDtResponse.Value);
+
+    // Must cast Component1 as a JsonElement and get its raw text in order to deserialize it as a dictionary
+    string component1RawText = ((JsonElement)basicDt.CustomProperties["Component1"]).GetRawText();
+    var component1 = JsonSerializer.Deserialize<IDictionary<string, object>>(component1RawText);
+
+    Console.WriteLine($"Retrieved and deserialized digital twin {basicDt.Id}  with ETag {basicDt.ETag} " +
+        $"and Prop1 '{basicDt.CustomProperties["Prop1"]}', Prop2 '{basicDt.CustomProperties["Prop2"]}', " +
+        $"ComponentProp1 '{component1["ComponentProp1"]}', ComponentProp2 '{component1["ComponentProp2"]}'");
+}
 ```
 
-For known twin model types, it may be best to create your own class that maps all the properties.
-This makes your code more readable, and properties easier to work with.
+Getting and deserializing a digital twin into a custom data type is extremely easy.
+Custom types provide the best possible experience.
 
 ```C# Snippet:DigitalTwinsSampleCreateCustomTwin
-string dtId2 = await GetUniqueTwinIdAsync(SamplesConstants.TemporaryTwinPrefix, DigitalTwinsClient).ConfigureAwait(false);
+string customDtId = await GetUniqueTwinIdAsync(SamplesConstants.TemporaryTwinPrefix, DigitalTwinsClient).ConfigureAwait(false);
 var customDigitalTwin = new CustomDigitalTwin
 {
-    Id = dtId2,
+    Id = customDtId,
     Metadata = new CustomDigitalTwinMetadata { ModelId = modelId },
     Prop1 = "Prop1 val",
     Prop2 = "Prop2 val",
@@ -168,11 +216,11 @@ var customDigitalTwin = new CustomDigitalTwin
 };
 string dt2Payload = JsonSerializer.Serialize(customDigitalTwin);
 
-Response<string> createDt2Response = await DigitalTwinsClient.CreateDigitalTwinAsync(dtId2, dt2Payload).ConfigureAwait(false);
-Console.WriteLine($"Created digital twin {dtId2} with response {createDt2Response.GetRawResponse().Status}.");
+Response<string> createCustomDtResponse = await DigitalTwinsClient.CreateDigitalTwinAsync(customDtId, dt2Payload).ConfigureAwait(false);
+Console.WriteLine($"Created digital twin {customDtId} with response {createCustomDtResponse.GetRawResponse().Status}.");
 ```
 
-### Query Digital Twin
+### Query digital twins
 
 Query the Azure Digital Twins instance for digital twins using the [Azure Digital Twins Query Store lanaguage](https://review.docs.microsoft.com/en-us/azure/digital-twins-v2/concepts-query-language?branch=pr-en-us-114648). Query calls support paging. Here's an example of how to query for digital twins and how to iterate over the results.
 
@@ -222,19 +270,19 @@ await foreach (Page<string> page in asyncPageableResponseWithCharge.AsPages())
 }
 ```
 
-### Delete Digital Twin
+### Delete digital twins
 
-Delete a digital twin simply by providing id of a digital twin as below.
+Delete a digital twin simply by providing Id of a digital twin as below.
 
 ```C# Snippet:DigitalTwinsSampleDeleteTwin
 await DigitalTwinsClient.DeleteDigitalTwinAsync(twin.Key).ConfigureAwait(false);
 ```
 
-## Get and update Digital Twin Component
+## Get and update digital twin components
 
-### Update Digital Twin Component
+### Update digital twin components
 
-To update a component or in other words to replace, remove and/or add a component property or subproperty within Digital Twin, you would need id of a digital twin, component name and application/json-patch+json operations to be performed on the specified digital twin's component. Here is the sample code on how to do it.  
+To update a component or in other words to replace, remove and/or add a component property or subproperty within Digital Twin, you would need Id of a digital twin, component name and application/json-patch+json operations to be performed on the specified digital twin's component. Here is the sample code on how to do it.  
 
 ```C# Snippet:DigitalTwinsSampleUpdateComponent
 // Update Component1 by replacing the property ComponentProp1 value
@@ -242,26 +290,26 @@ var componentUpdateUtility = new UpdateOperationsUtility();
 componentUpdateUtility.AppendReplaceOp("/ComponentProp1", "Some new value");
 string updatePayload = componentUpdateUtility.Serialize();
 
-Response<string> response = await DigitalTwinsClient.UpdateComponentAsync(dtId1, "Component1", updatePayload);
+Response<string> response = await DigitalTwinsClient.UpdateComponentAsync(basicDtId, "Component1", updatePayload);
 
-Console.WriteLine($"Updated component for digital twin {dtId1}. Update response status: {response.GetRawResponse().Status}");
+Console.WriteLine($"Updated component for digital twin {basicDtId}. Update response status: {response.GetRawResponse().Status}");
 ```
 
-### Get Digital Twin Component
+### Get digital twin components
 
-Get a component by providing name of a component and id of digital twin it belongs to.
+Get a component by providing name of a component and Id of digital twin it belongs to.
 
 ```C# Snippet:DigitalTwinsSampleGetComponent
-response = await DigitalTwinsClient.GetComponentAsync(dtId1, SamplesConstants.ComponentPath).ConfigureAwait(false);
+response = await DigitalTwinsClient.GetComponentAsync(basicDtId, SamplesConstants.ComponentPath).ConfigureAwait(false);
 
 Console.WriteLine($"Get component for digital twin: \n{response.Value}. Get response status: {response.GetRawResponse().Status}");
 ```
 
-## Create and list Digital Twin edges
+## Create and list digital twin relationships
 
-### Create Digital Twin Edge
+### Create digital twin telationships
 
-`CreateEdgeAsync` creates a relationship edge on a digital twin provided with id of a digital twin, name of relationship such as "contains", id of an edge such as "FloorContainsRoom" and an application/json edge to be created. Must contain property with key "$targetId" to specify the target of the edge. Sample payloads for relationships can be found [here](https://github.com/Azure/azure-sdk-for-net-pr/blob/feature/IoT-ADT/sdk/iot/Azure.Iot.DigitalTwins/samples/DigitalTwinServiceClientSample/DTDL/Relationships/HospitalEdges.json "RelationshipExamples").
+`CreateRelationshipAsync` creates a relationship on a digital twin provided with Id of a digital twin, name of relationship such as "contains", Id of an relationship such as "FloorContainsRoom" and an application/json relationship to be created. Must contain property with key "$targetId" to specify the target of the relationship. Sample payloads for relationships can be found [here](https://github.com/Azure/azure-sdk-for-net-pr/blob/feature/IoT-ADT/sdk/iot/Azure.Iot.DigitalTwins/samples/DigitalTwinServiceClientSample/DTDL/Relationships/HospitalRelationships.json "RelationshipExamples").
 
 ```C# Snippet:DigitalTwinsSampleCreateRelationship
 string serializedRelationship = JsonSerializer.Serialize(relationship);
@@ -273,9 +321,9 @@ await DigitalTwinsClient
         serializedRelationship)
     .ConfigureAwait(false);
 ```
-### List Digital Twin Edges
+### List digital twin relationships
 
-`GetEdgesAsync` and `GetIncomingEdgesAsync` lists all the edges and all incoming edges respectively of a digital twin
+`GetrelationshipsAsync` and `GetIncomingRelationshipsAsync` lists all the relationships and all incoming relationships respectively of a digital twin
 
 ```C# Snippet:DigitalTwinsSampleGetRelationships
 AsyncPageable<string> relationships = DigitalTwinsClient.GetRelationshipsAsync(twin.Key);
@@ -285,11 +333,11 @@ AsyncPageable<string> relationships = DigitalTwinsClient.GetRelationshipsAsync(t
 AsyncPageable<IncomingRelationship> incomingRelationships = DigitalTwinsClient.GetIncomingRelationshipsAsync(twin.Key);
 ```
 
-## Create, list and delete event routes of Digital Twin
+## Create, list, and delete event routes of digital twins
 
-### Create Event Route
+### Create event routes
 
-To create Event route, one needs to provide id of an event route such as "sampleEventRoute" and event route data containing the endpoint and optional filter like the example shown below
+To create an event route, provide an Id of an event route such as "sampleEventRoute" and event route data containing the endpoint and optional filter like the example shown below.
 
 ```C# Snippet:DigitalTwinsSampleCreateEventRoute
 string eventFilter = "$eventType = 'DigitalTwinTelemetryMessages' or $eventType = 'DigitalTwinLifecycleNotification'";
@@ -301,9 +349,9 @@ var eventRoute = new EventRoute(_eventhubEndpointName)
 Response createEventRouteResponse = await DigitalTwinsClient.CreateEventRouteAsync(_eventRouteId, eventRoute).ConfigureAwait(false);
 ```
 
-### List Event Routes
+### List event routes
 
-List a specific event route given event route id or all event routes setting options with `GetEventRouteAsync` and `GetEventRoutesAsync`.
+List a specific event route given event route Id or all event routes setting options with `GetEventRouteAsync` and `GetEventRoutesAsync`.
 
 ```C# Snippet:DigitalTwinsSampleGetEventRoutes
 AsyncPageable<EventRoute> response = DigitalTwinsClient.GetEventRoutesAsync();
@@ -313,17 +361,17 @@ await foreach (EventRoute er in response)
 }
 ```
 
-### Delete Event Route
+### Delete event routes
 
-Delete an event route given event route id
+Delete an event route given event route Id.
 
 ```C# Snippet:DigitalTwinsSampleDeleteEventRoute
 Response response = await DigitalTwinsClient.DeleteEventRouteAsync(_eventRouteId).ConfigureAwait(false);
 ```
 
-### Publish telemetry messages to a Digital Twin
+### Publish telemetry messages for a digital twin
 
-To publish a telemetry message to a digital twin, you need to provide the digital twin id, along with the payload on which telemetry that needs the update.
+To publish a telemetry message for a digital twin, you need to provide the digital twin Id, along with the payload on which telemetry that needs the update.
 
 ```C# Snippet:DigitalTwinsSamplePublishTelemetry
 // construct your json telemetry payload by hand.
@@ -331,7 +379,7 @@ Response publishTelemetryResponse = await DigitalTwinsClient.PublishTelemetryAsy
 Console.WriteLine($"Successfully published telemetry message, status: {publishTelemetryResponse.Status}");
 ```
 
-You can also publish a telemetry message to a specific component in a digital twin. In addition to the digital twin id and payload, you need to specify the target component id.
+You can also publish a telemetry message for a specific component in a digital twin. In addition to the digital twin Id and payload, you need to specify the target component Id.
 
 ```C# Snippet:DigitalTwinsSamplePublishComponentTelemetry
 // construct your json telemetry payload by serializing a dictionary.
diff --git a/sdk/digitaltwins/Azure.DigitalTwins.Core/src/DigitalTwinsClient.cs b/sdk/digitaltwins/Azure.DigitalTwins.Core/src/DigitalTwinsClient.cs
index 5d31f6589db76..d82597e05fa91 100644
--- a/sdk/digitaltwins/Azure.DigitalTwins.Core/src/DigitalTwinsClient.cs
+++ b/sdk/digitaltwins/Azure.DigitalTwins.Core/src/DigitalTwinsClient.cs
@@ -114,6 +114,20 @@ protected DigitalTwinsClient()
         /// <param name="digitalTwinId">The Id of the digital twin.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>The application/json digital twin and the http response.</returns>
+        /// <example>
+        /// This sample demonstrates getting and deserializing a digital twin into a custom data type.
+        ///
+        /// <code snippet="Snippet:DigitalTwinsSampleGetCustomDigitalTwin">
+        /// Response&lt;string&gt; getCustomDtResponse = await DigitalTwinsClient.GetDigitalTwinAsync(customDtId).ConfigureAwait(false);
+        /// if (getCustomDtResponse.GetRawResponse().Status == (int)HttpStatusCode.OK)
+        /// {
+        ///     CustomDigitalTwin customDt = JsonSerializer.Deserialize&lt;CustomDigitalTwin&gt;(getCustomDtResponse.Value);
+        ///     Console.WriteLine($&quot;Retrieved and deserialized digital twin {customDt.Id} with ETag {customDt.ETag} &quot; +
+        ///         $&quot;and Prop1 &apos;{customDt.Prop1}&apos;, Prop2 &apos;{customDt.Prop2}&apos;, &quot; +
+        ///         $&quot;ComponentProp1 &apos;{customDt.Component1.ComponentProp1}, ComponentProp2 &apos;{customDt.Component1.ComponentProp2}&apos;&quot;);
+        /// }
+        /// </code>
+        /// </example>
         public virtual Task<Response<string>> GetDigitalTwinAsync(string digitalTwinId, CancellationToken cancellationToken = default)
         {
             return _dtRestClient.GetByIdAsync(digitalTwinId, cancellationToken);
@@ -145,10 +159,10 @@ public virtual Response<string> GetDigitalTwin(string digitalTwinId, Cancellatio
         /// <remarks>The digital twin must be the serialization of an instance of <see cref="Serialization.BasicDigitalTwin"/> or the serialization of an extension of that type.</remarks>
         /// <example>
         /// <code snippet="Snippet:DigitalTwinsSampleCreateCustomTwin">
-        /// string dtId2 = await GetUniqueTwinIdAsync(SamplesConstants.TemporaryTwinPrefix, DigitalTwinsClient).ConfigureAwait(false);
+        /// string customDtId = await GetUniqueTwinIdAsync(SamplesConstants.TemporaryTwinPrefix, DigitalTwinsClient).ConfigureAwait(false);
         /// var customDigitalTwin = new CustomDigitalTwin
         /// {
-        ///     Id = dtId2,
+        ///     Id = customDtId,
         ///     Metadata = new CustomDigitalTwinMetadata { ModelId = modelId },
         ///     Prop1 = &quot;Prop1 val&quot;,
         ///     Prop2 = &quot;Prop2 val&quot;,
@@ -161,8 +175,8 @@ public virtual Response<string> GetDigitalTwin(string digitalTwinId, Cancellatio
         /// };
         /// string dt2Payload = JsonSerializer.Serialize(customDigitalTwin);
         ///
-        /// Response&lt;string&gt; createDt2Response = await DigitalTwinsClient.CreateDigitalTwinAsync(dtId2, dt2Payload).ConfigureAwait(false);
-        /// Console.WriteLine($&quot;Created digital twin {dtId2} with response {createDt2Response.GetRawResponse().Status}.&quot;);
+        /// Response&lt;string&gt; createCustomDtResponse = await DigitalTwinsClient.CreateDigitalTwinAsync(customDtId, dt2Payload).ConfigureAwait(false);
+        /// Console.WriteLine($&quot;Created digital twin {customDtId} with response {createCustomDtResponse.GetRawResponse().Status}.&quot;);
         /// </code>
         /// </example>
         public virtual Task<Response<string>> CreateDigitalTwinAsync(string digitalTwinId, string digitalTwin, CancellationToken cancellationToken = default)
@@ -254,7 +268,7 @@ public virtual Response<string> UpdateDigitalTwin(string digitalTwinId, string d
         /// <returns>Json string representation of the component corresponding to the provided componentPath and the HTTP response.</returns>
         /// <example>
         /// <code snippet="Snippet:DigitalTwinsSampleGetComponent">
-        /// response = await DigitalTwinsClient.GetComponentAsync(dtId1, SamplesConstants.ComponentPath).ConfigureAwait(false);
+        /// response = await DigitalTwinsClient.GetComponentAsync(basicDtId, SamplesConstants.ComponentPath).ConfigureAwait(false);
         ///
         /// Console.WriteLine($&quot;Get component for digital twin: \n{response.Value}. Get response status: {response.GetRawResponse().Status}&quot;);
         /// </code>
@@ -292,9 +306,9 @@ public virtual Response<string> GetComponent(string digitalTwinId, string compon
         /// componentUpdateUtility.AppendReplaceOp(&quot;/ComponentProp1&quot;, &quot;Some new value&quot;);
         /// string updatePayload = componentUpdateUtility.Serialize();
         ///
-        /// Response&lt;string&gt; response = await DigitalTwinsClient.UpdateComponentAsync(dtId1, &quot;Component1&quot;, updatePayload);
+        /// Response&lt;string&gt; response = await DigitalTwinsClient.UpdateComponentAsync(basicDtId, &quot;Component1&quot;, updatePayload);
         ///
-        /// Console.WriteLine($&quot;Updated component for digital twin {dtId1}. Update response status: {response.GetRawResponse().Status}&quot;);
+        /// Console.WriteLine($&quot;Updated component for digital twin {basicDtId}. Update response status: {response.GetRawResponse().Status}&quot;);
         /// </code>
         /// </example>
         public virtual Task<Response<string>> UpdateComponentAsync(string digitalTwinId, string componentPath, string componentUpdateOperations, RequestOptions requestOptions = default, CancellationToken cancellationToken = default)
diff --git a/sdk/digitaltwins/Azure.DigitalTwins.Core/src/Serialization/BasicDigitalTwin.cs b/sdk/digitaltwins/Azure.DigitalTwins.Core/src/Serialization/BasicDigitalTwin.cs
index 9330f5dd7dea0..2fd792d82f849 100644
--- a/sdk/digitaltwins/Azure.DigitalTwins.Core/src/Serialization/BasicDigitalTwin.cs
+++ b/sdk/digitaltwins/Azure.DigitalTwins.Core/src/Serialization/BasicDigitalTwin.cs
@@ -9,12 +9,14 @@ namespace Azure.DigitalTwins.Core.Serialization
     /// An optional, helper class for deserializing a digital twin.
     /// </summary>
     /// <example>
+    /// Here's an example of  how to use the BasicDigitalTwin helper class to serialize and create a digital twin.
+    ///
     /// <code snippet="Snippet:DigitalTwinsSampleCreateBasicTwin">
-    /// // Create digital twin with Component payload using the BasicDigitalTwin serialization helper
+    /// // Create digital twin with component payload using the BasicDigitalTwin serialization helper
     ///
     /// var basicDigitalTwin = new BasicDigitalTwin
     /// {
-    ///     Id = dtId1
+    ///     Id = basicDtId
     /// };
     /// basicDigitalTwin.Metadata.ModelId = modelId;
     /// basicDigitalTwin.CustomProperties.Add(&quot;Prop1&quot;, &quot;Value1&quot;);
@@ -27,10 +29,28 @@ namespace Azure.DigitalTwins.Core.Serialization
     ///
     /// basicDigitalTwin.CustomProperties.Add(&quot;Component1&quot;, componentMetadata);
     ///
-    /// string dt1Payload = JsonSerializer.Serialize(basicDigitalTwin);
+    /// string basicDtPayload = JsonSerializer.Serialize(basicDigitalTwin);
+    ///
+    /// Response&lt;string&gt; createBasicDtResponse = await DigitalTwinsClient.CreateDigitalTwinAsync(basicDtId, basicDtPayload).ConfigureAwait(false);
+    /// Console.WriteLine($&quot;Created digital twin {basicDtId} with response {createBasicDtResponse.GetRawResponse().Status}.&quot;);
+    /// </code>
+    ///
+    /// Here's an example of  how to use the BasicDigitalTwin helper class to get and deserialize a digital twin.
+    ///
+    /// <code snippet="Snippet:DigitalTwinsSampleGetBasicDigitalTwin">
+    /// Response&lt;string&gt; getBasicDtResponse = await DigitalTwinsClient.GetDigitalTwinAsync(basicDtId).ConfigureAwait(false);
+    /// if (getBasicDtResponse.GetRawResponse().Status == (int)HttpStatusCode.OK)
+    /// {
+    ///     BasicDigitalTwin basicDt = JsonSerializer.Deserialize&lt;BasicDigitalTwin&gt;(getBasicDtResponse.Value);
+    ///
+    ///     // Must cast Component1 as a JsonElement and get its raw text in order to deserialize it as a dictionary
+    ///     string component1RawText = ((JsonElement)basicDt.CustomProperties[&quot;Component1&quot;]).GetRawText();
+    ///     var component1 = JsonSerializer.Deserialize&lt;IDictionary&lt;string, object&gt;&gt;(component1RawText);
     ///
-    /// Response&lt;string&gt; createDt1Response = await DigitalTwinsClient.CreateDigitalTwinAsync(dtId1, dt1Payload).ConfigureAwait(false);
-    /// Console.WriteLine($&quot;Created digital twin {dtId1} with response {createDt1Response.GetRawResponse().Status}.&quot;);
+    ///     Console.WriteLine($&quot;Retrieved and deserialized digital twin {basicDt.Id}  with ETag {basicDt.ETag} &quot; +
+    ///         $&quot;and Prop1 &apos;{basicDt.CustomProperties[&quot;Prop1&quot;]}&apos;, Prop2 &apos;{basicDt.CustomProperties[&quot;Prop2&quot;]}&apos;, &quot; +
+    ///         $&quot;ComponentProp1 &apos;{component1[&quot;ComponentProp1&quot;]}&apos;, ComponentProp2 &apos;{component1[&quot;ComponentProp2&quot;]}&apos;&quot;);
+    /// }
     /// </code>
     /// </example>
     public class BasicDigitalTwin : ModelProperties
@@ -40,5 +60,11 @@ public class BasicDigitalTwin : ModelProperties
         /// </summary>
         [JsonPropertyName("$dtId")]
         public string Id { get; set; }
+
+        /// <summary>
+        /// A string representing a weak ETag for the entity that this request performs an operation against, as per RFC7232.
+        /// </summary>
+        [JsonPropertyName("$etag")]
+        public string ETag { get; set; }
     }
 }
diff --git a/sdk/digitaltwins/Azure.DigitalTwins.Core/src/Serialization/UpdateOperationsUtility.cs b/sdk/digitaltwins/Azure.DigitalTwins.Core/src/Serialization/UpdateOperationsUtility.cs
index ad6b3c1287e13..421fae4f398d4 100644
--- a/sdk/digitaltwins/Azure.DigitalTwins.Core/src/Serialization/UpdateOperationsUtility.cs
+++ b/sdk/digitaltwins/Azure.DigitalTwins.Core/src/Serialization/UpdateOperationsUtility.cs
@@ -16,9 +16,9 @@ namespace Azure.DigitalTwins.Core.Serialization
     /// componentUpdateUtility.AppendReplaceOp(&quot;/ComponentProp1&quot;, &quot;Some new value&quot;);
     /// string updatePayload = componentUpdateUtility.Serialize();
     ///
-    /// Response&lt;string&gt; response = await DigitalTwinsClient.UpdateComponentAsync(dtId1, &quot;Component1&quot;, updatePayload);
+    /// Response&lt;string&gt; response = await DigitalTwinsClient.UpdateComponentAsync(basicDtId, &quot;Component1&quot;, updatePayload);
     ///
-    /// Console.WriteLine($&quot;Updated component for digital twin {dtId1}. Update response status: {response.GetRawResponse().Status}&quot;);
+    /// Console.WriteLine($&quot;Updated component for digital twin {basicDtId}. Update response status: {response.GetRawResponse().Status}&quot;);
     /// </code>
     /// </example>
     public class UpdateOperationsUtility