diff --git a/sdk/digitaltwins/Azure.DigitalTwins.Core/samples/DigitalTwinsClientSample/ComponentSamples.cs b/sdk/digitaltwins/Azure.DigitalTwins.Core/samples/DigitalTwinsClientSample/ComponentSamples.cs index 38d757db321e..bf7fe203a8df 100644 --- a/sdk/digitaltwins/Azure.DigitalTwins.Core/samples/DigitalTwinsClientSample/ComponentSamples.cs +++ b/sdk/digitaltwins/Azure.DigitalTwins.Core/samples/DigitalTwinsClientSample/ComponentSamples.cs @@ -32,7 +32,7 @@ public async Task RunSamplesAsync() string componentModelId = await GetUniqueModelIdAsync(SamplesConstants.TemporaryComponentModelPrefix, DigitalTwinsClient).ConfigureAwait(false); string modelId = await GetUniqueModelIdAsync(SamplesConstants.TemporaryModelPrefix, DigitalTwinsClient).ConfigureAwait(false); - string dtId = await GetUniqueTwinIdAsync(SamplesConstants.TemporaryTwinPrefix, DigitalTwinsClient).ConfigureAwait(false); + string dtId1 = await GetUniqueTwinIdAsync(SamplesConstants.TemporaryTwinPrefix, DigitalTwinsClient).ConfigureAwait(false); string newComponentModelPayload = SamplesConstants.TemporaryComponentModelPayload .Replace(SamplesConstants.ComponentId, componentModelId); @@ -51,7 +51,10 @@ public async Task RunSamplesAsync() // Create digital twin with Component payload using the BasicDigitalTwin serialization helper - var basicDigitalTwin = new BasicDigitalTwin(); + var basicDigitalTwin = new BasicDigitalTwin + { + Id = dtId1 + }; basicDigitalTwin.Metadata.ModelId = modelId; basicDigitalTwin.CustomProperties.Add("Prop1", "Value1"); basicDigitalTwin.CustomProperties.Add("Prop2", "Value2"); @@ -63,13 +66,36 @@ public async Task RunSamplesAsync() basicDigitalTwin.CustomProperties.Add("Component1", componentMetadata); - string dtPayload = JsonSerializer.Serialize(basicDigitalTwin, new JsonSerializerOptions { IgnoreNullValues = true }); + string dt1Payload = JsonSerializer.Serialize(basicDigitalTwin); - Response createDtResponse = await DigitalTwinsClient.CreateDigitalTwinAsync(dtId, dtPayload).ConfigureAwait(false); - Console.WriteLine($"Created digital twin {dtId} with response {createDtResponse.GetRawResponse().Status}."); + Response createDt1Response = await DigitalTwinsClient.CreateDigitalTwinAsync(dtId1, dt1Payload).ConfigureAwait(false); + Console.WriteLine($"Created digital twin {dtId1} with response {createDt1Response.GetRawResponse().Status}."); #endregion Snippet:DigitalTwinsSampleCreateBasicTwin + #region Snippet:DigitalTwinsSampleCreateCustomTwin + + string dtId2 = await GetUniqueTwinIdAsync(SamplesConstants.TemporaryTwinPrefix, DigitalTwinsClient).ConfigureAwait(false); + var customDigitalTwin = new CustomDigitalTwin + { + Id = dtId2, + 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 createDt2Response = await DigitalTwinsClient.CreateDigitalTwinAsync(dtId2, dt2Payload).ConfigureAwait(false); + Console.WriteLine($"Created digital twin {dtId2} with response {createDt2Response.GetRawResponse().Status}."); + + #endregion Snippet:DigitalTwinsSampleCreateCustomTwin + #region Snippet:DigitalTwinsSampleUpdateComponent // Update Component1 by replacing the property ComponentProp1 value @@ -77,9 +103,9 @@ public async Task RunSamplesAsync() componentUpdateUtility.AppendReplaceOp("/ComponentProp1", "Some new value"); string updatePayload = componentUpdateUtility.Serialize(); - Response response = await DigitalTwinsClient.UpdateComponentAsync(dtId, "Component1", updatePayload); + Response response = await DigitalTwinsClient.UpdateComponentAsync(dtId1, "Component1", updatePayload); - Console.WriteLine($"Updated component for digital twin {dtId}. Update response status: {response.GetRawResponse().Status}"); + Console.WriteLine($"Updated component for digital twin {dtId1}. Update response status: {response.GetRawResponse().Status}"); #endregion Snippet:DigitalTwinsSampleUpdateComponent @@ -87,16 +113,24 @@ public async Task RunSamplesAsync() #region Snippet:DigitalTwinsSampleGetComponent - response = await DigitalTwinsClient.GetComponentAsync(dtId, SamplesConstants.ComponentPath).ConfigureAwait(false); + response = await DigitalTwinsClient.GetComponentAsync(dtId1, SamplesConstants.ComponentPath).ConfigureAwait(false); Console.WriteLine($"Get component for digital twin: \n{response.Value}. Get response status: {response.GetRawResponse().Status}"); #endregion Snippet:DigitalTwinsSampleGetComponent - // Now delete a Twin - await DigitalTwinsClient.DeleteDigitalTwinAsync(dtId).ConfigureAwait(false); + // Clean up + + try + { + await DigitalTwinsClient.DeleteDigitalTwinAsync(dtId1).ConfigureAwait(false); + await DigitalTwinsClient.DeleteDigitalTwinAsync(dtId2).ConfigureAwait(false); + } + catch (RequestFailedException ex) + { + Console.WriteLine($"Failed to delete digital twin due to {ex}"); + } - // Delete models try { await DigitalTwinsClient.DeleteModelAsync(modelId).ConfigureAwait(false); diff --git a/sdk/digitaltwins/Azure.DigitalTwins.Core/samples/DigitalTwinsClientSample/CustomDigitalTwin.cs b/sdk/digitaltwins/Azure.DigitalTwins.Core/samples/DigitalTwinsClientSample/CustomDigitalTwin.cs new file mode 100644 index 000000000000..4a55f8c5e1d4 --- /dev/null +++ b/sdk/digitaltwins/Azure.DigitalTwins.Core/samples/DigitalTwinsClientSample/CustomDigitalTwin.cs @@ -0,0 +1,66 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Text.Json.Serialization; +using Azure.DigitalTwins.Core.Serialization; + +namespace Azure.DigitalTwins.Core.Samples +{ + /// + /// Custom type for a sample illustrating how someone can create their own class to match a digital twin model type + /// for serialization, instead of using . + /// + internal class CustomDigitalTwin + { + [JsonPropertyName("$dtId")] + public string Id { get; set; } + + [JsonPropertyName("$metadata")] + public CustomDigitalTwinMetadata Metadata { get; set; } + + [JsonPropertyName("Prop1")] + public string Prop1 { get; set; } + + [JsonPropertyName("Prop2")] + public string Prop2 { get; set; } + + [JsonPropertyName("Component1")] + public Component1 Component1 { get; set; } + } + + internal class Component1 + { + [JsonPropertyName("$metadata")] + public Component1Metadata Metadata { get; set; } + + [JsonPropertyName("ComponentProp1")] + public string ComponentProp1 { get; set; } + + [JsonPropertyName("ComponentProp2")] + public string ComponentProp2 { get; set; } + } + + internal class Metadata + { + [JsonPropertyName("$model")] + public string ModelId { get; set; } + } + + internal class CustomDigitalTwinMetadata : Metadata + { + [JsonPropertyName("Prop1")] + public WritableProperty Prop1 { get; set; } + + [JsonPropertyName("Prop2")] + public WritableProperty Prop2 { get; set; } + } + + internal class Component1Metadata : Metadata + { + [JsonPropertyName("ComponentProp1")] + public WritableProperty ComponentProp1 { get; set; } + + [JsonPropertyName("ComponentProp2")] + public WritableProperty ComponentProp2 { get; set; } + } +} diff --git a/sdk/digitaltwins/Azure.DigitalTwins.Core/samples/Readme.md b/sdk/digitaltwins/Azure.DigitalTwins.Core/samples/Readme.md index 8f45ecb97e5e..2c68cd1672bd 100644 --- a/sdk/digitaltwins/Azure.DigitalTwins.Core/samples/Readme.md +++ b/sdk/digitaltwins/Azure.DigitalTwins.Core/samples/Readme.md @@ -121,10 +121,16 @@ catch (Exception ex) 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"). +One option is to use the provided class BasicDigitalTwin for serialization and deserialization. +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 -var basicDigitalTwin = new BasicDigitalTwin(); +var basicDigitalTwin = new BasicDigitalTwin +{ + Id = dtId1 +}; basicDigitalTwin.Metadata.ModelId = modelId; basicDigitalTwin.CustomProperties.Add("Prop1", "Value1"); basicDigitalTwin.CustomProperties.Add("Prop2", "Value2"); @@ -136,10 +142,34 @@ componentMetadata.CustomProperties.Add("ComponentProp2", "ComponentValue2"); basicDigitalTwin.CustomProperties.Add("Component1", componentMetadata); -string dtPayload = JsonSerializer.Serialize(basicDigitalTwin, new JsonSerializerOptions { IgnoreNullValues = true }); +string dt1Payload = JsonSerializer.Serialize(basicDigitalTwin); + +Response createDt1Response = await DigitalTwinsClient.CreateDigitalTwinAsync(dtId1, dt1Payload).ConfigureAwait(false); +Console.WriteLine($"Created digital twin {dtId1} with response {createDt1Response.GetRawResponse().Status}."); +``` + +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. + +```C# Snippet:DigitalTwinsSampleCreateCustomTwin +string dtId2 = await GetUniqueTwinIdAsync(SamplesConstants.TemporaryTwinPrefix, DigitalTwinsClient).ConfigureAwait(false); +var customDigitalTwin = new CustomDigitalTwin +{ + Id = dtId2, + 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 createDtResponse = await DigitalTwinsClient.CreateDigitalTwinAsync(dtId, dtPayload).ConfigureAwait(false); -Console.WriteLine($"Created digital twin {dtId} with response {createDtResponse.GetRawResponse().Status}."); +Response createDt2Response = await DigitalTwinsClient.CreateDigitalTwinAsync(dtId2, dt2Payload).ConfigureAwait(false); +Console.WriteLine($"Created digital twin {dtId2} with response {createDt2Response.GetRawResponse().Status}."); ``` ### Query Digital Twin @@ -212,9 +242,9 @@ var componentUpdateUtility = new UpdateOperationsUtility(); componentUpdateUtility.AppendReplaceOp("/ComponentProp1", "Some new value"); string updatePayload = componentUpdateUtility.Serialize(); -Response response = await DigitalTwinsClient.UpdateComponentAsync(dtId, "Component1", updatePayload); +Response response = await DigitalTwinsClient.UpdateComponentAsync(dtId1, "Component1", updatePayload); -Console.WriteLine($"Updated component for digital twin {dtId}. Update response status: {response.GetRawResponse().Status}"); +Console.WriteLine($"Updated component for digital twin {dtId1}. Update response status: {response.GetRawResponse().Status}"); ``` ### Get Digital Twin Component @@ -222,7 +252,7 @@ Console.WriteLine($"Updated component for digital twin {dtId}. Update response s Get a component by providing name of a component and id of digital twin it belongs to. ```C# Snippet:DigitalTwinsSampleGetComponent -response = await DigitalTwinsClient.GetComponentAsync(dtId, SamplesConstants.ComponentPath).ConfigureAwait(false); +response = await DigitalTwinsClient.GetComponentAsync(dtId1, SamplesConstants.ComponentPath).ConfigureAwait(false); Console.WriteLine($"Get component for digital twin: \n{response.Value}. Get response status: {response.GetRawResponse().Status}"); ``` diff --git a/sdk/digitaltwins/Azure.DigitalTwins.Core/src/DigitalTwinsClient.cs b/sdk/digitaltwins/Azure.DigitalTwins.Core/src/DigitalTwinsClient.cs index 95377339bab7..5d31f6589db7 100644 --- a/sdk/digitaltwins/Azure.DigitalTwins.Core/src/DigitalTwinsClient.cs +++ b/sdk/digitaltwins/Azure.DigitalTwins.Core/src/DigitalTwinsClient.cs @@ -144,25 +144,25 @@ public virtual Response GetDigitalTwin(string digitalTwinId, Cancellatio /// The created application/json digital twin and the http response. /// The digital twin must be the serialization of an instance of or the serialization of an extension of that type. /// - /// - /// // Create digital twin with Component payload using the BasicDigitalTwin serialization helper - /// - /// var basicDigitalTwin = new BasicDigitalTwin(); - /// basicDigitalTwin.Metadata.ModelId = modelId; - /// basicDigitalTwin.CustomProperties.Add("Prop1", "Value1"); - /// basicDigitalTwin.CustomProperties.Add("Prop2", "Value2"); - /// - /// var componentMetadata = new ModelProperties(); - /// componentMetadata.Metadata.ModelId = componentModelId; - /// componentMetadata.CustomProperties.Add("ComponentProp1", "ComponentValue1"); - /// componentMetadata.CustomProperties.Add("ComponentProp2", "ComponentValue2"); - /// - /// basicDigitalTwin.CustomProperties.Add("Component1", componentMetadata); - /// - /// string dtPayload = JsonSerializer.Serialize(basicDigitalTwin, new JsonSerializerOptions { IgnoreNullValues = true }); + /// + /// string dtId2 = await GetUniqueTwinIdAsync(SamplesConstants.TemporaryTwinPrefix, DigitalTwinsClient).ConfigureAwait(false); + /// var customDigitalTwin = new CustomDigitalTwin + /// { + /// Id = dtId2, + /// 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> createDtResponse = await DigitalTwinsClient.CreateDigitalTwinAsync(dtId, dtPayload).ConfigureAwait(false); - /// Console.WriteLine($"Created digital twin {dtId} with response {createDtResponse.GetRawResponse().Status}."); + /// Response<string> createDt2Response = await DigitalTwinsClient.CreateDigitalTwinAsync(dtId2, dt2Payload).ConfigureAwait(false); + /// Console.WriteLine($"Created digital twin {dtId2} with response {createDt2Response.GetRawResponse().Status}."); /// /// public virtual Task> CreateDigitalTwinAsync(string digitalTwinId, string digitalTwin, CancellationToken cancellationToken = default) @@ -254,7 +254,7 @@ public virtual Response UpdateDigitalTwin(string digitalTwinId, string d /// Json string representation of the component corresponding to the provided componentPath and the HTTP response. /// /// - /// response = await DigitalTwinsClient.GetComponentAsync(dtId, SamplesConstants.ComponentPath).ConfigureAwait(false); + /// response = await DigitalTwinsClient.GetComponentAsync(dtId1, SamplesConstants.ComponentPath).ConfigureAwait(false); /// /// Console.WriteLine($"Get component for digital twin: \n{response.Value}. Get response status: {response.GetRawResponse().Status}"); /// @@ -292,9 +292,9 @@ public virtual Response GetComponent(string digitalTwinId, string compon /// componentUpdateUtility.AppendReplaceOp("/ComponentProp1", "Some new value"); /// string updatePayload = componentUpdateUtility.Serialize(); /// - /// Response<string> response = await DigitalTwinsClient.UpdateComponentAsync(dtId, "Component1", updatePayload); + /// Response<string> response = await DigitalTwinsClient.UpdateComponentAsync(dtId1, "Component1", updatePayload); /// - /// Console.WriteLine($"Updated component for digital twin {dtId}. Update response status: {response.GetRawResponse().Status}"); + /// Console.WriteLine($"Updated component for digital twin {dtId1}. Update response status: {response.GetRawResponse().Status}"); /// /// public virtual Task> 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 882205d0798a..9330f5dd7dea 100644 --- a/sdk/digitaltwins/Azure.DigitalTwins.Core/src/Serialization/BasicDigitalTwin.cs +++ b/sdk/digitaltwins/Azure.DigitalTwins.Core/src/Serialization/BasicDigitalTwin.cs @@ -12,7 +12,10 @@ namespace Azure.DigitalTwins.Core.Serialization /// /// // Create digital twin with Component payload using the BasicDigitalTwin serialization helper /// - /// var basicDigitalTwin = new BasicDigitalTwin(); + /// var basicDigitalTwin = new BasicDigitalTwin + /// { + /// Id = dtId1 + /// }; /// basicDigitalTwin.Metadata.ModelId = modelId; /// basicDigitalTwin.CustomProperties.Add("Prop1", "Value1"); /// basicDigitalTwin.CustomProperties.Add("Prop2", "Value2"); @@ -24,10 +27,10 @@ namespace Azure.DigitalTwins.Core.Serialization /// /// basicDigitalTwin.CustomProperties.Add("Component1", componentMetadata); /// - /// string dtPayload = JsonSerializer.Serialize(basicDigitalTwin, new JsonSerializerOptions { IgnoreNullValues = true }); + /// string dt1Payload = JsonSerializer.Serialize(basicDigitalTwin); /// - /// Response<string> createDtResponse = await DigitalTwinsClient.CreateDigitalTwinAsync(dtId, dtPayload).ConfigureAwait(false); - /// Console.WriteLine($"Created digital twin {dtId} with response {createDtResponse.GetRawResponse().Status}."); + /// Response<string> createDt1Response = await DigitalTwinsClient.CreateDigitalTwinAsync(dtId1, dt1Payload).ConfigureAwait(false); + /// Console.WriteLine($"Created digital twin {dtId1} with response {createDt1Response.GetRawResponse().Status}."); /// /// public class BasicDigitalTwin : ModelProperties diff --git a/sdk/digitaltwins/Azure.DigitalTwins.Core/src/Serialization/DigitalTwinMetadata.cs b/sdk/digitaltwins/Azure.DigitalTwins.Core/src/Serialization/DigitalTwinMetadata.cs index bf2bbdbc31e2..a63fdb81f0c4 100644 --- a/sdk/digitaltwins/Azure.DigitalTwins.Core/src/Serialization/DigitalTwinMetadata.cs +++ b/sdk/digitaltwins/Azure.DigitalTwins.Core/src/Serialization/DigitalTwinMetadata.cs @@ -8,18 +8,18 @@ namespace Azure.DigitalTwins.Core.Serialization { /// /// An optional, helper class for deserializing a digital twin. - /// The $metadata class on a . + /// The $metadata class on a and . /// public class DigitalTwinMetadata { /// - /// The Id of the model that this digital twin is modeled by. + /// The Id of the model that the digital twin or component is modeled by. /// [JsonPropertyName("$model")] public string ModelId { get; set; } /// - /// Additional, model-defined properties. + /// Model-defined writable properties' request state. /// /// For your convenience, the value of each dictionary object can be turned into an instance of . [JsonExtensionData] diff --git a/sdk/digitaltwins/Azure.DigitalTwins.Core/src/Serialization/UpdateOperationsUtility.cs b/sdk/digitaltwins/Azure.DigitalTwins.Core/src/Serialization/UpdateOperationsUtility.cs index a7215b956473..ad6b3c1287e1 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("/ComponentProp1", "Some new value"); /// string updatePayload = componentUpdateUtility.Serialize(); /// - /// Response<string> response = await DigitalTwinsClient.UpdateComponentAsync(dtId, "Component1", updatePayload); + /// Response<string> response = await DigitalTwinsClient.UpdateComponentAsync(dtId1, "Component1", updatePayload); /// - /// Console.WriteLine($"Updated component for digital twin {dtId}. Update response status: {response.GetRawResponse().Status}"); + /// Console.WriteLine($"Updated component for digital twin {dtId1}. Update response status: {response.GetRawResponse().Status}"); /// /// public class UpdateOperationsUtility