Compartilhar via


Gerenciar um grafo de gêmeos digital usando relações

O núcleo dos Gêmeos Digitais do Azure é o grafo de gêmeos, que representa todo o ambiente. O grafo é formado por gêmeos digitais individuais conectados por meio de relações. O foco deste artigo é o gerenciamento de relações e do grafo como um todo. Para trabalhar com gêmeos digitais individuais, confira Gerenciar gêmeos digitais.

Depois criar uma instância dos Gêmeos Digitais do Azure e configurar o código de autenticação no aplicativo cliente, você pode criar, modificar e excluir os gêmeos digitais e as relações entre eles na instância dos Gêmeos Digitais do Azure.

Pré-requisitos

Para trabalhar com os Gêmeos Digitais do Azure neste artigo, você precisará ter uma instância dos Gêmeos Digitais do Azure e as permissões necessárias para usá-la. Se você já tiver uma instância dos Gêmeos Digitais do Azure configurada, use essa instância e vá direto para a próxima seção. Caso contrário, siga as instruções descritas em Configurar uma instância e uma autenticação. As instruções contêm informações que ajudarão você a verificar se cada etapa foi concluída com êxito.

Após configurar a instância, anote o nome do host da instância. Encontre o nome do host no portal do Azure.

Interfaces do desenvolvedor

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 SDKs de outras linguagens descritos em APIs e SDKs dos Gêmeos Digitais do Azure.

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

Visualização

O Azure Digital Twins Explorer é uma ferramenta visual para explorar os dados em seu grafo dos Gêmeos Digitais do Azure. Você pode usar o Explorer para exibir, consultar e editar seus modelos, gêmeos e relações.

Para ler sobre a ferramenta Azure Digital Twins Explorer, confira Azure Digital Twins Explorer. Para ver as etapas detalhadas sobre como usar seus recurso, confira Usar o Azure Digital Twins Explorer.

A visualização terá a seguinte aparência:

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

Criar relações

As relações descrevem a conexão entre gêmeos digitais, que forma a base do grafo de gêmeos.

Os tipos de relações que podem ser criados 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 uma relação usando a chamada do SDK CreateOrReplaceRelationshipAsync() com gêmeos e detalhes de relação que estão em conformidade com a definição de DTDL.

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

  • A ID do gêmeo de origem (srcId no exemplo de código abaixo): a ID do gêmeo que é a origem da relação.
  • A ID do gêmeo de destino (targetId no exemplo de código abaixo): a ID do gêmeo que é o destino da relação.
  • O nome da relação (relName no exemplo de código abaixo): o tipo genérico de relação, algo como contém.
  • A ID da relação (relId no exemplo de código abaixo): o nome específico da relação, algo como Relação1.

A ID da relação deve ser exclusiva dentro do gêmeo de origem. Ela não precisa ser globalmente exclusiva. Por exemplo, para o gêmeo Foo, cada ID de relação específica precisa ser exclusiva. No entanto, outro gêmeo, o Bar, pode ter uma relação de saída com a mesma ID de uma relação com Foo.

O exemplo de código a seguir mostra como criar relações em uma instância dos Gêmeos Digitais do Azure. O exemplo usa a chamada ao 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);

Para criar mais de uma relação, repita as chamadas ao mesmo método passando tipos de relação diferentes como argumento.

Veja mais informações sobre a classe auxiliar BasicRelationship em APIs e SDKs dos Gêmeos Digitais do Azure.

Criar mais de uma relação entre gêmeos

As relações podem ser classificadas como:

  • Relações de saída: relações que pertencem a este gêmeo e apontam para fora, formando conexão com outros gêmeos. O método GetRelationshipsAsync() é usado para obter as relações de saída de um gêmeo.
  • Relações de entrada: relações que pertencem a outros gêmeos e apontam para este gêmeo, criando um link de "entrada". O método GetIncomingRelationshipsAsync() é usado para obter as relações de entrada de um gêmeo.

Não há restrição quanto ao número de relações entre dois gêmeos. Você pode ter quantas relações entre gêmeos desejar.

Isso significa que você pode expressar vários tipos diferentes de relações entre dois gêmeos ao mesmo tempo. Por exemplo, o Gêmeo A pode ter tanto a relação armazenado quanto a relação fabricado com o Gêmeo B.

Você pode até mesmo criar mais de uma instância do mesmo tipo de relação entre os mesmos gêmeos, caso queira. Nesse exemplo, o Gêmeo A pode ter duas relações armazenado diferentes com o Gêmeo B, desde que as relações tenham IDs diferentes.

Observação

No momento, os atributos DTDL de minMultiplicity e maxMultiplicity para relações não têm suporte nos Gêmeos Digitais do Azure e, mesmo que eles sejam definidos como parte de um modelo, eles não serão aplicados pelo serviço. Para obter mais informações, confira as Notas da DTDL específicas do serviço.

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

Você pode usar a API Importar Trabalhos para criar vários relacionamentos ao mesmo tempo 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 de Gêmeos Digitais do Azure para relacionamentos e trabalhos em massa.

Dica

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 Importar Trabalhos.

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 após a seção, tornando-a a Twins última seção de dados do gráfico no arquivo. As relações definidas no arquivo podem fazer referência a gêmeos que estão definidos nesse arquivo ou já presentes na instância e, opcionalmente, podem incluir a inicialização de quaisquer propriedades que as relações 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 no 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 de API de Trabalhos de Importação. Você fornecerá a URL de armazenamento de blob do arquivo de entrada, bem como uma nova URL de armazenamento de blob para indicar onde você deseja que o log de saída seja armazenado quando for criado pelo serviço.

Listar relações

Listar propriedades de uma única relação

Você sempre pode desserializar dados de relação para um tipo de sua escolha. Para ter acesso básico a uma relação, use o tipo BasicRelationship. A classe auxiliar BasicRelationship também fornece acesso a propriedades definidas na relação por meio de um IDictionary<string, object>. 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 relações de saída de um gêmeo digital

Para acessar a lista de relações de saída de um determinado gêmeo no grafo, você pode usar o método GetRelationships() desta forma:

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

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

Veja um exemplo que recupera uma lista de relações. O exemplo usa a chamada ao 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 as relações de saída dos gêmeos desta forma:

await CustomMethod_FindOutgoingRelationshipsAsync(client, twin_Id);

Você pode usar as relações recuperadas para navegar para outros gêmeos no grafo lendo o campo target da relação retornada e usando-o como a ID da próxima chamada para GetDigitalTwin().

Listas as relações de entrada para um gêmeo digital

Os Gêmeos Digitais do Azure também têm uma chamada de SDK para encontrar todas as relações de entrada de um determinado gêmeo. Esse SDK geralmente é útil para navegação reversa ou ao excluir gêmeos.

Observação

As chamadas a IncomingRelationship não retornam o corpo completo da relação. Veja mais informações sobre a classe IncomingRelationship na documentação de referência.

O exemplo de código da seção anterior identifica as relações de saída de um gêmeo. O exemplo a seguir tem uma estrutura semelhante, mas identifica as relações de entrada do gêmeo. O exemplo também usa a chamada ao 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 as relações de entrada dos gêmeos desta forma:

await CustomMethod_FindIncomingRelationshipsAsync(client, twin_Id);

Exibir todas as propriedades e relações dos gêmeos

Usando os métodos acima para exibir as relações de saída e de entrada do gêmeo, você pode criar um método que exibe todas as informações dele, incluindo as propriedades e os dois tipos de relações. Este é um exemplo de método personalizado que mostra 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 desta forma:

await CustomMethod_FetchAndPrintTwinAsync(srcId, client);

Atualizar relações

Use o método UpdateRelationship para editar relações.

Observação

Ele serve para editar as propriedades da relação. Se você precisar alterar o gêmeo de origem ou de destino da relação, exclua a relação e crie-a novamente com os novos gêmeos.

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

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

Veja um exemplo de código que mostra como usar o método. O exemplo usa a chamada ao 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}");
    }

}

Veja um exemplo de chamada ao 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);

Exclusão de relacionamentos

O primeiro parâmetro especifica o gêmeo de origem da relação. O outro parâmetro é a ID da relação. É necessário usar tanto a ID do gêmeo quanto a ID da relação porque as IDs de relação são exclusivas apenas no escopo de um gêmeo.

Veja um exemplo de código que mostra como usar este método. O exemplo usa a chamada ao 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 uma relação desta forma:

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

Observação

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 de gráfico 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 Importar Trabalhos

Você pode usar a API Importar Trabalhos 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 de Gêmeos Digitais do Azure para elementos de gráfico (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. Você não precisa incluir todos os três tipos de dados de gráfico 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 nesse 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 que estão definidos nesse 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 no 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 de API de Trabalhos de Importação. Você fornecerá a URL de armazenamento de blob do arquivo de entrada, bem como uma nova URL de armazenamento de blob para indicar onde você 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 de gêmeos. 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.

Criar 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 mostra como ler os dados de arquivos CSV para criar grafos de gêmeos.

Considere a tabela de dados a seguir, que descreve um conjunto de gêmeos digitais e relações. Os modelos referenciados neste arquivo já devem existir na instância do Gêmeos Digitais do Azure.

ID do modelo ID de gêmeo (deve ser exclusiva) Nome do relacionamento ID do gêmeo de destino Dados de inicialização de gêmeos
dtmi:example:Floor;1 Floor1 contém Room1
dtmi:example:Floor;1 Andar0 contém Room0
dtmi:example:Room;1 Room1 {"Temperature": 80}
dtmi:example:Room;1 Room0 {"Temperature": 70}

Uma maneira de obter esses dados nos Gêmeos Digitais do Azure é converter a tabela em um arquivo CSV. Depois que a tabela é convertida, o código pode ser escrito para interpretar o arquivo em comandos para criar gêmeos e relações. O exemplo de código a seguir mostra como ler os dados do arquivo CSV e criar um grafo de gêmeos nos Gêmeos Digitais do Azure.

No código abaixo, o arquivo CSV é chamado de data.csv e há um espaço reservado que representa o nome do host da instância dos Gêmeos Digitais do Azure. O exemplo também usa vários pacotes que você pode adicionar ao seu projeto para ajudar no 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);
        }
    }
}

Exemplo de grafo de gêmeo executável

O snippet de código executável a seguir usa as operações de relação deste artigo para criar um grafo de gêmeos digitais e relações.

Configurar arquivos de projeto de exemplo

O snippet usa duas definições de modelo de exemplo, Room.json e Floor.json. Para baixar os arquivos de modelo a fim de usá-los no seu código, clique nesses links para acessar os arquivos diretamente no GitHub. Depois, clique com o botão direito do mouse em qualquer lugar na tela, selecione Salvar como no menu de contexto do navegador e use a janela Salvar como para salvar os arquivos como Room.json e Floor.json.

Depois, crie um projeto de aplicativo de console no Visual Studio ou no editor de sua preferência.

Copie o seguinte código do exemplo executável no 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>
    }
}

Observação

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

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

Configurar o projeto

Conclua as seguintes etapas para configurar o código do projeto:

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

  2. Substitua o espaço reservado <your-instance-hostname> pelo nome do host da instância dos Gêmeos Digitais do Azure.

  3. Adicione duas dependências ao seu projeto, que serão necessárias para trabalhar com os Gêmeos Digitais do Azure. A primeira é o pacote do SDK dos Gêmeos Digitais do Azure para .NET e a segunda 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 as credenciais locais se desejar executar a amostra diretamente. A próxima seção descreve esse processo.

Configurar credenciais locais do Azure

Quando você o executa em seu computador local, este exemplo usa o DefaultAzureCredential (parte da biblioteca Azure.Identity) para autenticar usuários com uma instância dos Gêmeos Digitais do Azure. Para obter mais informações sobre as diferentes maneiras como um aplicativo cliente pode se autenticar com os Gêmeos Digitais do Azure, confira Escrever o código de autenticação do aplicativo.

Com DefaultAzureCredential, o exemplo pesquisará as credenciais no ambiente local, como uma conexão do Azure em uma DefaultAzureCredential local ou no Visual Studio ou Visual Studio Code. Por isso, será necessário entrar no Azure localmente por meio de um desses mecanismos para configurar as credenciais do exemplo.

Caso esteja usando o Visual Studio ou o Visual Studio Code para executar exemplos de código, verifique se está conectado a esse editor com as mesmas credenciais do Azure que deseja usar para acessar a instância dos Gêmeos Digitais do Azure. Se estiver usando uma janela da CLI local, execute o comando az login para entrar na sua conta do Azure. Depois disso, quando você executar o exemplo de código, será autenticado automaticamente.

Execute o exemplo

Agora que você concluiu a instalação, execute 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.

Dica

O grafo de gêmeos é um conceito de criação de relações entre gêmeos. Para ver a representação visual do grafo de gêmeos, confira a seção Visualização deste artigo.

Próximas etapas

Saiba mais sobre consultas a grafos de gêmeos dos Gêmeos Digitais do Azure: