Gerenciar gêmeos digitais

As entidades são representadas no ambiente por gêmeos digitais. O gerenciamento de gêmeos digitais pode incluir criação, modificação e remoção.

Este artigo aborda o gerenciamento de gêmeos digitais. Para trabalhar com as relações e o grafo de gêmeo digital como um todo, confira Como gerenciar o grafo de gêmeo digital com relações.

Dica

Todas as funções do SDK são fornecidas em versões síncronas e assíncronas.

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:

Captura de tela do Azure Digital Twins Explorer mostrando modelos e gêmeos de exemplo.

Criar um gêmeo digital

Para criar um gêmeo, use o método CreateOrReplaceDigitalTwinAsync() no cliente de serviço da seguinte maneira:

await client.CreateOrReplaceDigitalTwinAsync<BasicDigitalTwin>(twinId, initData);

Para criar um gêmeo digital, você precisa fornecer:

  • Um valor de ID que você deseja atribuir ao gêmeo digital (define-se essa ID quando o gêmeo é criado)
  • O modelo que deseja usar
  • Qualquer inicialização desejada de dados gêmeos, incluindo...
    • Propriedades (inicialização opcional): você pode definir valores iniciais para as propriedades de um gêmeo digital, se desejar. As propriedades são tratadas como opcionais e podem ser definidas posteriormente, mas observe que elas não aparecerão como parte do gêmeo até que tenham sido definidas.
    • Componentes (inicialização obrigatória se estiverem presentes em um gêmeo): se o seu gêmeo contiver componentes, eles deverão ser inicializados quando o gêmeo for criado. Eles podem ser objetos vazios, mas os próprios componentes precisam existir.

O modelo e todos os valores de propriedade iniciais são fornecidos por meio do parâmetro initData, que é uma cadeia de caracteres JSON que contém os dados relevantes. Para obter mais informações sobre como estruturar o objeto, vá para a próxima seção.

Dica

Depois de criar ou atualizar um gêmeo, pode haver uma latência de até 10 segundos antes que as alterações sejam refletidas nas consultas. Esse atraso não acontece com a API GetDigitalTwin (descrita mais adiante neste artigo). Se você precisa de uma resposta imediata, use a chamada à API em vez da consulta para ver os gêmeos recém-criados.

Inicializar o modelo e as propriedades

Você pode inicializar as propriedades de um gêmeo no momento em que ele é criado.

A API de criação de gêmeo aceita um objeto que é serializado em uma descrição JSON válida das propriedades do gêmeo. Consulte Gêmeos digitais e o grafo de gêmeo para obter uma descrição do formato JSON para um gêmeo.

Você pode criar um objeto de dados para representar o gêmeo e os dados de propriedade. Você pode criar um objeto de parâmetro manualmente ou usando uma classe auxiliar já fornecida. Aqui está um exemplo de cada uma dessas ações.

Criar gêmeos usando dados criados manualmente

Sem o uso de classes auxiliares personalizadas, você pode representar as propriedades de um gêmeo em um Dictionary<string, object>, em que string é o nome da propriedade e object é um objeto que representa a propriedade e seu valor.

// Define a custom model type for the twin to be created

internal class CustomDigitalTwin
{
    [JsonPropertyName(DigitalTwinsJsonPropertyNames.DigitalTwinId)]
    public string Id { get; set; }

    [JsonPropertyName(DigitalTwinsJsonPropertyNames.DigitalTwinETag)]
    public string ETag { get; set; }

    [JsonPropertyName("temperature")]
    public double Temperature { get; set; }

    [JsonPropertyName("humidity")]
    public double Humidity{ get; set; }
}

// Initialize properties and create the twin
public class TwinOperationsCreateTwin
{
    public async Task CreateTwinAsync(DigitalTwinsClient client)
    {
        // Initialize the twin properties
        var myTwin = new CustomDigitalTwin
        {
            Temperature = 25.0,
            Humidity = 50.0,
        };

        // Create the twin
        const string twinId = "<twin-ID>";
        Response<CustomDigitalTwin> response = await client.CreateOrReplaceDigitalTwinAsync(twinId, myTwin);
        Console.WriteLine($"Temperature value: {response.Value.Temperature}");
    }
}

Criar gêmeos com classe auxiliar

A classe auxiliar do BasicDigitalTwin permite que você armazene campos de propriedade em um objeto "gêmeo" diretamente. Talvez você ainda queira criar a lista de propriedades usando um Dictionary<string, object>, que pode ser adicionado ao objeto gêmeo como o seu CustomProperties diretamente.

string twinId = "myTwinID";
var initData = new BasicDigitalTwin
{
    Id = twinId,
    Metadata = { ModelId = "dtmi:example:Room;1" },
    // Initialize properties
    Contents =
    {
        { "Temperature", 25.0 },
        { "Humidity", 50.0 },
    },
};

await client.CreateOrReplaceDigitalTwinAsync<BasicDigitalTwin>(twinId, initData);

Observação

Os objetos BasicDigitalTwin vêm com um campo Id. Esse campo vazio pode ficar vazio, mas caso você adicione um valor de ID, ele precisa corresponder ao parâmetro de ID passado para a chamada CreateOrReplaceDigitalTwinAsync(). Por exemplo:

twin.Id = "myRoomId";

Criar gêmeos em massa com a API de Trabalhos de Importação

Você pode usar a API de Importação de Trabalhos para criar muitos gêmeos ao mesmo tempo em uma única chamada à API. Este método requer o uso do Armazenamento de Blobs do Azure e permissões de gravação em sua instância dos Gêmeos Digitais do Azure para gêmeos e trabalhos em massa.

Dica

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

Para importar gêmeos em massa, você precisa estruturar seus gêmeos (e quaisquer outros recursos incluídos no trabalho de importação em massa) como um arquivo NDJSON. A seção Twins vem após a seção Models (e antes da seção Relationships). Os gêmeos definidos no arquivo podem referenciar modelos definidos neste arquivo ou já presentes na instância e, opcionalmente, podem incluir a inicialização das propriedades do gêmeo.

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

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, confira 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 for carregado no contêiner, obtenha sua URL dentro do contêiner de blob. Você usará esse valor posteriormente no corpo da chamada à API de importação em massa.

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

Captura de tela do portal do Azure que mostra a URL de um arquivo em um contêiner de armazenamento.

Em seguida, o arquivo pode ser usado em uma chamada à API de Importação de Trabalhos. Você fornece a URL de armazenamento de blobs do arquivo de entrada e uma nova URL de armazenamento de blobs para indicar onde deseja que o log de saída seja armazenado depois que ele for criado pelo serviço.

Obter dados para um gêmeo digital

Você pode acessar os detalhes de qualquer gêmeo digital chamando o método GetDigitalTwin() desta forma:

Response<BasicDigitalTwin> twinResponse = await client.GetDigitalTwinAsync<BasicDigitalTwin>(twinId);
twin = twinResponse.Value;

Essa chamada retorna dados de um gêmeo como um tipo de objeto fortemente tipado, como BasicDigitalTwin. BasicDigitalTwin é uma classe auxiliar de serialização incluída no SDK, que retorna os metadados e as propriedades do gêmeo principal em forma pré-preparada. Você sempre pode desserializar dados de gêmeos usando a biblioteca JSON de sua escolha, como System.Text.Json ou Newtonsoft.Json. No entanto, as classes auxiliares podem tornar mais conveniente o acesso básico a um gêmeo.

Observação

BasicDigitalTwin usa atributos System.Text.Json. Para usar BasicDigitalTwin com o DigitalTwinsClient, inicialize o cliente com o construtor padrão ou, se desejar personalizar a opção do serializador, use o JsonObjectSerializer.

A classe auxiliar BasicDigitalTwin também fornece acesso a propriedades definidas no gêmeo por meio de um Dictionary<string, object>. Para listar as propriedades do gêmeo, você pode usar:

BasicDigitalTwin twin;
Response<BasicDigitalTwin> twinResponse = await client.GetDigitalTwinAsync<BasicDigitalTwin>(twinId);
twin = twinResponse.Value;
Console.WriteLine($"Model id: {twin.Metadata.ModelId}");
foreach (string prop in twin.Contents.Keys)
{
    if (twin.Contents.TryGetValue(prop, out object value))
        Console.WriteLine($"Property '{prop}': {value}");
}

Somente as propriedades que foram definidas pelo menos uma vez são retornadas quando você recupera um gêmeo com o método GetDigitalTwin().

Dica

O displayName faz parte do metadados de modelo do gêmeo e não vai ser exibido ao obter dados para a instância do gêmeo. Para definir esse valor, você pode recuperá-lo por meio do modelo.

Para recuperar vários gêmeos usando uma única chamada à API, confira os exemplos de API de Consulta em Consultar o grafo gêmeo.

Considere o seguinte modelo (escrito em DTDL (Linguagem de Definição de Gêmeos Digitais)) que define um moon:

{
    "@id": "dtmi:example:Moon;1",
    "@type": "Interface",
    "@context": "dtmi:dtdl:context;3",
    "contents": [
        {
            "@type": "Property",
            "name": "radius",
            "schema": "double",
            "writable": true
        },
        {
            "@type": "Property",
            "name": "mass",
            "schema": "double",
            "writable": true
        }
    ]
}

O resultado de chamar object result = await client.GetDigitalTwinAsync("my-moon"); em um gêmeo do tipo Lua pode ter esta aparência:

{
  "$dtId": "myMoon-001",
  "$etag": "W/\"e59ce8f5-03c0-4356-aea9-249ecbdc07f9\"",
  "radius": 1737.1,
  "mass": 0.0734,
  "$metadata": {
    "$model": "dtmi:example:Moon;1",
    "radius": {
      "lastUpdateTime": "2022-12-06T20:00:32.8209188Z"
    },
    "mass": {
      "lastUpdateTime": "2022-12-04T12:04:43.3859361Z"
    }
  }
}

As propriedades definidas do gêmeo digital são retornadas como propriedades de nível superior no gêmeo digital. As informações de metadados ou do sistema que não fazem parte da definição DTDL são retornadas com um prefixo $. As propriedades de metadados incluem os seguintes valores:

  • $dtId: ID do gêmeo digital na instância dos Gêmeos Digitais do Azure
  • $etag: campo HTTP padrão atribuído pelo servidor Web. Ele é atualizado para um novo valor sempre que o gêmeo é atualizado, o que pode ser útil para determinar se o dado do gêmeo foi atualizado no servidor desde a última verificação. Você pode usar If-Match para executar atualizações e exclusões que só serão concluídas se a etag da entidade corresponder à etag fornecida. Para obter mais informações sobre essas operações, consulte a documentação para Atualização de DigitalTwins e para Exclusão de DigitalTwins.
  • $metadata: um conjunto de propriedades de metadados, que pode incluir o seguinte:
    • $model, o DTMI do modelo do gêmeo digital.
    • lastUpdateTime para propriedades gêmeas. Este é um carimbo de data/hora que indica a data e hora em que os Gêmeos Digitais do Azure processaram a mensagem de atualização de propriedade
    • sourceTime para propriedades gêmeas. Esta é uma propriedade opcional e gravável que representa o carimbo de data/hora de quando a atualização da propriedade foi observada no mundo real.

Você pode ler mais sobre os campos contidos em um gêmeo digital no formato JSON do gêmeo digital. Você pode ler mais sobre as classes auxiliares de serialização como a BasicDigitalTwin em APIs e SDKs dos Gêmeos Digitais do Azure.

Exibir todos os gêmeos digitais

Para exibir todos os gêmeos digitais na instância, use uma consulta. Você pode executar uma consulta com as APIs de consulta ou os Comandos da CLI.

Aqui está o corpo da consulta básica que retorna uma lista de todos os gêmeos digitais na instância:

SELECT * FROM DIGITALTWINS

Atualizar um gêmeo digital

Para atualizar as propriedades de um gêmeo digital, escreva as informações que deseja substituir no formato de Patch JSON. Para obter uma lista completa de operações do patch JSON que podem ser usadas, incluindo replace, add e remove, confira Operações para patch JSON.

Depois de criar o documento de patch JSON que contém informações de atualização, passe o documento para o método UpdateDigitalTwin():

await client.UpdateDigitalTwinAsync(twinId, updateTwinData);

Uma chamada de patch única pode atualizar quantas propriedades de um gêmeo você desejar (inclusive todas elas). Se você precisar atualizar as propriedades de vários gêmeos, será necessária uma chamada de atualização separada para cada gêmeo.

Dica

Depois de criar ou atualizar um gêmeo, pode haver uma latência de até 10 segundos antes que as alterações sejam refletidas nas consultas. Esse atraso não acontece com a API GetDigitalTwin (descrita anteriormente neste artigo). Se você precisa de uma resposta imediata, use a chamada à API em vez da consulta para ver os gêmeos recém-atualizados.

Este é um exemplo de código de Patch JSON. Este documento substitui os valores de propriedade mass e radius do gêmeo digital no qual é aplicado. Esse exemplo mostra a operação replace do patch JSON, que substitui o valor de uma propriedade existente.

[
    {
      "op": "replace",
      "path": "/mass",
      "value": 0.0799
    },
    {
      "op": "replace",
      "path": "/radius",
      "value": 0.800
    }
  ]

Ao atualizar um gêmeo a partir de um projeto de código usando o SDK do .NET, você pode criar patches JSON usando o JsonPatchDocument do SDK do .NET do Azure. Aqui está um exemplo de criação de um documento de Patch JSON e do uso de UpdateDigitalTwin() no código do projeto.

var updateTwinData = new JsonPatchDocument();
updateTwinData.AppendAdd("/Temperature", 25.0);
updateTwinData.AppendAdd("/myComponent/Property", "Hello");
// Un-set a property
updateTwinData.AppendRemove("/Humidity");

await client.UpdateDigitalTwinAsync("myTwin", updateTwinData).ConfigureAwait(false);

Dica

Você pode manter carimbos de data/hora de origem em seus gêmeos digital atualizando o campo $metadata.<property-name>.sourceTime com o processo descrito nesta seção. Para obter mais informações sobre esse campo e outros campos que podem ser gravados em gêmeos digitais, consulte Formato JSON de gêmeo digital.

Atualizar subpropriedades em componentes do gêmeo digital

Lembre-se de que um modelo pode conter componentes, o que permite que ele seja composto por outros modelos.

Para aplicar as propriedades de patch em um dos componentes do gêmeo digital, você pode usar a sintaxe de patch no Patch JSON:

[
  {
    "op": "replace",
    "path": "/mycomponentname/mass",
    "value": 0.0799
  }
]

Atualizar subpropriedades em propriedades de tipo de objeto

Os modelos podem conter propriedades que são de um tipo de objeto. Esses objetos podem ter suas próprias propriedades e talvez você queira atualizar uma dessas sub-propriedades pertencentes à propriedade do tipo objeto. Esse processo é semelhante ao processo de atualização de sub-propriedades em componentes, mas pode exigir algumas etapas adicionais.

Considere um modelo com uma propriedade de tipo de objeto, ObjectProperty. O ObjectProperty tem uma propriedade de cadeia de caracteres chamada StringSubProperty.

Quando um gêmeo é criado usando esse modelo, não é necessário criar uma instância do ObjectProperty no momento. Se não for criada uma instância da propriedade do objeto durante a criação do gêmeo, não haverá nenhum caminho padrão criado para acessar o ObjectProperty e o respectivo StringSubProperty para uma operação de patch. Você mesmo precisa adicionar o caminho ObjectProperty antes de atualizar suas propriedades.

Isso pode ser feito com uma operação add de patch JSON, desta forma:

[
  {
    "op": "add", 
    "path": "/ObjectProperty", 
    "value": {"StringSubProperty":"<string-value>"}
  }
]

Observação

Se ObjectProperty tiver mais de uma propriedade, você deverá incluir todas elas no campo value dessa operação, mesmo que esteja atualizando apenas uma:

... "value": {"StringSubProperty":"<string-value>", "Property2":"<property2-value>", ...}

Depois que isso tiver sido feito uma vez, existe um caminho para StringSubProperty e ele pode ser atualizado diretamente de agora em diante com uma operação replace típica:

[
  {
    "op": "replace",
    "path": "/ObjectProperty/StringSubProperty",
    "value": "<string-value>"
  }
]

Embora a primeira etapa não seja necessária nos casos em que ObjectProperty foi instanciado quando o gêmeo foi criado, é recomendável usá-la sempre que você atualizar uma sub-propriedade pela primeira vez, já que você pode nem sempre saber com certeza se a propriedade do objeto foi inicialmente instanciada ou não.

Atualizar um modelo de gêmeo digital

A função UpdateDigitalTwin() também pode ser usada para migrar um gêmeo digital para um modelo diferente.

Por exemplo, considere o seguinte documento de Patch JSON que substitui o campo $model dos metadados do gêmeo digital:

[
  {
    "op": "replace",
    "path": "/$metadata/$model",
    "value": "dtmi:example:foo;1"
  }
]

Esta operação só terá sucesso se o gêmeo digital que está sendo modificado pelo patch estiver em conformidade com o novo modelo.

Considere o seguinte exemplo:

  1. Imagine um gêmeo digital com um modelo de foo_old. foo_old define uma massa de propriedade necessária.
  2. O novo modelo foo_new define uma massa de propriedade e adiciona uma nova temperatura de propriedade necessária.
  3. Depois do patch, o gêmeo digital deve ter a propriedade mass e a propriedade temperature.

O patch para essa situação precisa atualizar a propriedade temperature do gêmeo e do modelo desta forma:

[
  {
    "op": "replace",
    "path": "/$metadata/$model",
    "value": "dtmi:example:foo_new;1"
  },
  {
    "op": "add",
    "path": "/temperature",
    "value": 60
  }
]

Atualizar o sourceTime de uma propriedade

Opcionalmente, você pode decidir usar o campo sourceTime em propriedades gêmeas para registrar carimbos de data/hora para quando as atualizações de propriedade forem observadas no mundo real. Os Gêmeos Digitais do Azure dão suporte nativamente a sourceTime nos metadados para cada propriedade gêmea. O valor sourceTime deve estar em conformidade com o formato de data e hora ISO 8601. Para obter mais informações sobre esse campo e outros campos em gêmeos digitais, consulte Formato JSON de gêmeo digital.

A versão mínima da API REST estável para dar suporte a esse campo é a versão de 31-05-2022. Para trabalhar com esse campo usando os SDKs dos Gêmeos Digitais do Azure, recomendamos usar a versão mais recente do SDK para garantir que esse campo esteja incluído.

Aqui está um exemplo de um documento de patch JSON que atualiza o valor e o campo sourceTime de uma propriedade Temperature:

[
  {
    "op": "replace",
    "path": "/Temperature",
    "value": "22.3"
  },
  {
    "op": "replace",
    "path": "/$metadata/Temperature/sourceTime",
    "value": "2021-11-30T18:47:53.7648958Z"
  }
]

Para atualizar o campo sourceTime em uma propriedade que faz parte de um componente, inclua o componente no início do caminho. No exemplo acima, você faria isso alterarando o valor do caminho de /$metadata/Temperature/sourceTime para myComponent/$metadata/Temperature/sourceTime.

Observação

Se você atualizar o valor e sourceTime em uma propriedade e, posteriormente, atualizar apenas o valor da propriedade, o carimbo de data/hora sourceTime da primeira atualização permanecerá.

Lidar com chamadas de atualização conflitantes

Os Gêmeos Digitais do Azure garantem que todas as solicitações de entrada sejam processadas uma após a outra. Isso significa que, mesmo que várias funções tentem atualizar a mesma propriedade em um gêmeo ao mesmo tempo, não há necessidade de gravar um código de bloqueio explícito para lidar com o conflito.

Esse comportamento acontece em cada gêmeo.

Como exemplo, imagine um cenário no qual estas três chamadas chegam ao mesmo tempo:

  • Gravar a propriedade A no Gêmeo1
  • Gravar a propriedade B no Gêmeo1
  • Gravar a propriedade A no Gêmeo2

As duas chamadas que modificam o Gêmeo1 são executadas uma após a outra e as mensagens de alteração são geradas para cada alteração. A chamada para modificar o Twin2 pode ser executada simultaneamente sem conflitos, assim que ela chegar.

Excluir um gêmeo digital

Você pode excluir gêmeos usando o método DeleteDigitalTwin(). No entanto, você só pode excluir um gêmeo quando ele não tiver mais relações. Exclua primeiro as relações de entrada e saída do gêmeo.

Aqui está um exemplo do código para excluir gêmeos e seus relacionamentos. A chamada do SDK DeleteDigitalTwin é realçada para esclarecer onde ela se encontra no contexto de exemplo mais amplo.

private static async Task CustomMethod_DeleteTwinAsync(DigitalTwinsClient client, string twinId)
{
    await CustomMethod_FindAndDeleteOutgoingRelationshipsAsync(client, twinId);
    await CustomMethod_FindAndDeleteIncomingRelationshipsAsync(client, twinId);
    try
    {
        await client.DeleteDigitalTwinAsync(twinId);
        Console.WriteLine("Twin deleted successfully");
    }
    catch (RequestFailedException ex)
    {
        Console.WriteLine($"*** Error:{ex.Message}");
    }
}

private static async Task CustomMethod_FindAndDeleteOutgoingRelationshipsAsync(DigitalTwinsClient client, string dtId)
{
    // Find the relationships for the twin

    try
    {
        // GetRelationshipsAsync will throw an error if a problem occurs
        AsyncPageable<BasicRelationship> rels = client.GetRelationshipsAsync<BasicRelationship>(dtId);

        await foreach (BasicRelationship rel in rels)
        {
            await client.DeleteRelationshipAsync(dtId, rel.Id).ConfigureAwait(false);
            Console.WriteLine($"Deleted relationship {rel.Id} from {dtId}");
        }
    }
    catch (RequestFailedException ex)
    {
        Console.WriteLine($"*** Error {ex.Status}/{ex.ErrorCode} retrieving or deleting relationships for {dtId} due to {ex.Message}");
    }
}

private static async Task CustomMethod_FindAndDeleteIncomingRelationshipsAsync(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);

        await foreach (IncomingRelationship incomingRel in incomingRels)
        {
            await client.DeleteRelationshipAsync(incomingRel.SourceId, incomingRel.RelationshipId).ConfigureAwait(false);
            Console.WriteLine($"Deleted incoming relationship {incomingRel.RelationshipId} from {dtId}");
        }
    }
    catch (RequestFailedException ex)
    {
        Console.WriteLine($"*** Error {ex.Status}/{ex.ErrorCode} retrieving or deleting incoming relationships for {dtId} due to {ex.Message}");
    }
}

Excluir todos os gêmeos digitais

Para obter um exemplo de como excluir todos os gêmeos de uma vez, baixe o aplicativo de amostra usado em Explorar os conceitos básicos com um aplicativo cliente de amostra. O arquivo CommandLoop.cs faz isso em uma função CommandDeleteAllTwins().

Observação

Se você quiser excluir todos os modelos, gêmeos e relações em uma instância de uma só vez, use a API de Exclusão de Trabalhos.

Exemplo de código de gêmeo digital executável

Você pode usar o exemplo de código executável abaixo para criar, atualizar os detalhes e excluir um gêmeo.

Configurar arquivos de projeto de exemplo

O snippet usa uma definição de modelo de exemplo, Room.json. Para baixar o arquivo de modelo para que você possa usá-lo no seu código, use este link para acessar diretamente o arquivo no GitHub. Depois, clique com o botão direito do mouse em qualquer lugar na tela, selecione Salvar como no menu ativado pelo botão direito do mouse do navegador e use a janela Salvar como para salvar o arquivo como Room.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.Collections.Generic;
using Azure;
using Azure.DigitalTwins.Core;
using Azure.Identity;
using System.IO;

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

            // Create the Azure Digital Twins client for API calls
            string adtInstanceUrl = "https://<your-instance-hostname>";
            var credentials = new DefaultAzureCredential();
            var client = new DigitalTwinsClient(new Uri(adtInstanceUrl), credentials);
            Console.WriteLine($"Service client created – ready to go");

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

            // Create new digital twin
            // <CreateTwin_withHelper>
            string twinId = "myTwinID";
            var initData = new BasicDigitalTwin
            {
                Id = twinId,
                Metadata = { ModelId = "dtmi:example:Room;1" },
                // Initialize properties
                Contents =
                {
                    { "Temperature", 25.0 },
                    { "Humidity", 50.0 },
                },
            };

            // <CreateTwinCall>
            await client.CreateOrReplaceDigitalTwinAsync<BasicDigitalTwin>(twinId, initData);
            // </CreateTwinCall>
            // </CreateTwin_withHelper>
            Console.WriteLine("Twin created successfully");

            //Print twin
            Console.WriteLine("--- Printing twin details:");
            await CustomMethod_FetchAndPrintTwinAsync(twinId, client);
            Console.WriteLine("--------");

            //Update twin data
            var updateTwinData = new JsonPatchDocument();
            updateTwinData.AppendAdd("/Temperature", 30.0);
            // <UpdateTwinCall>
            await client.UpdateDigitalTwinAsync(twinId, updateTwinData);
            // </UpdateTwinCall>
            Console.WriteLine("Twin properties updated");
            Console.WriteLine();

            //Print twin again
            Console.WriteLine("--- Printing twin details (after update):");
            await CustomMethod_FetchAndPrintTwinAsync(twinId, client);
            Console.WriteLine("--------");
            Console.WriteLine();

            //Delete twin
            await CustomMethod_DeleteTwinAsync(client, twinId);
        }

        private static async Task<BasicDigitalTwin> CustomMethod_FetchAndPrintTwinAsync(string twinId, DigitalTwinsClient client)
        {
            // <GetTwin>
            BasicDigitalTwin twin;
            // <GetTwinCall>
            Response<BasicDigitalTwin> twinResponse = await client.GetDigitalTwinAsync<BasicDigitalTwin>(twinId);
            twin = twinResponse.Value;
            // </GetTwinCall>
            Console.WriteLine($"Model id: {twin.Metadata.ModelId}");
            foreach (string prop in twin.Contents.Keys)
            {
                if (twin.Contents.TryGetValue(prop, out object value))
                    Console.WriteLine($"Property '{prop}': {value}");
            }
            // </GetTwin>

            return twin;
        }

        // <DeleteTwin>
        private static async Task CustomMethod_DeleteTwinAsync(DigitalTwinsClient client, string twinId)
        {
            await CustomMethod_FindAndDeleteOutgoingRelationshipsAsync(client, twinId);
            await CustomMethod_FindAndDeleteIncomingRelationshipsAsync(client, twinId);
            try
            {
                await client.DeleteDigitalTwinAsync(twinId);
                Console.WriteLine("Twin deleted successfully");
            }
            catch (RequestFailedException ex)
            {
                Console.WriteLine($"*** Error:{ex.Message}");
            }
        }

        private static async Task CustomMethod_FindAndDeleteOutgoingRelationshipsAsync(DigitalTwinsClient client, string dtId)
        {
            // Find the relationships for the twin

            try
            {
                // GetRelationshipsAsync will throw an error if a problem occurs
                AsyncPageable<BasicRelationship> rels = client.GetRelationshipsAsync<BasicRelationship>(dtId);

                await foreach (BasicRelationship rel in rels)
                {
                    await client.DeleteRelationshipAsync(dtId, rel.Id).ConfigureAwait(false);
                    Console.WriteLine($"Deleted relationship {rel.Id} from {dtId}");
                }
            }
            catch (RequestFailedException ex)
            {
                Console.WriteLine($"*** Error {ex.Status}/{ex.ErrorCode} retrieving or deleting relationships for {dtId} due to {ex.Message}");
            }
        }

        private static async Task CustomMethod_FindAndDeleteIncomingRelationshipsAsync(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);

                await foreach (IncomingRelationship incomingRel in incomingRels)
                {
                    await client.DeleteRelationshipAsync(incomingRel.SourceId, incomingRel.RelationshipId).ConfigureAwait(false);
                    Console.WriteLine($"Deleted incoming relationship {incomingRel.RelationshipId} from {dtId}");
                }
            }
            catch (RequestFailedException ex)
            {
                Console.WriteLine($"*** Error {ex.Status}/{ex.ErrorCode} retrieving or deleting incoming relationships for {dtId} due to {ex.Message}");
            }
        }
        // </DeleteTwin>

    }
}

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 o arquivo Room.json que você já baixou no projeto e substitua o espaço reservado <path-to> no código para informar ao programa onde encontrá-lo.

  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 projeto 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 precisa configurar credenciais locais se quiser executar o exemplo diretamente. A próxima seção descreve isso.

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 a instalação está concluída, você pode executar o projeto de código de exemplo.

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

Captura de tela da saída do console mostrando que o gêmeo foi criado, atualizado e excluído.

Próximas etapas

Consulte como criar e gerenciar relações entre os gêmeos digitais: