query-builder-for-digital-twins icon indicating copy to clipboard operation
query-builder-for-digital-twins copied to clipboard

[Bug]: generated queries cannot be used for strongly typed classes, since de-serialization does not work

Open mahdighorbanpourptw opened this issue 1 year ago • 4 comments

Describe the bug

qurying the twins with custom strongly typed classes does not work

Which version of the tool was used?

1.4.0

Provide the full query used. i.e. QueryBuilder.From<Space>()

QueryBuilder.From<City>();

Steps To Reproduce

Hi, I am trying to query the digital twins and deserialize the response to the custom c# classes. For that purpose, I took the simplest model from the repository "City" which has no relation and just two properties (name and code). I uploaded the provided model in the repo for city on my ADT instance. Then created few twins using the digital twins explorer UI. afterward created a new unit test that queries these twins like below

var credOptions = new DefaultAzureCredentialOptions()
{
    ExcludeSharedTokenCacheCredential = true,
    ExcludeVisualStudioCredential = true,
    InteractiveBrowserTenantId = "xxxxxxxxxxxxxxx"
};
var credential = new DefaultAzureCredential(credOptions);
var client = new DigitalTwinsClient(new System.Uri("https://xxxxxxxxx.digitaltwins.azure.net"), credential);

var query = Microsoft.DigitalWorkplace.DigitalTwins.QueryBuilder.QueryBuilder
   .From<City>();

var stringQuery = query.BuildAdtQuery();
var qresult = client.QueryAsync<City>(stringQuery);
var reslist = new List<City>();
await foreach (var twin in qresult)
{
    reslist.Add(twin);
}

The code successfully retrieves the twins. However, not correctly deserialized. All the custom properties are packed as a object Value inside the contents array. This is not the desired behavior. I would like to get the values inside the city object properties. Should I do it differently? Thank you

adt-queryBuilder-deserialize issue

mahdighorbanpourptw avatar Sep 11 '24 10:09 mahdighorbanpourptw

Could you share a gist or repo that exhibits this behavior?

I know of quite a few things that would break if this behavior did not work. My suspicion is that something is incorrect with your City class (like not extending BasicDigitalTwin or missing JsonPropertyName).

jarz avatar Sep 11 '24 16:09 jarz

Could you share a gist or repo that exhibits this behavior?

I know of quite a few things that would break if this behavior did not work. My suspicion is that something is incorrect with your City class (like not extending BasicDigitalTwin or missing JsonPropertyName).

I cloned this repo and made my changes. I used the city class and DTDL model from this repo with no changes. Just a new test class in the QueryBuilder.Test project with the provided code above. Also you need to add the "Azure.Identity" package for the authentication with ADT instance. Here is the complete code for that test class.

using Azure.DigitalTwins.Core;
using Azure.Identity;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using QueryBuilder.Test.Generated;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace QueryBuilder.Test;

[TestClass]
public class QueryRunTests
{
    [TestMethod]
    public async Task Test_City_Query()
    {
        var credOptions = new DefaultAzureCredentialOptions()
        {
            ExcludeSharedTokenCacheCredential = true,
            ExcludeVisualStudioCredential = true,
            InteractiveBrowserTenantId = "xxxxxxxxxxxxxxxxx"
        };
        var credential = new DefaultAzureCredential(credOptions);
        var client = new DigitalTwinsClient(new System.Uri("https://xxxxxxxxxxx.api.weu.digitaltwins.azure.net"), credential);

        var query = Microsoft.DigitalWorkplace.DigitalTwins.QueryBuilder.QueryBuilder
           .From<City>();

        var stringQuery = query.BuildAdtQuery();
        var qresult = client.QueryAsync<City>(stringQuery);
        var reslist = new List<City>();
        await foreach (var twin in qresult)
        {
            reslist.Add(twin);
        }
    }
}

and this is the city class from QueryBuilder.Test.Generated project

// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

namespace QueryBuilder.Test.Generated
{
    using Azure;
    using Azure.DigitalTwins.Core;
    using System;
    using System.Collections.Generic;
    using System.Runtime.Serialization;
    using System.Text.Json.Serialization;

    public class City : BasicDigitalTwin, IEquatable<City>, IEquatable<BasicDigitalTwin>
    {
        public City()
        {
            Metadata.ModelId = ModelId;
        }
        [JsonIgnore]
        public static string ModelId { get; } = "dtmi:test:City;1";
        [JsonPropertyName("name")]
        public string? Name { get; set; }
        [JsonPropertyName("code")]
        public string? Code { get; set; }
        public override bool Equals(object? obj)
        {
            return Equals(obj as City);
        }

        public bool Equals(City? other)
        {
            return other is not null && Id == other.Id && Metadata.ModelId == other.Metadata.ModelId && Name == other.Name && Code == other.Code;
        }

        public static bool operator ==(City? left, City? right)
        {
            return EqualityComparer<City?>.Default.Equals(left, right);
        }

        public static bool operator !=(City? left, City? right)
        {
            return !(left == right);
        }

        public override int GetHashCode()
        {
            return this.CustomHash(Id?.GetHashCode(), Metadata?.ModelId?.GetHashCode(), Name?.GetHashCode(), Code?.GetHashCode());
        }

        public bool Equals(BasicDigitalTwin? other)
        {
            return Equals(other as City) || new TwinEqualityComparer().Equals(this, other);
        }
    }
}

mahdighorbanpourptw avatar Sep 12 '24 04:09 mahdighorbanpourptw

This repo’s code focuses only on generating ADT queries. That functionality appears to be working.

However, I see the same behavior you describe. That indicates to me either a problem in the dtdl-model-generator with generating a C# class or in the Azure.DigitalTwins.Core SDK with deserializing the JSON into that class.

I don’t work with ADT anymore, so my time for troubleshooting is limited.

jarz avatar Sep 21 '24 18:09 jarz

This repo’s code focuses only on generating ADT queries. That functionality appears to be working.

However, I see the same behavior you describe. That indicates to me either a problem in the dtdl-model-generator with generating a C# class or in the Azure.DigitalTwins.Core SDK with deserializing the JSON into that class.

I don’t work with ADT anymore, so my time for troubleshooting is limited.

Thanks for confirming the behavior. Unfortunately, without the correct deserialization, making queries is not useful. I hope ADT team could make some time to resolve the issue. Could you please add someone who is actively working on ADT to this bug report?

mahdighorbanpourptw avatar Sep 24 '24 13:09 mahdighorbanpourptw