I would like to create a custom Metadata field $frq, similar to the $model field for my digital twins. I can create a CustomDigitalTwin class in C#, there is no error when trying to create the twin with the DigitalTwins.Core SDK. However, it doesn't show in Azure Digital Twins Explorer and I also cannot retrieve my custom metadata field FRQ with C#.
Here is my Code and my classes:
string twinId = "test-twin";
var initData = new TestDigitalTwin
{
Id = twinId,
Metadata =
{ModelId = "dtmi:com:dtwin:test;1",
FRQ = "60"},
};
await client.CreateOrReplaceDigitalTwinAsync<CustomDigitalTwin>(twinId, initData);
Response<CustomDigitalTwin> getCustomDtResponse = await client.GetDigitalTwinAsync<CustomDigitalTwin>(twinId);
CustomDigitalTwin customDt = getCustomDtResponse.Value;
Console.WriteLine(customDt.Metadata.FRQ); // FRQ is null here
Classes:
// Define a custom model type for the twin to be created
internal class TestDigitalTwin
{
[JsonPropertyName(DigitalTwinsJsonPropertyNames.DigitalTwinId)]
public string Id { get; set; }
[JsonPropertyName(DigitalTwinsJsonPropertyNames.DigitalTwinETag)]
public string ETag { get; set; }
[JsonPropertyName(DigitalTwinsJsonPropertyNames.DigitalTwinMetadata)]
public MyCustomDigitalTwinMetadata Metadata { get; set; } = new MyCustomDigitalTwinMetadata();
}
internal class MyCustomDigitalTwinMetadata
{
[JsonPropertyName(DigitalTwinsJsonPropertyNames.MetadataModel)]
public string ModelId { get; set; }
[JsonPropertyName(DigitalTwinsJsonPropertyNames.MetadataFRQ)]
public string FRQ { get; set; }
[JsonPropertyName("temperature")]
public DigitalTwinPropertyMetadata Temperature { get; set; }
[JsonPropertyName("humidity")]
public DigitalTwinPropertyMetadata Humidity { get; set; }
}
/// <summary>
/// String constants for use in JSON de/serialization for custom types.
/// </summary>
public static class DigitalTwinsJsonPropertyNames
{
/// <summary>
/// The JSON property name for the Id field on a digital twin.
/// </summary>
public const string DigitalTwinId = "$dtId";
/// <summary>
/// The JSON property name for the ETag field on a digital twin.
/// </summary>
public const string DigitalTwinETag = "$etag";
/// <summary>
/// The JSON property name for the metadata field on a digital twin or a component.
/// </summary>
public const string DigitalTwinMetadata = "$metadata";
/// <summary>
/// The JSON property name for the model field on a digital twin metadata.
/// </summary>
public const string MetadataModel = "$model";
/// <summary>
/// The JSON property name for the frequency field on a digital twin metadata.
/// </summary>
public const string MetadataFRQ = "$frq";
/// <summary>
/// The last update time of a digital twin property, used in the $metadata object
/// on a digital twin or component about their properties.
/// </summary>
public const string MetadataLastUpdateTime = "$lastUpdateTime";
/// <summary>
/// The last update time of a digital twin property, used in the $metadata object
/// on a digital twin or component about their properties.
/// </summary>
public const string MetadataPropertyLastUpdateTime = "lastUpdateTime";
/// <summary>
/// The time the value of a digital twin property was sourced, used in the $metadata
/// object on a digital twin or component about their properties.
/// </summary>
public const string MetadataPropertySourceTime = "sourceTime";
/// <summary>
/// The JSON property name for the Id field on a relationship.
/// </summary>
public const string RelationshipId = "$relationshipId";
/// <summary>
/// The JSON property name for the source Id field on a relationship.
/// </summary>
public const string RelationshipSourceId = "$sourceId";
/// <summary>
/// The JSON property name for the target Id field on a relationship.
/// </summary>
public const string RelationshipTargetId = "$targetId";
/// <summary>
/// The JSON property name for the name field on a relationship.
/// </summary>
public const string RelationshipName = "$relationshipName";
}
public class DigitalTwinPropertyMetadata
{
/// <summary>
/// The date and time the property was last updated.
/// </summary>
[JsonPropertyName(DigitalTwinsJsonPropertyNames.MetadataPropertyLastUpdateTime)]
public DateTimeOffset LastUpdatedOn { get; set; }
/// <summary>
/// The date and time the value of the property was sourced.
/// </summary>
[JsonPropertyName(DigitalTwinsJsonPropertyNames.MetadataPropertySourceTime)]
public DateTimeOffset? SourceTime { get; set; }
}
The API does not support adding custom metadata fields. You can update the model, or update the sourceTime
property in the $metadata
object, but adding a custom field is not supported. But the API is pretty inconsistent in letting the user know. For instance, when you create a new twin with the following body (I'm doing this against the REST API directly with Postman)
{
"$metadata": {
"$model": "dtmi:some:model;2",
"$stuff": "foo"
},
"someProperty" : "bar"
}
The result is 200 OK with the response:
{
"$dtId": "the-id",
"$etag": "W/\"908d9907-971e-4036-b269-d0595d6e04b7\"",
"someProperty": "bar",
"$metadata": {
"$model": "dtmi:some:model;2",
"someProperty": {
"lastUpdateTime": "2022-07-25T11:33:15.2115802Z"
}
}
}
You can see the custom property vanished, but the API says it's okay. However, when I try adding a custom property through a PATCH, this is what happens. Request:
[
{
"op": "add",
"path": "/$metadata/$foo",
"value": "stuff"
}
]
The result is 400 Bad Request
{
"error": {
"code": "JsonPatchInvalid",
"message": "Cannot patch path /$metadata/$stuff Please provide a valid patch document. See section on update apis in the documentation https://aka.ms/adtv2twins."
}
}