Gerencie um gráfico de gêmeos digitais usando relacionamentos

O coração dos Gêmeos Digitais do Azure é o gráfico gêmeo que representa todo o seu ambiente. O gráfico de gêmeos é feito de gêmeos digitais individuais conectados através de relacionamentos. Este artigo se concentra no gerenciamento de relacionamentos e do gráfico como um todo; para trabalhar com gêmeos digitais individuais, consulte Gerenciar gêmeos digitais.

Depois de ter uma instância de Gêmeos Digitais do Azure em funcionamento e ter configurado o código de autenticação em seu aplicativo cliente, você pode criar, modificar e excluir gêmeos digitais e seus relacionamentos em uma instância de Gêmeos Digitais do Azure.

Pré-requisitos

Para trabalhar com Gêmeos Digitais do Azure neste artigo, você precisará de uma instância de Gêmeos Digitais do Azure e as permissões necessárias para usá-la. Se você já tiver uma instância do Azure Digital Twins configurada, poderá usar essa instância e pular para a próxima seção. Caso contrário, siga as instruções em Configurar uma instância e autenticação. As instruções contêm informações para ajudá-lo a verificar se você concluiu cada etapa com êxito.

Depois de configurar sua instância, anote o nome do host da instância. Você pode encontrar o nome do host no portal do Azure.

Interfaces de desenvolvimento

Este artigo destaca como concluir diferentes operações de gerenciamento usando o SDK do .NET (C#). Você também pode criar essas mesmas chamadas de gerenciamento usando os outros SDKs de linguagem descritos em APIs e SDKs do Azure Digital Twins.

Outras interfaces de desenvolvedor que podem ser usadas para concluir essas operações incluem:

Visualização

O Azure Digital Twins Explorer é uma ferramenta visual para explorar os dados no seu gráfico de Gêmeos Digitais do Azure. Pode utilizar o explorador para ver, consultar e editar os seus modelos, gémeos e relações.

Para ler sobre a ferramenta Azure Digital Twins Explorer, consulte Azure Digital Twins Explorer. Para obter etapas detalhadas sobre como usar seus recursos, consulte Usar o Azure Digital Twins Explorer.

Veja como é a visualização:

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

Criar relações

As relações descrevem como diferentes gêmeos digitais estão conectados entre si, o que forma a base do gráfico de gêmeos.

Os tipos de relações que podem ser criadas de um gêmeo (origem) para outro gêmeo (destino) são definidos como parte do modelo DTDL do gêmeo de origem. Você pode criar uma instância de um relacionamento usando a chamada SDK com gêmeos e detalhes de relacionamento que seguem a CreateOrReplaceRelationshipAsync() definição DTDL.

Para criar uma relação, você precisa especificar:

  • O ID do gêmeo de origem (srcId no exemplo de código abaixo): O ID do gêmeo onde o relacionamento se origina.
  • O ID do gêmeo de destino (targetId no exemplo de código abaixo): O ID do gêmeo onde o relacionamento chega.
  • Um nome de relacionamento (relName no exemplo de código abaixo): O tipo genérico de relacionamento, algo como contém.
  • Um ID de relacionamento (relId no exemplo de código abaixo): O nome específico para esse relacionamento, algo como Relacionamento1.

O ID de relacionamento deve ser exclusivo dentro do gêmeo de origem dado. Não precisa ser globalmente único. Por exemplo, para o Foo gêmeo, cada ID de relacionamento específico deve ser exclusivo. No entanto, outro bar gêmeo pode ter um relacionamento de saída que corresponde ao mesmo ID de um relacionamento Foo.

O exemplo de código a seguir ilustra como criar um relacionamento em sua instância do Azure Digital Twins. Ele usa a chamada SDK (realçada) dentro de um método personalizado que pode aparecer no contexto de um programa maior.

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

}

Essa função personalizada agora pode ser chamada para criar uma relação contém da seguinte maneira:

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

Se desejar criar vários relacionamentos, você pode repetir chamadas para o mesmo método, passando diferentes tipos de relacionamento para o argumento.

Para obter mais informações sobre a classe BasicRelationshipauxiliar, consulte APIs e SDKs do Azure Digital Twins.

Crie várias relações entre gémeos

As relações podem ser classificadas como:

  • Relações extrovertidas: relações pertencentes a este gêmeo que apontam para fora para conectá-lo a outros gêmeos. O GetRelationshipsAsync() método é usado para obter relações de saída de um gêmeo.
  • Relações de entrada: Relações pertencentes a outros gémeos que apontam para este gémeo para criar um elo de "entrada". O GetIncomingRelationshipsAsync() método é usado para obter relações de entrada de um gêmeo.

Não há restrição quanto ao número de relacionamentos que você pode ter entre dois gêmeos – você pode ter quantos relacionamentos entre gêmeos quiser.

Este fato significa que você pode expressar vários tipos diferentes de relacionamentos entre dois gêmeos ao mesmo tempo. Por exemplo, o Twin A pode ter tanto uma relação armazenada como uma relação fabricada com o Twin B.

Você pode até criar várias instâncias do mesmo tipo de relacionamento entre os mesmos dois gêmeos, se quiser. Neste exemplo, o Twin A pode ter duas relações armazenadas diferentes com o Twin B, desde que as relações tenham IDs de relacionamento diferentes.

Nota

Os atributos DTDL de e maxMultiplicity para relacionamentos não são suportados atualmente nos Gêmeos Digitais do Azure — mesmo que sejam definidos como parte de minMultiplicity um modelo, eles não serão impostos pelo serviço. Para obter mais informações, consulte Notas DTDL específicas do serviço.

Crie relacionamentos em massa com a API de Importação de Trabalhos

Você pode usar a API de Trabalhos de Importação para criar muitos relacionamentos de uma só vez em uma única chamada de API . Esse método requer o uso do Armazenamento de Blobs do Azure, bem como permissões de gravação em sua instância do Azure Digital Twins para relacionamentos e trabalhos em massa.

Gorjeta

A API de Trabalhos de Importação também permite que modelos e gêmeos sejam importados na mesma chamada, para criar todas as partes de um gráfico de uma só vez. Para obter mais informações sobre esse processo, consulte Carregar modelos, gêmeos e relacionamentos em massa com a API de Trabalhos de Importação.

Para importar relacionamentos em massa, você precisará estruturar seus relacionamentos (e quaisquer outros recursos incluídos no trabalho de importação em massa) como um arquivo NDJSON . A Relationships seção vem depois da Twins seção, tornando-se a última seção de dados do gráfico no arquivo. As relações definidas no arquivo podem fazer referência a gêmeos definidos neste arquivo ou já presentes na instância e, opcionalmente, podem incluir a inicialização de quaisquer propriedades que os relacionamentos tenham.

Você pode exibir um arquivo de importação de exemplo e um projeto de exemplo para criar esses arquivos na introdução da API de Trabalhos de Importação.

Em seguida, o arquivo precisa ser carregado em um blob de acréscimo no Armazenamento de Blobs do Azure. Para obter instruções sobre como criar um contêiner de armazenamento do Azure, consulte Criar um contêiner. Em seguida, carregue o arquivo usando seu método de carregamento preferido (algumas opções são o comando AzCopy, a CLI do Azure ou o portal do Azure).

Depois que o arquivo NDJSON tiver sido carregado para o contêiner, obtenha sua URL dentro do contêiner de blob. Você usará esse valor posteriormente no corpo da chamada de API de importação em massa.

Aqui está uma captura de tela mostrando o valor da URL de um arquivo de blob no portal do Azure:

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

Em seguida, o arquivo pode ser usado em uma chamada da API Import Jobs. Você fornecerá a URL de armazenamento de blob do arquivo de entrada, bem como uma nova URL de armazenamento de blob para indicar onde deseja que o log de saída seja armazenado quando for criado pelo serviço.

Listar relações

Listar propriedades de um único relacionamento

Você sempre pode desserializar dados de relacionamento para um tipo de sua escolha. Para acesso básico a uma relação, use o tipo BasicRelationship. A BasicRelationship classe auxiliar também dá acesso às propriedades definidas na relação, por meio de um IDictionary<string, object>arquivo . Para listar propriedades, você pode usar:

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

Listar relacionamentos de saída de um gêmeo digital

Para acessar a lista de relações de saída para um determinado gêmeo no gráfico, você pode usar o GetRelationships() método assim:

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

Esse método retorna um Azure.Pageable<T> ou , dependendo se você usa a versão síncrona ou Azure.AsyncPageable<T>assíncrona da chamada.

Aqui está um exemplo que recupera uma lista de relações. Ele usa a chamada SDK (realçada) dentro de um método personalizado que pode aparecer no contexto de um programa maior.

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

Agora você pode chamar esse método personalizado para ver os relacionamentos de saída dos gêmeos assim:

await CustomMethod_FindOutgoingRelationshipsAsync(client, twin_Id);

Você pode usar os relacionamentos recuperados para navegar para outros gêmeos em seu gráfico lendo o campo do relacionamento que é retornado e usando-o como o target ID para sua próxima chamada para GetDigitalTwin().

Listar relacionamentos de entrada para um gêmeo digital

Os Gêmeos Digitais do Azure também têm uma chamada SDK para localizar todos os relacionamentos de entrada para um determinado gêmeo. Este SDK geralmente é útil para navegação reversa ou ao excluir um gêmeo.

Nota

IncomingRelationship As chamadas não devolvem todo o corpo da relação. Para obter mais informações sobre a IncomingRelationship classe, consulte sua documentação de referência.

O exemplo de código na seção anterior se concentrou em descobrir relacionamentos de saída de um gêmeo. O exemplo a seguir é estruturado de forma semelhante, mas encontra relações de entrada para o gêmeo. Este exemplo também usa a chamada SDK (realçada) dentro de um método personalizado que pode aparecer no contexto de um programa maior.

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

Agora você pode chamar esse método personalizado para ver os relacionamentos de entrada dos gêmeos assim:

await CustomMethod_FindIncomingRelationshipsAsync(client, twin_Id);

Listar todas as propriedades e relações gêmeas

Usando os métodos acima para listar relacionamentos de entrada e saída para um gêmeo, você pode criar um método que imprime informações completas do gêmeo, incluindo as propriedades do gêmeo e ambos os tipos de seus relacionamentos. Aqui está um exemplo de método personalizado mostrando como combinar os métodos personalizados acima para essa finalidade.

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

Agora você pode chamar essa função personalizada assim:

await CustomMethod_FetchAndPrintTwinAsync(srcId, client);

Atualizar relações

As relações são atualizadas usando o UpdateRelationship método.

Nota

Este método é para atualizar as propriedades de uma relação. Se você precisar alterar o gêmeo de origem ou o gêmeo alvo do relacionamento, você precisará excluir o relacionamento e recriar um usando os novos gêmeos.

Os parâmetros necessários para a chamada do cliente são:

  • O ID do gémeo de origem (o gémeo onde a relação se origina).
  • A ID da relação a ser atualizada.
  • Um documento de patch JSON contendo as propriedades e os novos valores que você deseja atualizar.

Aqui está um trecho de código de exemplo mostrando como usar esse método. Este exemplo usa a chamada SDK (realçada) dentro de um método personalizado que pode aparecer no contexto de um programa maior.

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

}

Aqui está um exemplo de uma chamada para esse método personalizado, passando um documento de patch JSON com as informações para atualizar uma propriedade.

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

Eliminar relações

O primeiro parâmetro especifica o gêmeo de origem (o gêmeo onde a relação se origina). O outro parâmetro é o ID da relação. Você precisa do ID do gêmeo e do ID do relacionamento, porque os IDs do relacionamento são exclusivos apenas dentro do escopo de um gêmeo.

Aqui está o código de exemplo mostrando como usar esse método. Este exemplo usa a chamada SDK (realçada) dentro de um método personalizado que pode aparecer no contexto de um programa maior.

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

Agora você pode chamar esse método personalizado para excluir um relacionamento como este:

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

Nota

Se você quiser excluir todos os modelos, gêmeos e relacionamentos em uma instância de uma só vez, use a API Excluir trabalhos.

Crie vários elementos gráficos de uma só vez

Esta seção descreve estratégias para criar um gráfico com vários elementos ao mesmo tempo, em vez de usar chamadas de API individuais para carregar modelos, gêmeos e relacionamentos para carregá-los um a um.

Carregue modelos, gêmeos e relacionamentos em massa com a API de Trabalhos de Importação

Você pode usar a API de Trabalhos de Importação para carregar vários modelos, gêmeos e relacionamentos para sua instância em uma única chamada de API , criando efetivamente o gráfico de uma só vez. Esse método requer o uso do Armazenamento de Blobs do Azure, bem como permissões de gravação em sua instância do Azure Digital Twins para elementos gráficos (modelos, gêmeos e relacionamentos) e trabalhos em massa.

Para importar recursos em massa, comece criando um arquivo NDJSON contendo os detalhes de seus recursos. O arquivo começa com uma Header seção, seguida pelas seções Modelsopcionais , Twinse Relationships. Não é necessário incluir os três tipos de dados gráficos no arquivo, mas todas as seções presentes devem seguir essa ordem. Os gêmeos definidos no arquivo podem fazer referência a modelos definidos neste arquivo ou já presentes na instância e, opcionalmente, podem incluir a inicialização das propriedades do gêmeo. As relações definidas no arquivo podem fazer referência a gêmeos definidos neste arquivo ou já presentes na instância e, opcionalmente, podem incluir a inicialização de propriedades de relacionamento.

Você pode exibir um arquivo de importação de exemplo e um projeto de exemplo para criar esses arquivos na introdução da API de Trabalhos de Importação.

Em seguida, o arquivo precisa ser carregado em um blob de acréscimo no Armazenamento de Blobs do Azure. Para obter instruções sobre como criar um contêiner de armazenamento do Azure, consulte Criar um contêiner. Em seguida, carregue o arquivo usando seu método de carregamento preferido (algumas opções são o comando AzCopy, a CLI do Azure ou o portal do Azure).

Depois que o arquivo NDJSON tiver sido carregado para o contêiner, obtenha sua URL dentro do contêiner de blob. Você usará esse valor posteriormente no corpo da chamada de API de importação em massa.

Aqui está uma captura de tela mostrando o valor da URL de um arquivo de blob no portal do Azure:

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

Em seguida, o arquivo pode ser usado em uma chamada da API Import Jobs. Você fornecerá a URL de armazenamento de blob do arquivo de entrada, bem como uma nova URL de armazenamento de blob para indicar onde deseja que o log de saída seja armazenado quando for criado pelo serviço.

Importar gráfico com o Azure Digital Twins Explorer

O Azure Digital Twins Explorer é uma ferramenta visual para exibir e interagir com seu gráfico gêmeo. Ele contém um recurso para importar um arquivo gráfico no formato JSON ou Excel que pode conter vários modelos, gêmeos e relacionamentos.

Para obter informações detalhadas sobre como usar esse recurso, consulte Importar gráfico na documentação do Azure Digital Twins Explorer.

Crie gêmeos e relacionamentos a partir de um arquivo CSV

Às vezes, talvez seja necessário criar hierarquias gêmeas a partir de dados armazenados em um banco de dados diferente, em uma planilha ou em um arquivo CSV. Esta seção ilustra como ler dados de um arquivo CSV e criar um gráfico gêmeo a partir dele.

Considere a tabela de dados a seguir, descrevendo um conjunto de gêmeos digitais e relacionamentos. Os modelos referenciados neste arquivo já devem existir na instância do Azure Digital Twins.

Model ID Twin ID (deve ser exclusivo) Nome da relação ID do gêmeo alvo Dados de inicialização gêmea
dtmi:exemplo:Chão; 1 1º andar: contém Quarto1
dtmi:exemplo:Chão; 1 Andar0 contém Quarto0
dtmi:exemplo:Sala; 1 Quarto1 {"Temperatura": 80}
dtmi:exemplo:Sala; 1 Quarto0 {"Temperatura": 70}

Uma maneira de obter esses dados no Azure Digital Twins é converter a tabela em um arquivo CSV. Uma vez que a tabela é convertida, o código pode ser escrito para interpretar o arquivo em comandos para criar gêmeos e relacionamentos. O exemplo de código a seguir ilustra a leitura dos dados do arquivo CSV e a criação de um gráfico gêmeo no Azure Digital Twins.

No código abaixo, o arquivo CSV é chamado de data.csv e há um espaço reservado representando o nome do host da sua instância do Azure Digital Twins. O exemplo também faz uso de vários pacotes que você pode adicionar ao seu projeto para ajudar com esse 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);
        }
    }
}

Amostra de gráfico duplo executável

O trecho de código executável a seguir usa as operações de relacionamento deste artigo para criar um gráfico gêmeo a partir de gêmeos digitais e relacionamentos.

Configurar arquivos de projeto de exemplo

O trecho usa duas definições de modelo de exemplo, Room.json e Floor.json. Para baixar os arquivos de modelo para que você possa usá-los em seu código, use esses links para ir diretamente para os arquivos no GitHub. Em seguida, clique com o botão direito do mouse em qualquer lugar da tela, selecione Salvar como no menu do botão direito do mouse do navegador e use a janela Salvar como para salvar os arquivos como Room.json e Floor.json.

Em seguida, crie um novo projeto de aplicativo de console no Visual Studio ou no editor de sua escolha.

Em seguida, copie o seguinte código do exemplo executável em seu projeto:

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

Atualmente, há um problema conhecido que afeta a classe wrapper que pode resultar em um erro durante a DefaultAzureCredential autenticação. Se você encontrar esse problema, você pode tentar instanciar DefaultAzureCredential com o seguinte parâmetro opcional para resolvê-lo: new DefaultAzureCredential(new DefaultAzureCredentialOptions { ExcludeSharedTokenCacheCredential = true });

Para obter mais informações sobre esse problema, consulte Problemas conhecidos dos Gêmeos Digitais do Azure.

Configurar projeto

Em seguida, conclua as seguintes etapas para configurar o código do projeto:

  1. Adicione os arquivos Room.json e Floor.json que você baixou anteriormente ao seu projeto e substitua os <path-to> espaços reservados no código para informar ao seu programa onde encontrá-los.

  2. Substitua o espaço reservado <your-instance-hostname> pelo nome de host da instância do Azure Digital Twins.

  3. Adicione duas dependências ao seu projeto que serão necessárias para trabalhar com os Gêmeos Digitais do Azure. O primeiro é o pacote para o SDK de Gêmeos Digitais do Azure para .NET, e o segundo fornece ferramentas para ajudar com a autenticação no Azure.

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

Você também precisará configurar credenciais locais se quiser executar o exemplo diretamente. A próxima seção percorre esse processo.

Configurar credenciais locais do Azure

Este exemplo usa DefaultAzureCredential (parte da Azure.Identity biblioteca) para autenticar usuários com a instância do Azure Digital Twins quando você a executa em sua máquina local. Para obter mais informações sobre diferentes maneiras pelas quais um aplicativo cliente pode se autenticar com Gêmeos Digitais do Azure, consulte Escrever código de autenticação de aplicativo.

Com DefaultAzureCredentialo , o exemplo procurará credenciais em seu ambiente local, como uma entrada do Azure em uma CLI do Azure local ou no Visual Studio ou Visual Studio Code. Por esse motivo, você deve entrar no Azure localmente por meio de um desses mecanismos para configurar credenciais para o exemplo.

Se estiver a utilizar o Visual Studio ou o Visual Studio Code para executar exemplos de código, certifique-se de que tem sessão iniciada nesse editor com as mesmas credenciais do Azure que pretende utilizar para aceder à sua instância do Azure Digital Twins. Se você estiver usando uma janela de CLI local, execute o az login comando para entrar em sua conta do Azure. Depois disso, quando você executa seu exemplo de código, você deve ser autenticado automaticamente.

Executar o exemplo

Agora que você concluiu a instalação, você pode executar o projeto de código de exemplo.

Aqui está a saída do console do programa:

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

Gorjeta

O gráfico de gêmeos é um conceito de criação de relações entre gêmeos. Se você quiser exibir a representação visual do gráfico gêmeo, consulte a seção Visualização deste artigo.

Próximos passos

Saiba mais sobre como consultar um gráfico de gêmeos do Azure Digital Twins: