Share via


Gestire un grafico di gemelli digitali usando le relazioni

Il cuore di Gemelli digitali di Azure è il grafico dei gemelli che rappresenta l'intero ambiente. Il grafo del gemello è costituito da singoli gemelli digitali connessi tramite relazioni. Questo articolo è incentrato sulla gestione delle relazioni e del grafico nel suo complesso; per lavorare con singoli gemelli digitali, vedere Gestire gemelli digitali.

Dopo aver creato un'istanza di Gemelli digitali di Azure funzionante e aver configurato il codice di autenticazione nell'app client, è possibile creare, modificare ed eliminare gemelli digitali e le relative relazioni in un'istanza di Gemelli digitali di Azure.

Prerequisiti

Per usare Gemelli digitali di Azure in questo articolo, è necessaria un'istanza di Gemelli digitali di Azure e le autorizzazioni necessarie per usarlo. Se è già stata configurata un'istanza di Gemelli digitali di Azure, è possibile usare tale istanza e passare alla sezione successiva. In caso contrario, seguire le istruzioni riportate in Configurare un'istanza e l'autenticazione. Le istruzioni contengono informazioni che consentono di verificare che ogni passaggio sia stato completato correttamente.

Dopo aver configurato l'istanza, prendere nota del nome host dell'istanza. È possibile trovare il nome host nella portale di Azure.

Interfacce per sviluppatori

Questo articolo illustra come completare diverse operazioni di gestione usando .NET (C#) SDK. È anche possibile creare queste stesse chiamate di gestione usando gli altri SDK del linguaggio descritti in API e SDK di Gemelli digitali di Azure.

Altre interfacce di sviluppo che possono essere usate per completare queste operazioni includono:

Visualizzazione

Azure Digital Twins Explorer è uno strumento visivo per esplorare i dati nel grafico di Gemelli digitali di Azure. È possibile usare Esplora risorse per visualizzare, eseguire query e modificare modelli, gemelli e relazioni.

Per informazioni sullo strumento Azure Digital Twins Explorer, vedere Azure Digital Twins Explorer. Per informazioni dettagliate su come usare le funzionalità, vedere Usare Azure Digital Twins Explorer.

Ecco l'aspetto della visualizzazione:

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

Creare relazioni

Le relazioni descrivono il modo in cui gemelli digitali diversi sono connessi tra loro. Tali relazioni costituiscono la base del grafo dei gemelli.

I tipi di relazioni che possono essere creati da un gemello (di origine) a un altro gemello (di destinazione) vengono definiti come parte del modello DTDL del gemello di origine. È possibile creare un'istanza di una relazione usando la CreateOrReplaceRelationshipAsync() chiamata SDK con gemelli e i dettagli della relazione che seguono la definizione DTDL.

Per creare una relazione, è necessario specificare i seguenti elementi:

  • ID del gemello di origine (srcId nell'esempio di codice seguente): ID del gemello in cui ha origine la relazione.
  • ID del gemello di destinazione (targetId nell'esempio di codice seguente): ID del gemello in cui arriva la relazione.
  • Nome di relazione (relName nell'esempio di codice riportato di seguito): tipo generico di relazione, simile al contenuto.
  • ID relazione (relId nell'esempio di codice seguente): nome specifico per questa relazione, simile a Relationship1.

Nel gemello di origine specificato l’ID relazione deve essere univoco. Non è comunque necessario che l’ID sia univoco a livello globale. Ad esempio, per il gemello Foo, ogni ID di relazione specifico deve essere univoco. Tuttavia, un'altra barra gemella può avere una relazione in uscita che corrisponde allo stesso ID di una relazione Foo.

Il codice di esempio che segue illustra come creare una relazione nell'istanza di Gemelli digitali di Azure. Usa la chiamata SDK (evidenziata) all'interno di un metodo personalizzato che potrebbe apparire nel contesto di un programma più grande.

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}");
    }

}

Questa funzione personalizzata può ora essere chiamata per creare una relazione contenente nel modo seguente:

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

Se si desidera creare più relazioni, è possibile ripetere le chiamate allo stesso metodo, passando tipi di relazione diversi nell'argomento .

Per altre informazioni sulla classe BasicRelationshiphelper , vedere API e SDK di Gemelli digitali di Azure.

Creare più relazioni tra gemelli

Le relazioni possono essere classificate come:

  • Relazioni in uscita: relazioni appartenenti a questo gemello che puntano verso l'esterno per connettere il gemello ad altri gemelli. Il metodo GetRelationshipsAsync() viene usato per ottenere le relazioni in uscita di un gemello.
  • Relazioni in ingresso: relazioni appartenenti ad altri gemelli che puntano a questo gemello per creare un collegamento "in ingresso". Il metodo GetIncomingRelationshipsAsync() viene usato per ottenere le relazioni in ingresso di un gemello.

Non esiste alcuna restrizione sul numero di relazioni che è possibile avere tra due gemelli: è possibile avere il maggior numero di relazioni tra i gemelli desiderati.

Questo significa che è possibile esprimere diversi tipi di relazioni tra due gemelli contemporaneamente. Ad esempio, Twin A può avere una relazione archiviata e una relazione prodotta con Twin B.

È anche possibile creare più istanze dello stesso tipo di relazione tra gli stessi due gemelli, se necessario. In questo esempio, Twin A potrebbe avere due relazioni archiviate diverse con Twin B, purché le relazioni abbiano ID relazione diversi.

Nota

Gli attributi DTDL di minMultiplicity e maxMultiplicity per le relazioni non sono attualmente supportati in Gemelli digitali di Azure, anche se sono definiti come parte di un modello, non verranno applicati dal servizio. Per altre informazioni, vedere Note DTDL specifiche del servizio.

Creare relazioni in blocco con l'API Importa processi

È possibile usare l'API Importa processi per creare molte relazioni contemporaneamente in una singola chiamata API. Questo metodo richiede l'uso di Archiviazione BLOB di Azure, nonché le autorizzazioni di scrittura nell'istanza di Gemelli digitali di Azure per relazioni e processi in blocco.

Suggerimento

L'API Processi di importazione consente anche l'importazione di modelli e gemelli nella stessa chiamata, per creare tutte le parti di un grafico contemporaneamente. Per altre informazioni su questo processo, vedere Caricare modelli, gemelli e relazioni in blocco con l'API Importa processi.

Per importare relazioni in blocco, è necessario strutturare le relazioni (e tutte le altre risorse incluse nel processo di importazione bulk) come file NDJSON . La Relationships sezione segue la Twins sezione , rendendola l'ultima sezione dei dati del grafo nel file. Le relazioni definite nel file possono fare riferimento a gemelli definiti in questo file o già presenti nell'istanza e possono includere facoltativamente l'inizializzazione di tutte le proprietà presenti nelle relazioni.

È possibile visualizzare un file di importazione di esempio e un progetto di esempio per la creazione di questi file nell'introduzione all'API Importa processi.

Successivamente, il file deve essere caricato in un BLOB di accodamento in Archiviazione BLOB di Azure. Per istruzioni su come creare un contenitore di archiviazione di Azure, vedere Creare un contenitore. Caricare quindi il file usando il metodo di caricamento preferito(alcune opzioni sono il comando AzCopy, l'interfaccia della riga di comando di Azure o il portale di Azure).

Dopo aver caricato il file NDJSON nel contenitore, ottenere l'URL all'interno del contenitore BLOB. Questo valore verrà usato più avanti nel corpo della chiamata API di importazione bulk.

Ecco uno screenshot che mostra il valore URL di un file BLOB nel portale di Azure:

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

Il file può quindi essere usato in una chiamata API Import Jobs. Si fornirà l'URL di archiviazione BLOB del file di input, nonché un nuovo URL di archiviazione BLOB per indicare dove archiviare il log di output quando viene creato dal servizio.

Elencare le relazioni

Elencare le proprietà di una singola relazione

È sempre possibile deserializzare i dati delle relazioni con un tipo di propria scelta. Per l'accesso di base a una relazione, usare il tipo BasicRelationship. La BasicRelationship classe helper consente inoltre di accedere alle proprietà definite nella relazione, tramite un oggetto IDictionary<string, object>. Per elencare le proprietà, è possibile usare:

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}");
        }
    }
}

Elencare le relazioni in uscita da un gemello digitale

Per accedere all'elenco delle relazioni in uscita per un determinato gemello nel grafico, è possibile usare il GetRelationships() metodo come segue:

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

Questo metodo restituisce un oggetto Azure.Pageable<T> o Azure.AsyncPageable<T>, a seconda che si usi la versione sincrona o asincrona della chiamata.

Ecco un esempio che recupera un elenco di relazioni. Usa la chiamata SDK (evidenziata) all'interno di un metodo personalizzato che potrebbe apparire nel contesto di un programma più grande.

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;
    }
}

È ora possibile chiamare questo metodo personalizzato per visualizzare le relazioni in uscita dei gemelli. Allo scopo, procedere nel modo seguente:

await CustomMethod_FindOutgoingRelationshipsAsync(client, twin_Id);

È possibile usare le relazioni recuperate per passare ad altri gemelli nel grafico leggendo il target campo dalla relazione restituita e usandolo come ID per la chiamata successiva a GetDigitalTwin().

Elencare le relazioni in ingresso con un gemello digitale

Gemelli digitali di Azure include anche una chiamata SDK per trovare tutte le relazioni in ingresso a un determinato gemello. Questo SDK è spesso utile per lo spostamento inverso o quando si elimina un gemello.

Nota

IncomingRelationship le chiamate non restituiscono il corpo completo della relazione. Per altre informazioni sulla classe, vedere la IncomingRelationship relativa documentazione di riferimento.

L'esempio di codice nella sezione precedente è incentrato sulla ricerca di relazioni in uscita da un gemello. L'esempio seguente è strutturato in modo analogo, ma trova invece le relazioni in ingresso con il gemello. In questo esempio viene usata anche la chiamata SDK (evidenziata) all'interno di un metodo personalizzato che potrebbe essere visualizzato nel contesto di un programma più grande.

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;
    }
}

È ora possibile chiamare questo metodo personalizzato per visualizzare le relazioni in uscita dei gemelli. Allo scopo, procedere nel modo seguente:

await CustomMethod_FindIncomingRelationshipsAsync(client, twin_Id);

Elencare tutte le proprietà e le relazioni dei gemelli

Usando i metodi precedenti per elencare le relazioni in uscita e in ingresso a un gemello, è possibile creare un metodo che stampa informazioni complete sui gemelli, incluse le proprietà del gemello e entrambi i tipi di relazioni. Ecco un esempio di metodo personalizzato che illustra come combinare i metodi personalizzati precedenti per questo scopo.

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;
}

È ora possibile chiamare questa funzione personalizzata come segue:

await CustomMethod_FetchAndPrintTwinAsync(srcId, client);

Aggiornare le relazioni

Le relazioni vengono aggiornate usando il UpdateRelationship metodo .

Nota

Questo metodo consente di aggiornare le proprietà di una relazione. Se è necessario modificare il gemello di origine o il gemello di destinazione della relazione, è necessario eliminare la relazione e crearne nuovamente una usando i nuovi gemelli.

I parametri obbligatori per la chiamata client sono:

  • ID del gemello di origine (il gemello in cui ha origine la relazione).
  • ID della relazione da aggiornare.
  • Documento patch JSON contenente le proprietà e i nuovi valori da aggiornare.

Ecco un frammento di codice di esempio che illustra come usare questo metodo. In questo esempio viene usata la chiamata SDK (evidenziata) all'interno di un metodo personalizzato che potrebbe apparire nel contesto di un programma più grande.

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}");
    }

}

Ecco un esempio di chiamata a questo metodo personalizzato, passando un documento patch JSON con le informazioni per aggiornare una proprietà.

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

Eliminare le relazioni

Il primo parametro specifica il gemello di origine (il gemello in cui ha origine la relazione). L'altro parametro è l'ID relazione. Sono necessari sia l'ID gemello che l'ID relazione, perché gli ID relazione sono univoci solo nell'ambito di un gemello.

Ecco il codice di esempio che illustra come usare questo metodo. In questo esempio viene usata la chiamata SDK (evidenziata) all'interno di un metodo personalizzato che potrebbe apparire nel contesto di un programma più grande.

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}");
    }
}

È ora possibile chiamare questo metodo personalizzato per eliminare una relazione simile alla seguente:

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

Nota

Se si vogliono eliminare tutti i modelli, i gemelli e le relazioni in un'istanza contemporaneamente, usare l'API Elimina processi.

Creare più elementi del grafo contemporaneamente

Questa sezione descrive le strategie per la creazione di un grafo con più elementi contemporaneamente, anziché l'uso di singole chiamate API per caricare modelli, gemelli e relazioni per caricarli uno alla volta.

Caricare modelli, gemelli e relazioni in blocco con l'API Importa processi

È possibile usare l'API Importa processi per caricare più modelli, gemelli e relazioni all'istanza in una singola chiamata API, creando in modo efficace il grafico contemporaneamente. Questo metodo richiede l'uso di Archiviazione BLOB di Azure, nonché le autorizzazioni di scrittura nell'istanza di Gemelli digitali di Azure per gli elementi del grafo (modelli, gemelli e relazioni) e i processi in blocco.

Per importare le risorse in blocco, iniziare creando un file NDJSON contenente i dettagli delle risorse. Il file inizia con una Header sezione, seguita dalle sezioni Modelsfacoltative , Twinse Relationships. Non è necessario includere tutti e tre i tipi di dati del grafo nel file, ma tutte le sezioni presenti devono seguire tale ordine. I gemelli definiti nel file possono fare riferimento a modelli definiti in questo file o già presenti nell'istanza e possono includere facoltativamente l'inizializzazione delle proprietà del gemello. Le relazioni definite nel file possono fare riferimento a gemelli definiti in questo file o già presenti nell'istanza e possono includere facoltativamente l'inizializzazione delle proprietà della relazione.

È possibile visualizzare un file di importazione di esempio e un progetto di esempio per la creazione di questi file nell'introduzione all'API Importa processi.

Successivamente, il file deve essere caricato in un BLOB di accodamento in Archiviazione BLOB di Azure. Per istruzioni su come creare un contenitore di archiviazione di Azure, vedere Creare un contenitore. Caricare quindi il file usando il metodo di caricamento preferito(alcune opzioni sono il comando AzCopy, l'interfaccia della riga di comando di Azure o il portale di Azure).

Dopo aver caricato il file NDJSON nel contenitore, ottenere l'URL all'interno del contenitore BLOB. Questo valore verrà usato più avanti nel corpo della chiamata API di importazione bulk.

Ecco uno screenshot che mostra il valore URL di un file BLOB nel portale di Azure:

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

Il file può quindi essere usato in una chiamata API Import Jobs. Si fornirà l'URL di archiviazione BLOB del file di input, nonché un nuovo URL di archiviazione BLOB per indicare dove archiviare il log di output quando viene creato dal servizio.

Importare un grafo con Azure Digital Twins Explorer

Azure Digital Twins Explorer è uno strumento visivo per la visualizzazione e l'interazione con il grafo del gemello. Contiene una funzionalità per l'importazione di un file a grafo in formato JSON o Excel che può contenere più modelli, gemelli e relazioni.

Per informazioni dettagliate sull'uso di questa funzionalità, vedere Importare un grafo nella documentazione di Azure Digital Twins Explorer.

Creare gemelli e relazioni da un file CSV

In alcuni casi, potrebbe essere necessario creare gerarchie gemelli da dati archiviati in un database diverso o in un foglio di calcolo o in un file CSV. Questa sezione illustra come leggere i dati da un file CSV e crearne uno esterno.

Si consideri la tabella di dati seguente, che descrive un set di gemelli digitali e relazioni. I modelli a cui si fa riferimento in questo file devono già esistere nell'istanza di Gemelli digitali di Azure.

Model ID ID gemello (deve essere univoco) Nome relazione ID del dispositivo gemello di destinazione Dati init gemelli
dtmi:example:Floor; 1 Floor1 contains Room1
dtmi:example:Floor; 1 Floor0 contains Room0
dtmi:example:Room; 1 Room1 {"Temperature": 80}
dtmi:example:Room; 1 Room0 {"Temperature": 70}

Un modo per ottenere questi dati in Gemelli digitali di Azure consiste nel convertire la tabella in un file CSV. Dopo la conversione della tabella, il codice può essere scritto per interpretare il file in comandi per creare gemelli e relazioni. L'esempio di codice seguente illustra la lettura dei dati dal file CSV e la creazione di un grafo gemello in Gemelli digitali di Azure.

Nel codice seguente il file CSV è denominato data.csv ed è presente un segnaposto che rappresenta il nome host dell'istanza di Gemelli digitali di Azure. L'esempio usa anche diversi pacchetti che è possibile aggiungere al progetto per facilitare questo processo.

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);
        }
    }
}

Esempio di grafo di gemelli eseguibili

Il frammento di codice eseguibile seguente usa le operazioni di relazione di questo articolo per creare un grafo gemello da gemelli digitali e relazioni.

Configurare i file di progetto di esempio

Il frammento di codice usa due definizioni di modello di esempio, Room.json e Floor.json. Per scaricare i file del modello in modo da poterli usare nel codice, usare questi collegamenti per passare direttamente ai file in GitHub. Fare quindi clic con il pulsante destro del mouse in un punto qualsiasi della schermata, scegliere Salva con nome nel menu di scelta rapida del browser e usare la finestra Salva con nome per salvare i file come Room.json e Floor.json.

Creare quindi un nuovo progetto di app console in Visual Studio o nell'editor preferito.

Copiare quindi il codice seguente dell'esempio eseguibile nel progetto:

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>
    }
}

Nota

Attualmente è presente un problema noto che interessa la DefaultAzureCredential classe wrapper che può causare un errore durante l'autenticazione. Se si verifica questo problema, è possibile provare a creare DefaultAzureCredential un'istanza con il parametro facoltativo seguente per risolverlo: new DefaultAzureCredential(new DefaultAzureCredentialOptions { ExcludeSharedTokenCacheCredential = true });

Per altre informazioni su questo problema, vedere Problemi noti di Gemelli digitali di Azure.

Configurare il progetto

Completare quindi i passaggi seguenti per configurare il codice del progetto:

  1. Aggiungere i file Room.json e Floor.json scaricati in precedenza al progetto e sostituire i <path-to> segnaposto nel codice per indicare al programma dove trovarli.

  2. Sostituire il segnaposto <your-instance-hostname> con il nome host dell'istanza di Gemelli digitali di Azure.

  3. Aggiungere due dipendenze al progetto che sarà necessario per lavorare con Gemelli digitali di Azure. Il primo è il pacchetto per Azure Digital Twins SDK per .NET e il secondo fornisce strumenti per facilitare l'autenticazione in Azure.

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

È anche necessario configurare le credenziali locali se si vuole eseguire direttamente l'esempio. La sezione successiva illustra questo processo.

Configurare le credenziali di Azure locali

Questo esempio usa DefaultAzureCredential (parte della libreria Azure.Identity) per l'autenticazione degli utenti con l'istanza di Gemelli digitali di Azure eseguita nel computer locale. Per altre informazioni sui diversi modi con cui un'app client può eseguire l'autenticazione con Gemelli digitali di Azure, vedere Scrivere il codice di autenticazione dell'app.

Con DefaultAzureCredential, l'esempio cercherà le credenziali nell'ambiente locale, ad esempio un account di accesso di Azure nell'interfaccia della riga di comando di Azure locale o in Visual Studio o Visual Studio Code. Per questo motivo è necessario accedere ad Azure in locale tramite uno di questi meccanismi per configurare le credenziali per l'esempio.

Se si usa Visual Studio o Visual Studio Code per eseguire esempi di codice, assicurarsi di aver eseguito l'accesso a tale editor con le stesse credenziali di Azure da usare per accedere all'istanza di Gemelli digitali di Azure. Se si usa una finestra dell'interfaccia della riga di comando locale, eseguire il az login comando per accedere all'account Azure. In seguito, quando si esegue l'esempio di codice, si dovrebbe essere autenticati automaticamente.

Eseguire l'esempio

Dopo aver completato l'installazione, è possibile eseguire il progetto di codice di esempio.

Ecco l'output della console del programma:

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

Suggerimento

Il grafico dei gemelli è un concetto di creazione di relazioni tra gemelli. Per visualizzare la rappresentazione visiva del grafico gemello, vedere la sezione Visualizzazione di questo articolo.

Passaggi successivi

Informazioni sull'esecuzione di query su un grafo di Gemelli digitali di Azure: