Share via


Gérer un graphique de jumeaux numériques à l’aide de relations

Azure Digital Twins consiste en un graphique de jumeaux représentant l’ensemble de votre environnement. Le graphe de jumeaux est constitué de jumeaux numériques individuels connectés via des relations. Cet article se concentre sur la gestion globale des relations et du graphe. Pour utiliser des jumeaux numériques individuels, consultez Gérer des jumeaux numériques.

Une fois que vous disposez d’une instance Azure Digital Twins opérationnelle et que vous avez configuré un code d’authentification dans votre application cliente, vous pouvez créer, modifier et supprimer des jumeaux numériques et leurs relations dans une instance Azure Digital Twins.

Prérequis

Pour utiliser Azure Digital Twins dans cet article, vous avez besoin d’une instance Azure Digital Twins et des autorisations requises pour l’utiliser. Si vous disposez déjà d’une instance Azure Digital Twins configurée, vous pouvez utiliser cette instance et passer à la section suivante. Dans le cas contraire, suivez les instructions indiquées dans Configurer une instance et l’authentification. Les instructions contiennent des informations qui vous aideront à vérifier que vous avez correctement effectué chaque étape.

Une fois l’instance configurée, notez son nom d’hôte. Vous trouverez le nom d’hôte dans le portail Azure.

Interfaces développeur

Cet article montre comment effectuer différentes opérations de gestion à l’aide du kit SDK .NET (C#). Vous pouvez également créer les mêmes appels de gestion à l’aide des autres kits de langage SDK décrits dans les API Azure Digital Twins et kits SDK.

Les autres interfaces développeur qui peuvent être utilisées pour effectuer ces opérations sont les suivantes :

Visualisation

Azure Digital Twins Explorer est un outil visuel permettant d’explorer les données dans votre graphique Azure Digital Twins. Vous pouvez utiliser l’explorateur pour afficher, interroger et modifier vos modèles, vos jumeaux et vos relations.

Pour en savoir plus sur l’outil Azure Digital Twins Explorer, consultez Azure Digital Twins Explorer. Pour obtenir des instructions détaillées sur l’utilisation de ses fonctionnalités, consultez Utilisation d’Azure Digital Twins Explorer.

Voici à quoi ressemble la visualisation :

Screenshot of Azure Digital Twins Explorer showing sample models and twins.

Créer des relations

Les relations décrivent les interconnexions entre différents jumeaux numériques, ce qui constitue la base du graphique de jumeaux.

Les types de relations qui peuvent être créés entre un jumeau (source) et un autre (cible) sont définis dans le modèle DTDL du jumeau source. Vous pouvez créer une instance d’une relation à l’aide de l’appel du SDK CreateOrReplaceRelationshipAsync() avec des jumeaux et des détails de relation qui respectent à la définition DTDL.

Pour créer une relation, vous devez spécifier :

  • ID de jumeau source (srcId dans l’exemple de code ci-dessous) : ID du jumeau d’origine de la relation.
  • ID de jumeau cible (targetId dans l’exemple de code ci-dessous) : ID du jumeau où la relation arrive.
  • Un nom de relation (relName dans l’exemple de code ci-dessous) : le type générique de la relation, tel que contains.
  • Un ID de relation (relId dans l’exemple de code ci-dessous) : le nom spécifique de cette relation, tel que Relationship1.

L’ID de relation doit être unique au sein du jumeau source donné. Il ne doit pas être globalement unique. Par exemple, pour le jumeau Foo, chaque ID de relation spécifique doit être unique. Toutefois, un autre jumeau Bar peut avoir une relation sortante qui correspond au même ID que celui d’une relation Foo.

L’exemple de code suivant illustre la procédure de création d’une relation dans votre instance Azure Digital Twins. Il utilise l’appel du kit SDK (en surbrillance) dans une méthode personnalisée pouvant apparaître au sein d’un programme plus volumineux.

private async static Task CustomMethod_CreateRelationshipAsync(DigitalTwinsClient client, string srcId, string targetId, string relName, IDictionary<string,object> inputProperties)
{
    var relationship = new BasicRelationship
    {
        TargetId = targetId,
        Name = relName,
        Properties = inputProperties
    };

    try
    {
        string relId = $"{srcId}-{relName}->{targetId}";
        await client.CreateOrReplaceRelationshipAsync<BasicRelationship>(srcId, relId, relationship);
        Console.WriteLine($"Created {relName} relationship successfully. Relationship ID is {relId}.");
    }
    catch (RequestFailedException rex)
    {
        Console.WriteLine($"Create relationship error: {rex.Status}:{rex.Message}");
    }

}

Cette fonction personnalisée peut maintenant être appelée pour créer une relation contains de cette manière :

await CustomMethod_CreateRelationshipAsync(client, srcId, targetId, "contains", properties);

Si vous souhaitez créer plusieurs relations, vous pouvez répéter des appels à la même méthode, en passant différents types de relations dans l’argument.

Pour plus d’informations sur la classe d’assistance BasicRelationship, consultez API et SDK Azure Digital Twins.

Créer plusieurs relations entre jumeaux

Les relations peuvent être classées comme suit :

  • Relations sortantes : relations appartenant à ce jumeau qui pointent vers l’extérieur pour le connecter à d’autres jumeaux. La méthode GetRelationshipsAsync() est utilisée pour obtenir les relations sortantes d’un jumeau.
  • Relations entrantes : relations appartenant à d’autres jumeaux qui pointent vers ce jumeau pour créer un lien « entrant ». La méthode GetIncomingRelationshipsAsync() est utilisée pour obtenir les relations entrantes d’un jumeau.

Il n’existe aucune restriction du nombre de relations entre deux jumeaux : vous pouvez avoir autant de relations entre jumeaux que vous le souhaitez.

Cela signifie que vous pouvez exprimer simultanément différents types de relations entre deux jumeaux. Par exemple, le Jumeau A peut avoir une relation stockée et une relation fabriquée avec le Jumeau B.

Vous pouvez même créer plusieurs instances du même type de relation entre les deux mêmes jumeaux si vous le voulez. Dans cet exemple, le Jumeau A peut avoir deux relations stockées différentes avec le Jumeau B, à condition que les relations aient différents ID de relation.

Remarque

Les attributs DTDL de minMultiplicity et de maxMultiplicity pour les relations ne sont actuellement pas pris en charge dans Azure Digital Twins ; même s’ils sont définis dans un modèle, ils ne sont pas appliqués par le service. Pour plus d’informations, consultez les Notes DTDL spécifiques du service.

Créer des relations en bloc avec l’API Importer des travaux

Vous pouvez utiliser l’API Importer des travaux pour créer plusieurs relations à la fois dans un seul appel d’API. Cette méthode nécessite l’utilisation de Stockage Blob Azure, ainsi que des autorisations d’écriture dans votre instance Azure Digital Twins pour les relations et les travaux en bloc.

Conseil

L’API Importer des travaux permet également aux modèles et aux jumeaux d’être importés dans le même appel, de créer toutes les parties d’un graphique à la fois. Pour plus d’informations sur ce processus, consultez Charger des modèles, des jumeaux et des relations en bloc avec l’API Importer des travaux.

Pour importer des relations en bloc, vous devez structurer vos relations (et toutes les autres ressources incluses dans le travail d’importation en bloc) en tant que fichier NDJSON . La Relationships section vient après la Twins section, ce qui en fait la dernière section de données de graphe dans le fichier. Les relations définies dans le fichier peuvent référencer des jumeaux définis dans ce fichier ou déjà présents dans l’instance, et ils peuvent éventuellement inclure l’initialisation de toutes les propriétés que les relations ont.

Vous pouvez afficher un exemple de fichier d’importation et un exemple de projet pour créer ces fichiers dans l’introduction de l’API Importer des travaux.

Ensuite, le fichier doit être chargé dans un objet blob d’ajout dans Stockage Blob Azure. Pour obtenir des instructions sur la création d’un conteneur de stockage Azure, consultez Créer un conteneur. Ensuite, chargez le fichier à l’aide de votre méthode de chargement préférée (certaines options sont la commande AzCopy, Azure CLI ou le Portail Azure).

Une fois le fichier NDJSON chargé dans le conteneur, obtenez son URL dans le conteneur d’objets blob. Vous utiliserez cette valeur plus loin dans le corps de l’appel d’API d’importation en bloc.

Voici une capture d’écran montrant la valeur d’URL d’un fichier blob dans la Portail Azure :

Screenshot of the Azure portal showing the URL of a file in a storage container.

Ensuite, le fichier peut être utilisé dans un appel d’API Importer des travaux. Vous fournirez l’URL de stockage d’objets blob du fichier d’entrée, ainsi qu’une nouvelle URL de stockage d’objets blob pour indiquer où vous souhaitez stocker le journal de sortie lors de sa création par le service.

Lister les relations

Lister les propriétés d’une relation unique

Vous pouvez toujours désérialiser les données de relation vers le type de votre choix. Pour un accès de base à une relation, utilisez le type BasicRelationship. La classe d’assistance BasicRelationship vous donne également accès aux propriétés définies dans la relation, par le biais d’un IDictionary<string, object>. Pour lister les propriétés, vous pouvez utiliser ceci :

public async Task ListRelationshipProperties(DigitalTwinsClient client, string twinId, string relId, BasicDigitalTwin twin)
{

    var res = await client.GetRelationshipAsync<BasicRelationship>(twinId, relId);
    BasicRelationship rel = res.Value;
    Console.WriteLine($"Relationship Name: {rel.Name}");
    foreach (string prop in rel.Properties.Keys)
    {
        if (twin.Contents.TryGetValue(prop, out object value))
        {
            Console.WriteLine($"Property '{prop}': {value}");
        }
    }
}

Lister les relations sortantes d’un jumeau numérique

Pour accéder à la liste des relations sortantes pour un jumeau donné dans le graphe, vous pouvez utiliser la méthode GetRelationships() comme suit :

AsyncPageable<BasicRelationship> rels = client.GetRelationshipsAsync<BasicRelationship>(dtId);

Cette méthode retourne Azure.Pageable<T> ou Azure.AsyncPageable<T>, selon que vous utilisez la version synchrone ou asynchrone de l’appel.

Voici un exemple qui récupère une liste de relations. Il utilise l’appel du kit SDK (en surbrillance) dans une méthode personnalisée pouvant apparaître au sein d’un programme plus volumineux.

private static async Task<List<BasicRelationship>> CustomMethod_FindOutgoingRelationshipsAsync(DigitalTwinsClient client, string dtId)
{
    // Find the relationships for the twin
    
    try
    {
        // GetRelationshipsAsync will throw if an error occurs
        AsyncPageable<BasicRelationship> rels = client.GetRelationshipsAsync<BasicRelationship>(dtId);
        var results = new List<BasicRelationship>();
        await foreach (BasicRelationship rel in rels)
        {
            results.Add(rel);
            Console.WriteLine($"Found relationship: {rel.Id}");

            //Print its properties
            Console.WriteLine($"Relationship properties:");
            foreach(KeyValuePair<string, object> property in rel.Properties)
            {
                Console.WriteLine("{0} = {1}", property.Key, property.Value);
            }
        }

        return results;
    }
    catch (RequestFailedException ex)
    {
        Console.WriteLine($"*** Error {ex.Status}/{ex.ErrorCode} retrieving relationships for {dtId} due to {ex.Message}");
        return null;
    }
}

Vous pouvez à présent appeler cette méthode personnalisée pour voir les relations sortantes des jumeaux comme ceci :

await CustomMethod_FindOutgoingRelationshipsAsync(client, twin_Id);

Vous pouvez utiliser les relations récupérées pour accéder à d’autres jumeaux dans votre graphe en lisant le champ target de la relation retournée et en l’utilisant comme ID de votre prochain appel de GetDigitalTwin().

Lister les relations entrantes vers un jumeau numérique

Azure Digital Twins fournit également un appel d’API qui permet de rechercher toutes les relations entrantes vers un jumeau donné. Ce SDK s’avère souvent utile pour la navigation inverse ou lors de la suppression d’un jumeau.

Remarque

Les appels à IncomingRelationship ne retournent pas le corps complet de la relation. Pour plus d’informations sur la classe IncomingRelationship, consultez sa documentation de référence.

L’exemple de code de la section précédente s’est concentré sur la recherche des relations sortantes d’un jumeau. L’exemple suivant est structuré de la même façon, mais il recherche des relations entrantes. Cet exemple utilise également l’appel du kit SDK (en surbrillance) dans une méthode personnalisée pouvant apparaître au sein d’un programme plus volumineux.

private static async Task<List<IncomingRelationship>> CustomMethod_FindIncomingRelationshipsAsync(DigitalTwinsClient client, string dtId)
{
    // Find the relationships for the twin
    
    try
    {
        // GetRelationshipsAsync will throw an error if a problem occurs
        AsyncPageable<IncomingRelationship> incomingRels = client.GetIncomingRelationshipsAsync(dtId);

        var results = new List<IncomingRelationship>();
        await foreach (IncomingRelationship incomingRel in incomingRels)
        {
            results.Add(incomingRel);
            Console.WriteLine($"Found incoming relationship: {incomingRel.RelationshipId}");

            //Print its properties
            Response<BasicRelationship> relResponse = await client.GetRelationshipAsync<BasicRelationship>(incomingRel.SourceId, incomingRel.RelationshipId);
            BasicRelationship rel = relResponse.Value;
            Console.WriteLine($"Relationship properties:");
            foreach(KeyValuePair<string, object> property in rel.Properties)
            {
                Console.WriteLine("{0} = {1}", property.Key, property.Value);
            }
        }
        return results;
    }
    catch (RequestFailedException ex)
    {
        Console.WriteLine($"*** Error {ex.Status}/{ex.ErrorCode} retrieving incoming relationships for {dtId} due to {ex.Message}");
        return null;
    }
}

Vous pouvez à présent appeler cette méthode personnalisée pour voir les relations entrantes des jumeaux comme ceci :

await CustomMethod_FindIncomingRelationshipsAsync(client, twin_Id);

Répertorier toutes les propriétés de jumeau et les relations

En utilisant les méthodes ci-dessus pour répertorier les relations sortantes et entrantes en lien avec un jumeau, vous pouvez créer une méthode qui imprime des informations complètes sur le jumeau, notamment les propriétés et les deux types de ses relations. Voici un exemple de méthode personnalisée qui montre comment combiner les méthodes personnalisées ci-dessus à cette fin.

private static async Task CustomMethod_FetchAndPrintTwinAsync(string twin_Id, DigitalTwinsClient client)
{
    Response<BasicDigitalTwin> res = await client.GetDigitalTwinAsync<BasicDigitalTwin>(twin_Id);
    await CustomMethod_FindOutgoingRelationshipsAsync(client, twin_Id);
    await CustomMethod_FindIncomingRelationshipsAsync(client, twin_Id);

    return;
}

Vous pouvez à présent appeler cette fonction personnalisée comme ceci :

await CustomMethod_FetchAndPrintTwinAsync(srcId, client);

Mettre à jour les relations

Les relations sont mises à jour à l’aide de la méthode UpdateRelationship.

Remarque

Cette méthode permet de mettre à jour les propriétés d’une relation. Si vous êtes amené à changer le jumeau source ou le jumeau cible de la relation, vous devez supprimer la relation et en recréer une à l’aide des nouveaux jumeaux.

Les paramètres obligatoires pour l’appel client sont les suivants :

  • L’ID du jumeau source (le jumeau à l’origine de la relation).
  • L’ID de la relation à mettre à jour.
  • Un document JSON Patch contenant les propriétés et les nouvelles valeurs à mettre à jour.

Voici un exemple de code qui montre comment utiliser cette méthode. Cet exemple utilise l’appel du kit SDK (en surbrillance) dans une méthode personnalisée pouvant apparaître au sein d’un programme plus volumineux.

private async static Task CustomMethod_UpdateRelationshipAsync(DigitalTwinsClient client, string srcId, string relId, Azure.JsonPatchDocument updateDocument)
{

    try
    {
        await client.UpdateRelationshipAsync(srcId, relId, updateDocument);
        Console.WriteLine($"Successfully updated {relId}");
    }
    catch (RequestFailedException rex)
    {
        Console.WriteLine($"Update relationship error: {rex.Status}:{rex.Message}");
    }

}

Voici un exemple d’appel de cette méthode personnalisée, qui passe un document JSON Patch avec les informations nécessaires à la mise à jour d’une propriété.

var updatePropertyPatch = new JsonPatchDocument();
updatePropertyPatch.AppendAdd("/ownershipUser", "ownershipUser NEW value");
await CustomMethod_UpdateRelationshipAsync(client, srcId, $"{srcId}-contains->{targetId}", updatePropertyPatch);

Supprimer les relations

Le premier paramètre spécifie le jumeau source (le jumeau à l’origine de la relation). L’autre paramètre est l’ID de relation. Vous avez besoin de l’ID de jumeau et de l’ID de relation, car les ID de relation ne sont uniques que dans l’étendue d’un jumeau.

Voici un exemple de code qui montre comment utiliser cette méthode. Cet exemple utilise l’appel du kit SDK (en surbrillance) dans une méthode personnalisée pouvant apparaître au sein d’un programme plus volumineux.

private static async Task CustomMethod_DeleteRelationshipAsync(DigitalTwinsClient client, string srcId, string relId)
{
    try
    {
        Response response = await client.DeleteRelationshipAsync(srcId, relId);
        await CustomMethod_FetchAndPrintTwinAsync(srcId, client);
        Console.WriteLine("Deleted relationship successfully");
    }
    catch (RequestFailedException e)
    {
        Console.WriteLine($"Error {e.ErrorCode}");
    }
}

Vous pouvez à présent appeler cette méthode personnalisée pour supprimer une relation comme ceci :

await CustomMethod_DeleteRelationshipAsync(client, srcId, $"{srcId}-contains->{targetId}");

Remarque

Si vous souhaitez supprimer tous les modèles, jumeaux et relations dans une instance à la fois, utilisez l’API Supprimer les travaux.

Créer plusieurs éléments de graphe à la fois

Cette section décrit les stratégies de création d’un graphique avec plusieurs éléments en même temps, plutôt que d’utiliser des appels d’API individuels pour charger des modèles, des jumeaux et des relations pour les charger un par un.

Charger des modèles, des jumeaux et des relations en bloc avec l’API Importer des travaux

Vous pouvez utiliser l’API Importer des travaux pour charger plusieurs modèles, jumeaux et relations vers votre instance dans un seul appel d’API, créant ainsi le graphique en même temps. Cette méthode nécessite l’utilisation de Stockage Blob Azure, ainsi que des autorisations d’écriture dans votre instance Azure Digital Twins pour les éléments de graphe (modèles, jumeaux et relations) et les travaux en bloc.

Pour importer des ressources en bloc, commencez par créer un fichier NDJSON contenant les détails de vos ressources. Le fichier commence par une Header section, suivie des sections facultatives Models, Twinset Relationships. Vous n’avez pas besoin d’inclure les trois types de données de graphe dans le fichier, mais toutes les sections présentes doivent suivre cet ordre. Les jumeaux définis dans le fichier peuvent référencer des modèles définis dans ce fichier ou déjà présents dans l’instance, et ils peuvent éventuellement inclure l’initialisation des propriétés du jumeau. Les relations définies dans le fichier peuvent référencer des jumeaux définis dans ce fichier ou déjà présents dans l’instance, et ils peuvent éventuellement inclure l’initialisation des propriétés de relation.

Vous pouvez afficher un exemple de fichier d’importation et un exemple de projet pour créer ces fichiers dans l’introduction de l’API Importer des travaux.

Ensuite, le fichier doit être chargé dans un objet blob d’ajout dans Stockage Blob Azure. Pour obtenir des instructions sur la création d’un conteneur de stockage Azure, consultez Créer un conteneur. Ensuite, chargez le fichier à l’aide de votre méthode de chargement préférée (certaines options sont la commande AzCopy, Azure CLI ou le Portail Azure).

Une fois le fichier NDJSON chargé dans le conteneur, obtenez son URL dans le conteneur d’objets blob. Vous utiliserez cette valeur plus loin dans le corps de l’appel d’API d’importation en bloc.

Voici une capture d’écran montrant la valeur d’URL d’un fichier blob dans la Portail Azure :

Screenshot of the Azure portal showing the URL of a file in a storage container.

Ensuite, le fichier peut être utilisé dans un appel d’API Importer des travaux. Vous fournirez l’URL de stockage d’objets blob du fichier d’entrée, ainsi qu’une nouvelle URL de stockage d’objets blob pour indiquer où vous souhaitez stocker le journal de sortie lors de sa création par le service.

Importer un graphique avec Azure Digital Twins Explorer

Azure Digital Twins Explorer est un outil visuel permettant d’afficher et d’interagir avec votre graphe de jumeaux. Il contient une fonctionnalité permettant d’importer un fichier de graphique au format JSON ou Excel qui peut contenir plusieurs modèles, jumeaux et relations.

Pour plus d’informations sur l’utilisation de cette fonctionnalité, consultez Importer un graphique dans la documentation d’Azure Digital Twins Explorer.

Créer des jumeaux et des relations à partir d’un fichier CSV

Parfois, vous devrez peut-être créer des hiérarchies de jumeaux à partir de données stockées dans une autre base de données, ou dans une feuille de calcul ou un fichier CSV. Cette section explique comment lire des données à partir d’un fichier CSV et créer un graphe de jumeaux en dehors de celui-ci.

Examinez le tableau de données suivant, qui décrit un ensemble de jumeaux numériques et de relations. Les modèles référencés dans ce fichier doivent déjà exister dans l’instance Azure Digital Twins.

ID de modèle ID de jumeau (doit être unique) Nom de la relation ID de jumeau cible Données d’initialisation du jumeau
dtmi:example:Floor;1 Floor1 contains Room1
dtmi:example:Floor;1 Floor0 contains Room0
dtmi:example:Room;1 Room1 {"Température » : 80}
dtmi:example:Room;1 Room0 {"Température » : 70}

L’une des méthodes permettant d’obtenir ces données dans Azure Digital Twins consiste à convertir le tableau en fichier CSV. Une fois ce tableau converti, le code peut être écrit pour interpréter le fichier en commandes permettant de créer des jumeaux et des relations. L’exemple de code suivant illustre la lecture des données du fichier CSV et la création d’un graphe de jumeaux dans Azure Digital Twins.

Dans le code ci-dessous, le fichier CSV est appelé data.csv ; il contient un espace réservé représentant le nom d’hôte de votre instance Azure Digital Twins. L’exemple utilise aussi plusieurs packages que vous pouvez ajouter à votre projet pour faciliter ce processus.

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Azure;
using Azure.DigitalTwins.Core;
using Azure.Identity;

namespace creating_twin_graph_from_csv
{
    class Program
    {
        static async Task Main(string[] args)
        {
            var relationshipRecordList = new List<BasicRelationship>();
            var twinList = new List<BasicDigitalTwin>();
            List<List<string>> data = ReadData();
            DigitalTwinsClient client = CreateDtClient();

            // Interpret the CSV file data, by each row
            foreach (List<string> row in data)
            {
                string modelID = row.Count > 0 ? row[0].Trim() : null;
                string srcID = row.Count > 1 ? row[1].Trim() : null;
                string relName = row.Count > 2 ? row[2].Trim() : null;
                string targetID = row.Count > 3 ? row[3].Trim() : null;
                string initProperties = row.Count > 4 ? row[4].Trim() : null;
                Console.WriteLine($"ModelID: {modelID}, TwinID: {srcID}, RelName: {relName}, TargetID: {targetID}, InitData: {initProperties}");
                var props = new Dictionary<string, object>();
                // Parse properties into dictionary (left out for compactness)
                // ...

                // Null check for source and target IDs
                if (!string.IsNullOrWhiteSpace(srcID) && !string.IsNullOrWhiteSpace(targetID) && !string.IsNullOrWhiteSpace(relName))
                {
                    relationshipRecordList.Add(
                        new BasicRelationship
                        {
                            SourceId = srcID,
                            TargetId = targetID,
                            Name = relName,
                        });
                }

                if (!string.IsNullOrWhiteSpace(srcID) && !string.IsNullOrWhiteSpace(modelID))
                twinList.Add(
                    new BasicDigitalTwin
                    {
                        Id = srcID,
                        Metadata = { ModelId = modelID },
                        Contents = props,
                    });
            }

            // Create digital twins
            foreach (BasicDigitalTwin twin in twinList)
            {
                try
                {
                    await client.CreateOrReplaceDigitalTwinAsync<BasicDigitalTwin>(twin.Id, twin);
                    Console.WriteLine("Twin is created");
                }
                catch (RequestFailedException ex)
                {
                    Console.WriteLine($"Error {ex.Status}: {ex.Message}");
                }
            }

            // Create relationships between the twins
            foreach (BasicRelationship rec in relationshipRecordList)
            {
                string relId = $"{rec.SourceId}-{rec.Name}->{rec.TargetId}";
                try
                {
                    await client.CreateOrReplaceRelationshipAsync<BasicRelationship>(rec.SourceId, relId, rec);
                    Console.WriteLine($"Relationship {relId} is created");
                }
                catch (RequestFailedException ex)
                {
                    Console.WriteLine($"Error creating relationship {relId}. {ex.Status}: {ex.Message}");
                }
            }
        }

        // Method to ingest data from the CSV file
        public static List<List<string>> ReadData()
        {
            string path = "<path-to>/data.csv";
            string[] lines = System.IO.File.ReadAllLines(path);
            var data = new List<List<string>>();
            int count = 0;
            foreach (string line in lines)
            {
                if (count++ == 0)
                    continue;
                var cols = new List<string>();
                string[] columns = line.Split(',');
                foreach (string column in columns)
                {
                    cols.Add(column);
                }
                data.Add(cols);
            }
            return data;
        }

        // Method to create the digital twins client
        private static DigitalTwinsClient CreateDtClient()
        {
            string adtInstanceUrl = "https://<your-instance-hostname>";
            var credentials = new DefaultAzureCredential();
            return new DigitalTwinsClient(new Uri(adtInstanceUrl), credentials);
        }
    }
}

Exemple de graphe de jumeaux exécutable

L’extrait de code exécutable suivant utilise les opérations de relation de cet article pour créer un graphe de jumeaux en dehors des relations et des jumeaux.

Configurer des exemples de fichiers de projet

L’extrait de code utilise deux exemples de définitions de modèle, Room.json et Floor.json. Pour télécharger les fichiers de modèle et les utiliser dans votre code, utilisez les liens suivants afin d’accéder directement aux fichiers dans GitHub. Cliquez avec le bouton droit n’importe où sur l’écran, sélectionnez Enregistrer sous dans le menu contextuel du navigateur, puis utilisez la fenêtre Enregistrer sous pour enregistrer les fichiers sous les noms Room.json et Floor.json.

Ensuite, créez un projet d’application console dans Visual Studio ou l’éditeur de votre choix.

Puis, copiez le code suivant de l’exemple exécutable dans votre projet :

using System;
using System.Threading.Tasks;
using System.IO;
using System.Collections.Generic;
using Azure;
using Azure.DigitalTwins.Core;
using Azure.Identity;

namespace DigitalTwins_Samples
{
    public class GraphOperationsSample
    {
        public static async Task Main(string[] args)
        {
            Console.WriteLine("Hello World!");

            // Create the Azure Digital Twins client for API calls
            DigitalTwinsClient client = createDtClient();
            Console.WriteLine($"Service client created – ready to go");
            Console.WriteLine();

            // Upload models
            Console.WriteLine($"Upload models");
            Console.WriteLine();
            string dtdl = File.ReadAllText("<path-to>/Room.json");
            string dtdl1 = File.ReadAllText("<path-to>/Floor.json");
            var models = new List<string>
            {
                dtdl,
                dtdl1,
            };
            // Upload the models to the service
            await client.CreateModelsAsync(models);

            // Create new (Floor) digital twin
            var floorTwin = new BasicDigitalTwin();
            string srcId = "myFloorID";
            floorTwin.Metadata.ModelId = "dtmi:example:Floor;1";
            // Floor twins have no properties, so nothing to initialize
            // Create the twin
            await client.CreateOrReplaceDigitalTwinAsync<BasicDigitalTwin>(srcId, floorTwin);
            Console.WriteLine("Twin created successfully");

            // Create second (Room) digital twin
            var roomTwin = new BasicDigitalTwin();
            string targetId = "myRoomID";
            roomTwin.Metadata.ModelId = "dtmi:example:Room;1";
            // Initialize properties
            roomTwin.Contents.Add("Temperature", 35.0);
            roomTwin.Contents.Add("Humidity", 55.0);
            // Create the twin
            await client.CreateOrReplaceDigitalTwinAsync<BasicDigitalTwin>(targetId, roomTwin);
            
            // Create relationship between them
            var properties = new Dictionary<string, object>
            {
                { "ownershipUser", "ownershipUser original value" },
            };
            // <UseCreateRelationship>
            await CustomMethod_CreateRelationshipAsync(client, srcId, targetId, "contains", properties);
            // </UseCreateRelationship>
            Console.WriteLine();

            // Update relationship's Name property
            // <UseUpdateRelationship>
            var updatePropertyPatch = new JsonPatchDocument();
            updatePropertyPatch.AppendAdd("/ownershipUser", "ownershipUser NEW value");
            await CustomMethod_UpdateRelationshipAsync(client, srcId, $"{srcId}-contains->{targetId}", updatePropertyPatch);
            // </UseUpdateRelationship>
            Console.WriteLine();

            //Print twins and their relationships
            Console.WriteLine("--- Printing details:");
            Console.WriteLine($"Outgoing relationships from source twin, {srcId}:");
            // <UseFetchAndPrint>
            await CustomMethod_FetchAndPrintTwinAsync(srcId, client);
            // </UseFetchAndPrint>
            Console.WriteLine();
            Console.WriteLine($"Incoming relationships to target twin, {targetId}:");
            await CustomMethod_FetchAndPrintTwinAsync(targetId, client);
            Console.WriteLine("--------");
            Console.WriteLine();

            // Delete the relationship
            // <UseDeleteRelationship>
            await CustomMethod_DeleteRelationshipAsync(client, srcId, $"{srcId}-contains->{targetId}");
            // </UseDeleteRelationship>
            Console.WriteLine();

            // Print twins and their relationships again
            Console.WriteLine("--- Printing details (after relationship deletion):");
            Console.WriteLine("Outgoing relationships from source twin:");
            await CustomMethod_FetchAndPrintTwinAsync(srcId, client);
            Console.WriteLine();
            Console.WriteLine("Incoming relationships to target twin:");
            await CustomMethod_FetchAndPrintTwinAsync(targetId, client);
            Console.WriteLine("--------");
            Console.WriteLine();
        }

        private static DigitalTwinsClient createDtClient()
        {
            string adtInstanceUrl = "https://<your-instance-hostname>";
            var credentials = new DefaultAzureCredential();
            var client = new DigitalTwinsClient(new Uri(adtInstanceUrl), credentials);
            return client;
        }

        // <CreateRelationshipMethod>
        private async static Task CustomMethod_CreateRelationshipAsync(DigitalTwinsClient client, string srcId, string targetId, string relName, IDictionary<string,object> inputProperties)
        {
            var relationship = new BasicRelationship
            {
                TargetId = targetId,
                Name = relName,
                Properties = inputProperties
            };

            try
            {
                string relId = $"{srcId}-{relName}->{targetId}";
                await client.CreateOrReplaceRelationshipAsync<BasicRelationship>(srcId, relId, relationship);
                Console.WriteLine($"Created {relName} relationship successfully. Relationship ID is {relId}.");
            }
            catch (RequestFailedException rex)
            {
                Console.WriteLine($"Create relationship error: {rex.Status}:{rex.Message}");
            }

        }
        // </CreateRelationshipMethod>

        // <UpdateRelationshipMethod>
        private async static Task CustomMethod_UpdateRelationshipAsync(DigitalTwinsClient client, string srcId, string relId, Azure.JsonPatchDocument updateDocument)
        {

            try
            {
                await client.UpdateRelationshipAsync(srcId, relId, updateDocument);
                Console.WriteLine($"Successfully updated {relId}");
            }
            catch (RequestFailedException rex)
            {
                Console.WriteLine($"Update relationship error: {rex.Status}:{rex.Message}");
            }

        }
        // </UpdateRelationshipMethod>

        // <FetchAndPrintMethod>
        private static async Task CustomMethod_FetchAndPrintTwinAsync(string twin_Id, DigitalTwinsClient client)
        {
            Response<BasicDigitalTwin> res = await client.GetDigitalTwinAsync<BasicDigitalTwin>(twin_Id);
            // <UseFindOutgoingRelationships>
            await CustomMethod_FindOutgoingRelationshipsAsync(client, twin_Id);
            // </UseFindOutgoingRelationships>
            // <UseFindIncomingRelationships>
            await CustomMethod_FindIncomingRelationshipsAsync(client, twin_Id);
            // </UseFindIncomingRelationships>

            return;
        }
        // </FetchAndPrintMethod>

        // <FindOutgoingRelationshipsMethod>
        private static async Task<List<BasicRelationship>> CustomMethod_FindOutgoingRelationshipsAsync(DigitalTwinsClient client, string dtId)
        {
            // Find the relationships for the twin
            
            try
            {
                // GetRelationshipsAsync will throw if an error occurs
                // <GetRelationshipsCall>
                AsyncPageable<BasicRelationship> rels = client.GetRelationshipsAsync<BasicRelationship>(dtId);
                // </GetRelationshipsCall>
                var results = new List<BasicRelationship>();
                await foreach (BasicRelationship rel in rels)
                {
                    results.Add(rel);
                    Console.WriteLine($"Found relationship: {rel.Id}");

                    //Print its properties
                    Console.WriteLine($"Relationship properties:");
                    foreach(KeyValuePair<string, object> property in rel.Properties)
                    {
                        Console.WriteLine("{0} = {1}", property.Key, property.Value);
                    }
                }

                return results;
            }
            catch (RequestFailedException ex)
            {
                Console.WriteLine($"*** Error {ex.Status}/{ex.ErrorCode} retrieving relationships for {dtId} due to {ex.Message}");
                return null;
            }
        }
        // </FindOutgoingRelationshipsMethod>

        // <FindIncomingRelationshipsMethod>
        private static async Task<List<IncomingRelationship>> CustomMethod_FindIncomingRelationshipsAsync(DigitalTwinsClient client, string dtId)
        {
            // Find the relationships for the twin
            
            try
            {
                // GetRelationshipsAsync will throw an error if a problem occurs
                AsyncPageable<IncomingRelationship> incomingRels = client.GetIncomingRelationshipsAsync(dtId);

                var results = new List<IncomingRelationship>();
                await foreach (IncomingRelationship incomingRel in incomingRels)
                {
                    results.Add(incomingRel);
                    Console.WriteLine($"Found incoming relationship: {incomingRel.RelationshipId}");

                    //Print its properties
                    Response<BasicRelationship> relResponse = await client.GetRelationshipAsync<BasicRelationship>(incomingRel.SourceId, incomingRel.RelationshipId);
                    BasicRelationship rel = relResponse.Value;
                    Console.WriteLine($"Relationship properties:");
                    foreach(KeyValuePair<string, object> property in rel.Properties)
                    {
                        Console.WriteLine("{0} = {1}", property.Key, property.Value);
                    }
                }
                return results;
            }
            catch (RequestFailedException ex)
            {
                Console.WriteLine($"*** Error {ex.Status}/{ex.ErrorCode} retrieving incoming relationships for {dtId} due to {ex.Message}");
                return null;
            }
        }
        // </FindIncomingRelationshipsMethod>

        // <DeleteRelationshipMethod>
        private static async Task CustomMethod_DeleteRelationshipAsync(DigitalTwinsClient client, string srcId, string relId)
        {
            try
            {
                Response response = await client.DeleteRelationshipAsync(srcId, relId);
                await CustomMethod_FetchAndPrintTwinAsync(srcId, client);
                Console.WriteLine("Deleted relationship successfully");
            }
            catch (RequestFailedException e)
            {
                Console.WriteLine($"Error {e.ErrorCode}");
            }
        }
        // </DeleteRelationshipMethod>
    }
}

Remarque

Il existe actuellement un problème connu affectant la classe wrapper DefaultAzureCredential susceptible d’entraîner une erreur lors de l’authentification. Si vous rencontrez ce problème, vous pouvez essayer d’instancier DefaultAzureCredential avec le paramètre facultatif suivant afin de le résoudre : new DefaultAzureCredential(new DefaultAzureCredentialOptions { ExcludeSharedTokenCacheCredential = true });

Pour plus d’informations sur ce problème, consultez Problèmes connus d’Azure Digital Twins.

Configurer un projet

Ensuite, effectuez les étapes ci-après pour configurer votre code de projet :

  1. Ajoutez les fichiers Room.json et Floor.json précédemment téléchargés à votre projet et remplacez les espaces réservés <path-to> dans le code pour indiquer à votre programme où les trouver.

  2. Remplacez l’espace réservé <your-instance-hostname> par le nom d’hôte de votre instance Azure Digital Twins.

  3. Ajoutez deux dépendances à votre projet. Elles seront nécessaires pour travailler avec Azure Digital Twins. La première correspond au package pour le SDK Azure Digital Twins pour .NET, et la seconde fournit des outils facilitant l’authentification auprès d’Azure.

    dotnet add package Azure.DigitalTwins.Core
    dotnet add package Azure.Identity
    

Vous devez également configurer des informations d’identification locales si vous souhaitez exécuter l’exemple directement. La section suivante décrit ce processus pas à pas.

Configurer les informations d’identification Azure locales

Cet exemple utilise DefaultAzureCredential (qui fait partie de la bibliothèque Azure.Identity) pour authentifier les utilisateurs auprès de l’instance Azure Digital Twins quand vous l’exécutez sur votre ordinateur local. Pour plus d’informations sur les différentes façons dont une application cliente peut s’authentifier auprès d’Azure Digital Twins, consultez Écrire le code d’authentification de l’application.

Avec DefaultAzureCredential, l’exemple recherche les informations d’identification dans votre environnement local, comme une connexion Azure dans une interface de ligne de commande Azure locale ou dans Visual Studio ou Visual Studio Code. C’est la raison pour laquelle vous devez vous connecter à Azure localement via l’un de ces mécanismes afin de configurer les informations d’identification pour l’exemple.

Si vous utilisez Visual Studio ou Visual Studio Code pour exécuter des exemples de code, vérifiez que vous êtes connecté à cet éditeur avec les mêmes informations d’identification Azure que celles que vous souhaitez utiliser pour accéder à votre instance Azure Digital Twins. Si vous utilisez une fenêtre CLI locale, exécutez la commande az login pour vous connecter à votre compte Azure. Après cela, lorsque vous exécutez votre échantillon de code, vous devriez être authentifié automatiquement.

Exécution de l'exemple

Vous avez terminé la configuration. Vous pouvez à présent exécuter l’exemple de projet de code.

Voici la sortie de la console du programme :

Screenshot of the console output showing the twin details with incoming and outgoing relationships of the twins.

Conseil

Le graphe de jumeaux est un concept en lien avec la création de relations entre les jumeaux. Si vous souhaitez afficher la représentation visuelle du graphe de jumeaux, consultez la section Visualisation de cet article.

Étapes suivantes

En savoir plus sur l’interrogation d’un graphique de jumeaux Azure Digital Twins :