Esercitazione: Codifica con Azure Digital Twins SDK

Gli sviluppatori che lavorano con Gemelli digitali di Azure scrivono in genere applicazioni client per interagire con l'istanza del servizio Gemelli digitali di Azure. Questa esercitazione destinata agli sviluppatori offre un'introduzione alla programmazione per il servizio Gemelli digitali di Azure, usando l'SDK di Gemelli digitali di Azure per .NET (C#). Viene descritta la procedura dettagliata per scrivere un'app client console in C# a partire da zero.

  • Configurare il progetto
  • Iniziare a scrivere il codice del progetto
  • Esempio di codice completo
  • Pulire le risorse
  • Passaggi successivi

Prerequisiti

Questa esercitazione su Gemelli digitali di Azure usa la riga di comando per il lavoro di installazione e progetto. Di conseguenza, è possibile usare qualsiasi editor di codice per esaminare gli esercizi.

Per iniziare, è necessario avere:

  • Qualsiasi editor di codice
  • .NET Core 3.1 nel computer di sviluppo. È possibile scaricare questa versione di .NET Core SDK multipiattaforma dalla pagina di download di .NET Core 3.1.

Preparare un'istanza di Gemelli digitali di Azure

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.

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.

Configurare il progetto

Quando si è pronti per iniziare a usare l'istanza di Gemelli digitali di Azure, iniziare a configurare il progetto dell'app client.

Aprire una finestra della console nel computer e creare una directory di progetto vuota in cui archiviare il lavoro durante questa esercitazione. Assegnare alla directory il nome desiderato, ad esempio DigitalTwinsCodeTutorial.

Passare alla nuova directory.

Nella directory del progetto creare un progetto di app console .NET vuoto. Nella finestra di comando è possibile eseguire il comando seguente per creare un progetto C# minimo per la console:

dotnet new console

Questo comando creerà diversi file all'interno della directory, incluso uno denominato Program.cs in cui si scriverà la maggior parte del codice.

Tenere aperta la finestra di comando perché si continuerà a usarla nell'intera esercitazione.

Successivamente, aggiungere due dipendenze al progetto che saranno necessarie per l'uso con Gemelli digitali di Azure. La prima è il pacchetto per Azure Digital Twins SDK per .NET, la seconda fornisce gli strumenti che consentono di eseguire l'autenticazione in Azure.

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

Iniziare a scrivere il codice del progetto

In questa sezione si inizierà a scrivere il codice per il nuovo progetto di app da usare con Gemelli digitali di Azure. Le azioni descritte includono:

  • Autenticazione per il servizio
  • Caricamento di un modello
  • Rilevamento degli errori
  • Creazione di gemelli digitali
  • Creazione di relazioni
  • Esecuzione di query sui gemelli digitali

È disponibile anche una sezione che mostra il codice completo alla fine dell'esercitazione. È possibile usare questa sezione come riferimento per controllare il programma man mano che si procede.

Per iniziare, aprire il file Program.cs in qualsiasi editor di codice. Verrà visualizzato un modello di codice minimo simile al seguente:

Screenshot of a snippet of sample code in a code editor.

Per prima cosa, aggiungere alcune righe di using all'inizio del codice per eseguire il pull delle dipendenze necessarie.

using Azure.DigitalTwins.Core;
using Azure.Identity;

In seguito verrà aggiunto codice in questo file per inserire alcune funzionalità.

Autenticazione per il servizio

La prima operazione che dovrà essere eseguita dall'app è l'autenticazione per il servizio Gemelli digitali di Azure. Quindi, è possibile creare una classe client del servizio per accedere alle funzioni dell'SDK.

Per eseguire l'autenticazione, è necessario il nome host dell'istanza di Gemelli digitali di Azure.

In Program.cs incollare il codice seguente sotto la riga di stampa "Hello, World!" nel Main metodo . Impostare il valore di sul nome host dell'istanza di Gemelli digitali di adtInstanceUrl Azure.

string adtInstanceUrl = "https://<your-Azure-Digital-Twins-instance-hostName>"; 

var credential = new DefaultAzureCredential();
var client = new DigitalTwinsClient(new Uri(adtInstanceUrl), credential);
Console.WriteLine($"Service client created – ready to go");

Salva il file.

Nella finestra di comando eseguire il codice con questo comando:

dotnet run

Questo comando ripristinerà le dipendenze alla prima esecuzione e quindi eseguirà il programma.

  • Se non si verifica alcun errore, il programma visualizzerà: "Client del servizio creato - pronto per l'esecuzione".
  • Poiché non è ancora presente alcuna gestione degli errori in questo progetto, in caso di problemi, verrà visualizzata un'eccezione generata dal codice.

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.

Caricare un modello

Gemelli digitali di Azure non include un vocabolario di dominio intrinseco. I tipi di elementi dell'ambiente che è possibile rappresentare in Gemelli digitali di Azure vengono definiti dall'utente usando i modelli. I modelli sono simili alle classi nei linguaggi di programmazione orientati a oggetti. Forniscono modelli definiti dall'utente per i gemelli digitali da seguire e di cui creare istanze in seguito. Vengono scritti in un linguaggio simile a JSON denominato DTDL (Digital Twins Definition Language).

Il primo passaggio per la creazione di una soluzione di Gemelli digitali di Azure consiste nel definire almeno un modello in un file DTDL.

Nella directory in cui è stato creato il progetto creare un nuovo file json denominato SampleModel.json. Incollare il corpo del file seguente:

{
  "@id": "dtmi:example:SampleModel;1",
  "@type": "Interface",
  "displayName": "SampleModel",
  "contents": [
    {
      "@type": "Relationship",
      "name": "contains"
    },
    {
      "@type": "Property",
      "name": "data",
      "schema": "string"
    }
  ],
  "@context": "dtmi:dtdl:context;3"
}

Suggerimento

Se per questa esercitazione si usa Visual Studio, è possibile selezionare il nuovo file JSON e impostare la proprietà Copia nella directory di output di Controllo proprietà su Copia se è più recente o su Copia sempre. In questo modo Visual Studio troverà il file JSON con il percorso predefinito quando si eseguirà il programma con F5 durante il resto dell'esercitazione.

Suggerimento

È possibile controllare i documenti del modello per assicurarsi che DTDL sia valido usando la libreria DTDLParser. Per altre informazioni sull'uso di questa libreria, vedere Analizzare e convalidare i modelli.

Aggiungere quindi altro codice a Program.cs per caricare il modello creato nell'istanza di Gemelli digitali di Azure.

Aggiungere prima di tutto alcune istruzioni using all'inizio del file:

using System.Threading.Tasks;
using System.IO;
using System.Collections.Generic;
using Azure;

Prepararsi quindi a usare i metodi asincroni dell'SDK del servizio C# cambiando la firma del metodo Main per consentire l'esecuzione asincrona.

static async Task Main(string[] args)
{

Nota

L'uso di async non è strettamente necessario, in quanto l'SDK fornisce anche versioni sincrone di tutte le chiamate. Questa esercitazione illustra come usare async.

Quindi aggiungere il primo frammento di codice che interagisce con il servizio Gemelli digitali di Azure. Questo codice carica il file DTDL creato dal disco e quindi lo carica nell'istanza del servizio Gemelli digitali di Azure.

Incollare il codice seguente sotto il codice di autorizzazione aggiunto in precedenza.

Console.WriteLine();
Console.WriteLine($"Upload a model");
string dtdl = File.ReadAllText("SampleModel.json");
var models = new List<string> { dtdl };
// Upload the model to the service
await client.CreateModelsAsync(models);

Nella finestra di comando eseguire il programma con questo comando:

dotnet run

"Caricare un modello" verrà stampato nell'output, a indicare che il codice è stato raggiunto, ma non è ancora presente alcun output per indicare se il caricamento è riuscito.

Per aggiungere un'istruzione di stampa che indica che tutti i modelli sono stati caricati correttamente nell'istanza, aggiungere il codice seguente subito dopo la sezione precedente:

// Read a list of models back from the service
AsyncPageable<DigitalTwinsModelData> modelDataList = client.GetModelsAsync();
await foreach (DigitalTwinsModelData md in modelDataList)
{
    Console.WriteLine($"Model: {md.Id}");
}

Prima di eseguire di nuovo il programma per testare questo nuovo codice, tenere presente che l'ultima volta in cui è stato eseguito il programma, il modello è già stato caricato. Gemelli digitali di Azure non consente di caricare lo stesso modello due volte, quindi se si tenta di caricare di nuovo lo stesso modello, il programma dovrebbe generare un'eccezione.

Tenendo presente queste informazioni, eseguire di nuovo il programma con questo comando nella finestra di comando:

dotnet run

Il programma dovrà generare un'eccezione. Quando si prova a caricare un modello che è già stato caricato, il servizio restituisce un errore di tipo "richiesta non valida" tramite l'API REST. Di conseguenza, l'SDK del client di Gemelli digitali di Azure genererà a sua volta un'eccezione, per ogni codice restituito dal servizio diverso da success.

Nella sezione successiva vengono illustrate eccezioni come questa, con informazioni su come gestirle nel codice.

Rilevare gli errori

Per evitare l'arresto anomalo del programma, è possibile aggiungere il codice di eccezione intorno al codice di caricamento del modello. Eseguire il wrapping della chiamata client esistente await client.CreateModelsAsync(typeList) in un gestore try/catch, come indicato di seguito:

try
{
    await client.CreateModelsAsync(models);
    Console.WriteLine("Models uploaded to the instance:");
}
catch (RequestFailedException e)
{
    Console.WriteLine($"Upload model error: {e.Status}: {e.Message}");
}

Eseguire di nuovo il programma con dotnet run nella finestra di comando. Si noterà che si ottengono altri dettagli sul problema di caricamento del modello, incluso un codice di errore che indica che ModelIdAlreadyExists.

Da questo punto in poi, nell'esercitazione verrà eseguito il wrapping di tutte le chiamate ai metodi del servizio in gestori try/catch.

Creare i gemelli digitali

Dopo aver caricato un modello in Gemelli digitali di Azure, è possibile usare questa definizione di modello per creare gemelli digitali. I gemelli digitali sono istanze di un modello e rappresentano le entità all'interno dell'ambiente aziendale, ad esempio i sensori di un'azienda agricola, le stanze di un edificio o i fari di un'auto. Questa sezione crea alcuni gemelli digitali basati sul modello caricato in precedenza.

Aggiungere il codice seguente alla fine del metodo Main per creare e inizializzare tre gemelli digitali basati su questo modello.

var twinData = new BasicDigitalTwin();
twinData.Metadata.ModelId = "dtmi:example:SampleModel;1";
twinData.Contents.Add("data", $"Hello World!");

string prefix = "sampleTwin-";
for (int i = 0; i < 3; i++)
{
    try
    {
        twinData.Id = $"{prefix}{i}";
        await client.CreateOrReplaceDigitalTwinAsync<BasicDigitalTwin>(twinData.Id, twinData);
        Console.WriteLine($"Created twin: {twinData.Id}");
    }
    catch(RequestFailedException e)
    {
        Console.WriteLine($"Create twin error: {e.Status}: {e.Message}");
    }
}

Nella finestra di comando eseguire il programma con dotnet run. Nell'output cercare i messaggi che indicano che sampleTwin-0, sampleTwin-1 e sampleTwin-2 sono stati creati.

Quindi eseguire di nuovo il programma.

Si noti che non viene generato alcun errore quando i gemelli vengono creati per la seconda volta, anche se esistono già dopo la prima esecuzione. A differenza della creazione del modello, la creazione di un gemello è, a livello REST, una chiamata PUT con semantica upsert. L'uso di questo tipo di chiamata REST significa che se esiste già un gemello, un tentativo di creare di nuovo lo stesso gemello sostituirà semplicemente il gemello originale. Non vengono generati errori.

Creare relazioni

Successivamente, è possibile creare relazioni tra i gemelli creati, per connetterli in un grafo dei gemelli. I grafi dei gemelli vengono usati per rappresentare l'intero ambiente.

Aggiungere un nuovo metodo statico alla Program classe sotto il Main metodo (il codice include ora due metodi):

public async static Task CreateRelationshipAsync(DigitalTwinsClient client, string srcId, string targetId)
{
    var relationship = new BasicRelationship
    {
        TargetId = targetId,
        Name = "contains"
    };

    try
    {
        string relId = $"{srcId}-contains->{targetId}";
        await client.CreateOrReplaceRelationshipAsync(srcId, relId, relationship);
        Console.WriteLine("Created relationship successfully");
    }
    catch (RequestFailedException e)
    {
        Console.WriteLine($"Create relationship error: {e.Status}: {e.Message}");
    }
}

Aggiungere quindi il codice seguente alla fine del metodo Main per chiamare il metodo CreateRelationship e usare il codice appena scritto:

// Connect the twins with relationships
await CreateRelationshipAsync(client, "sampleTwin-0", "sampleTwin-1");
await CreateRelationshipAsync(client, "sampleTwin-0", "sampleTwin-2");

Nella finestra di comando eseguire il programma con dotnet run. Nell'output cercare le istruzioni di stampa che indicano che le due relazioni sono state create correttamente.

Gemelli digitali di Azure non consente di creare una relazione se esiste già un'altra relazione con lo stesso ID. Se si esegue il programma più volte, verranno visualizzate eccezioni alla creazione della relazione. Questo codice rileva le eccezioni e le ignora.

Elencare le relazioni

Il codice successivo che verrà aggiunto consente di visualizzare l'elenco di relazioni create.

Aggiungere il nuovo metodo seguente alla classe Program:

public async static Task ListRelationshipsAsync(DigitalTwinsClient client, string srcId)
{
    try
    {
        AsyncPageable<BasicRelationship> results = client.GetRelationshipsAsync<BasicRelationship>(srcId);
        Console.WriteLine($"Twin {srcId} is connected to:");
        await foreach (BasicRelationship rel in results)
        {
            Console.WriteLine($" -{rel.Name}->{rel.TargetId}");
        }
    }
    catch (RequestFailedException e)
    {
        Console.WriteLine($"Relationship retrieval error: {e.Status}: {e.Message}");
    }
}

Aggiungere quindi il codice seguente alla fine del metodo Main per chiamare il codice ListRelationships:

//List the relationships
await ListRelationshipsAsync(client, "sampleTwin-0");

Nella finestra di comando eseguire il programma con dotnet run. Verrà visualizzato un elenco di tutte le relazioni create in un'istruzione di output simile alla seguente:

Screenshot of a console showing the program output, which results in a message that lists the twin relationships.

Eseguire query sui gemelli digitali

Una delle principali funzionalità di Gemelli digitali di Azure è la possibilità di eseguire query sul grafo dei gemelli in modo semplice ed efficiente per trovare le risposte alle domande sull'ambiente.

L'ultima sezione del codice da aggiungere in questa esercitazione esegue una query sull'istanza di Gemelli digitali di Azure. La query usata in questo esempio restituisce tutti i gemelli digitali dell'istanza.

Aggiungere questa istruzione using per consentire l'uso della classe JsonSerializer e presentare le informazioni del gemello digitale:

using System.Text.Json;

Aggiungere quindi il codice seguente alla fine del metodo Main:

// Run a query for all twins
string query = "SELECT * FROM digitaltwins";
AsyncPageable<BasicDigitalTwin> queryResult = client.QueryAsync<BasicDigitalTwin>(query);

await foreach (BasicDigitalTwin twin in queryResult)
{
    Console.WriteLine(JsonSerializer.Serialize(twin));
    Console.WriteLine("---------------");
}

Nella finestra di comando eseguire il programma con dotnet run. Nell'output verranno visualizzati tutti i dispositivi gemelli digitali di questa istanza.

Nota

Dopo aver apportato una modifica ai dati nel grafico, potrebbe verificarsi una latenza fino a 10 secondi prima che le modifiche vengano riflesse nelle query.

L'API DigitalTwins riflette immediatamente le modifiche, quindi se è necessaria una risposta immediata, usare una richiesta API (DigitalTwins GetById) o una chiamata SDK (GetDigitalTwin) per ottenere dati gemelli anziché una query.

Esempio di codice completo

A questo punto dell'esercitazione è disponibile un'app client completa che può eseguire azioni di base su Gemelli digitali di Azure. Per riferimento, di seguito è riportato il codice completo del programma nel file Program.cs:

using System;
// <Azure_Digital_Twins_dependencies>
using Azure.DigitalTwins.Core;
using Azure.Identity;
// </Azure_Digital_Twins_dependencies>
// <Model_dependencies>
using System.Threading.Tasks;
using System.IO;
using System.Collections.Generic;
using Azure;
// </Model_dependencies>
// <Query_dependencies>
using System.Text.Json;
// </Query_dependencies>

namespace DigitalTwins_Samples
{
    class DigitalTwinsClientAppSample
    {
        // <Async_signature>
        static async Task Main(string[] args)
        {
        // </Async_signature>
            Console.WriteLine("Hello World!");
            // <Authentication_code>
            string adtInstanceUrl = "https://<your-Azure-Digital-Twins-instance-hostName>"; 
            
            var credential = new DefaultAzureCredential();
            var client = new DigitalTwinsClient(new Uri(adtInstanceUrl), credential);
            Console.WriteLine($"Service client created – ready to go");
            // </Authentication_code>

            // <Model_code>
            Console.WriteLine();
            Console.WriteLine("Upload a model");
            string dtdl = File.ReadAllText("SampleModel.json");
            var models = new List<string> { dtdl };

            // Upload the model to the service
            // <Model_try_catch>
            try
            {
                await client.CreateModelsAsync(models);
                Console.WriteLine("Models uploaded to the instance:");
            }
            catch (RequestFailedException e)
            {
                Console.WriteLine($"Upload model error: {e.Status}: {e.Message}");
            }
            // </Model_try_catch>

            // <Print_model>
            // Read a list of models back from the service
            AsyncPageable<DigitalTwinsModelData> modelDataList = client.GetModelsAsync();
            await foreach (DigitalTwinsModelData md in modelDataList)
            {
                Console.WriteLine($"Model: {md.Id}");
            }
            // </Print_model>
            // </Model_code>

            // <Initialize_twins>
            var twinData = new BasicDigitalTwin();
            twinData.Metadata.ModelId = "dtmi:example:SampleModel;1";
            twinData.Contents.Add("data", $"Hello World!");
            
            string prefix = "sampleTwin-";
            for (int i = 0; i < 3; i++)
            {
                try
                {
                    twinData.Id = $"{prefix}{i}";
                    await client.CreateOrReplaceDigitalTwinAsync<BasicDigitalTwin>(twinData.Id, twinData);
                    Console.WriteLine($"Created twin: {twinData.Id}");
                }
                catch(RequestFailedException e)
                {
                    Console.WriteLine($"Create twin error: {e.Status}: {e.Message}");
                }
            }
            // </Initialize_twins>

            // <Use_create_relationship>
            // Connect the twins with relationships
            await CreateRelationshipAsync(client, "sampleTwin-0", "sampleTwin-1");
            await CreateRelationshipAsync(client, "sampleTwin-0", "sampleTwin-2");
            // </Use_create_relationship>

            // <Use_list_relationships>
            //List the relationships
            await ListRelationshipsAsync(client, "sampleTwin-0");
            // </Use_list_relationships>

            // <Query_twins>
            // Run a query for all twins
            string query = "SELECT * FROM digitaltwins";
            AsyncPageable<BasicDigitalTwin> queryResult = client.QueryAsync<BasicDigitalTwin>(query);
            
            await foreach (BasicDigitalTwin twin in queryResult)
            {
                Console.WriteLine(JsonSerializer.Serialize(twin));
                Console.WriteLine("---------------");
            }
            // </Query_twins>
        }

        // <Create_relationship>
        public async static Task CreateRelationshipAsync(DigitalTwinsClient client, string srcId, string targetId)
        {
            var relationship = new BasicRelationship
            {
                TargetId = targetId,
                Name = "contains"
            };
        
            try
            {
                string relId = $"{srcId}-contains->{targetId}";
                await client.CreateOrReplaceRelationshipAsync(srcId, relId, relationship);
                Console.WriteLine("Created relationship successfully");
            }
            catch (RequestFailedException e)
            {
                Console.WriteLine($"Create relationship error: {e.Status}: {e.Message}");
            }
        }
        // </Create_relationship>
        
        // <List_relationships>
        public async static Task ListRelationshipsAsync(DigitalTwinsClient client, string srcId)
        {
            try
            {
                AsyncPageable<BasicRelationship> results = client.GetRelationshipsAsync<BasicRelationship>(srcId);
                Console.WriteLine($"Twin {srcId} is connected to:");
                await foreach (BasicRelationship rel in results)
                {
                    Console.WriteLine($" -{rel.Name}->{rel.TargetId}");
                }
            }
            catch (RequestFailedException e)
            {
                Console.WriteLine($"Relationship retrieval error: {e.Status}: {e.Message}");
            }
        }
        // </List_relationships>
    }
}

Pulire le risorse

Dopo aver completato questa esercitazione, è possibile scegliere le risorse da rimuovere, a seconda delle operazioni da eseguire successivamente.

  • Se si prevede di continuare con l'esercitazione successiva, l'istanza usata in questa esercitazione può essere riutilizzata nella successiva. È possibile mantenere le risorse di Gemelli digitali di Azure configurate qui e ignorare il resto di questa sezione.
  • Se non sono necessarie risorse create in questa esercitazione, è possibile eliminare l'istanza di Gemelli digitali di Azure e tutte le altre risorse di questo articolo con il comando az group delete dell'interfaccia della riga di comando. In questo modo vengono eliminate tutte le risorse di Azure in un gruppo di risorse, nonché il gruppo di risorse stesso.

    Importante

    L'eliminazione di un gruppo di risorse è irreversibile. Il gruppo di risorse e tutte le risorse in esso contenute vengono eliminati in modo permanente. Assicurarsi di non eliminare accidentalmente il gruppo di risorse sbagliato o le risorse errate.

    Aprire Azure Cloud Shell o una finestra dell'interfaccia della riga di comando locale ed eseguire il comando seguente per eliminare il gruppo di risorse e tutto ciò che contiene.

    az group delete --name <your-resource-group>
    

È anche possibile eliminare la cartella del progetto dal computer locale.

Passaggi successivi

In questa esercitazione è stata creata un'applicazione client console .NET da zero. È stato scritto il codice per l'app client per eseguire le azioni di base con un'istanza di Gemelli digitali di Azure.

Continuare con l'esercitazione successiva per esplorare le operazioni che è possibile eseguire con questa app client di esempio: