デジタル ツインを管理する

環境内のエンティティは、デジタル ツインで表されます。 デジタル ツインの管理には、作成、変更、削除が含まれる場合があります。

この記事では、デジタル ツインの管理に重点を置いて説明します。リレーションシップとツイン グラフの全体的な操作については、「ツイン グラフとリレーションシップを管理する」をご覧ください。

ヒント

すべての SDK 関数に同期バージョンと非同期バージョンがあります。

前提条件

この記事で Azure Digital Twins を操作するには、まず Azure Digital Twins インスタンスとそれを使用するために必要なアクセス許可が必要です。 Azure Digital Twins インスタンスが既に設定されている場合は、そのインスタンスを使用し、次のセクションに進むことができます。 それ以外の場合は、「インスタンスと認証を設定する」の手順に従います。 手順には、各ステップを正常に完了したことを確認するために役立つ情報が含まれています。

インスタンスの設定後、インスタンスのホスト名を書き留めておきます。 ホスト名は Azure portal で確認できます。

開発者インターフェイス

この記事では、.NET (C#) SDK を使用して、さまざまな管理操作を実行する方法について説明します。 また、「Azure Digital Twins API および SDK」で説明されているその他の言語の SDK を使用して、これらと同じ管理呼び出しを作成することもできます。

これらの操作を完了するために使用できるその他の開発者インターフェイスは次のとおりです。

ビジュアル化

Azure Digital Twins Explorer は、Azure Digital Twins グラフ内のデータを探索するためのビジュアル ツールです。 エクスプローラーを使用して、モデル、ツイン、リレーションシップを表示、クエリ、編集できます。

Azure Digital Twins Explorer ツールについて詳しくは、Azure Digital Twins Explorer に関するページを参照してください。 その機能を使用する方法の詳細な手順については、Azure Digital Twins Explorer を使用するに関するページを参照してください。

視覚化は次のように表示されます。

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

デジタル ツインを作成する

ツインを作成するには、次のようにサービス クライアントで CreateOrReplaceDigitalTwinAsync() メソッドを使用します。

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

デジタル ツインを作成するには、以下を指定する必要があります。

  • デジタル ツインに割り当てる ID 値 (ツインの作成時にその ID を定義する)
  • 使用するモデル
  • ツイン データの任意の初期化 (例: ...
    • プロパティ (初期化オプション): 必要に応じて、デジタル ツインのプロパティの初期値を設定できます。 プロパティはオプションとして扱われ、後で設定できますが、設定されるまではツインの一部として表示されません
    • コンポーネント (ツインに存在する場合は初期化が必要): ツインにコンポーネントが含まれている場合は、ツインの作成時に初期化する必要があります。 空のオブジェクトにすることができますが、コンポーネント自体は存在する必要があります。

モデルと初期プロパティ値は、initData パラメーターによって提供されます。これは、関連データを含む JSON 文字列です。 このオブジェクトを構造化する方法の詳細については、次のセクションに進んでください。

ヒント

ツインを作成または更新した後、変更がクエリに反映されるまでに最大 10 秒の待機時間が発生する可能性があります。 GetDigitalTwin API (この記事で後ほど説明します) ではこの遅延が発生しないため、迅速な応答が必要な場合は、クエリの代わりに API 呼び出しを使用して、新しく作成されたツインを確認してください。

モデルとプロパティを初期化する

ツインの作成時、ツインのプロパティを初期化できます。

ツイン作成 API は、ツイン プロパティの有効な JSON 記述にシリアル化されるオブジェクトを受け入れます。 ツインの JSON 形式の説明については、デジタル ツインとツイン グラフに関する記事をご覧ください。

まず、ツインとそのプロパティ データを表すデータ オブジェクトを作成することができます。 パラメーター オブジェクトは手動で作成することも、用意されているヘルパー クラスを使用して作成することもできます。 それぞれの例を次に示します。

手動で作成したデータを使用してツインを作成する

カスタム ヘルパー クラスを使用しない場合は、Dictionary<string, object> でツインのプロパティを表すことができます。ここで、string はプロパティの名前であり、object はプロパティとその値を表すオブジェクトです。

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

ヘルパー クラスを使用してツインを作成する

BasicDigitalTwin のヘルパー クラスを使用すると、"ツイン" オブジェクトにプロパティ フィールドを直接的に格納できます。 引き続き、ツイン オブジェクトCustomPropertiesに直接追加できるプロパティの一覧を作成Dictionary<string, object>することもできます。

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

Note

BasicDigitalTwin オブジェクトには、Id フィールドが付属しています。 このフィールドは空のままとすることができますが、ID 値を追加する場合は、それが、CreateOrReplaceDigitalTwinAsync() 呼び出しに渡される ID パラメーターと一致する必要があります。 次に例を示します。

twin.Id = "myRoomId";

インポート ジョブ API を使用してツインを一括で作成する

Import Jobs API を 使用すると、1 回の API 呼び出しで多数のツインを一度に作成できます。 この方法では、Azure Blob Storage を使用し、ツインと一括ジョブに対する Azure Digital Twins インスタンスの書き込みアクセス許可を必要とします。

ヒント

Import Jobs API では、モデルとリレーションシップを同じ呼び出しでインポートして、グラフのすべての部分を一度に作成することもできます。 このプロセスの詳細については、「Import Jobs API を使用してモデル、ツイン、リレーションシップを一括でアップロードする」を参照してください

ツインを一括インポートするには、ツイン (および一括インポート ジョブに含まれるその他のリソース) を NDJSON ファイルとして構成する必要があります。 セクションは Twins セクションの後 (およびセクションの Models 前) にあります Relationships 。 ファイルで定義されているツインは、このファイルで定義されているか、インスタンスに既に存在するモデルを参照でき、必要に応じてツインのプロパティの初期化を含めることができます。

インポート ファイルの例と、これらのファイルを作成するためのサンプル プロジェクトについては、インポート ジョブ API の 概要を参照してください。

次に、Azure Blob Storage 内の追加 BLOB にファイルをアップロードする必要があります。 Azure ストレージ コンテナーを作成する方法の手順については、「コンテナーを作成する」を参照してください。 次に、ご希望のアップロード方法を使用してファイルをアップロードします (AzCopy コマンドAzure CLIAzure portal などのオプションがあります)。

NDJSON ファイルがコンテナーにアップロードされたら、BLOB コンテナー内で URL を取得します。 この値は、後で一括インポート API 呼び出しの本文で使用します。

Azure portal 内の BLOB ファイルの URL 値のスクリーンショットを次に示します。

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

その後、インポート ジョブ API 呼び出しでファイルを使用できます。 入力ファイルの BLOB ストレージ URL と、サービスが作成した後に出力ログを格納する場所を示す新しい BLOB ストレージ URL を指定します。

デジタル ツインのデータを取得する

次のように GetDigitalTwin() メソッドを呼び出すことで、デジタル ツインの詳細にアクセスすることができます。

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

この呼び出しからは、ツイン データが BasicDigitalTwin のような厳密に型指定されたオブジェクト型として返されます。 BasicDigitalTwin は SDK に含まれるシリアル化ヘルパー クラスで、準備された形式でコア ツインのメタデータとプロパティを返します。 System.Text.JsonNewtonsoft.Json といった任意の JSON ライブラリを使用して、ツイン データをいつでも逆シリアル化できます。 ただし、ツインへの基本的なアクセスについては、ヘルパー クラスを使用すると便利です。

Note

BasicDigitalTwin では System.Text.Json 属性が使用されます。 BasicDigitalTwinDigitalTwinsClient で使用するためには、既定のコンストラクターを使用してクライアントを初期化する必要があります。または、シリアライザー オプションをカスタマイズする場合は、JsonObjectSerializer を使用します。

また、BasicDigitalTwin ヘルパー クラスを使用すると、ツインで定義されたプロパティに Dictionary<string, object> を介してアクセスできます。 ツインのプロパティを一覧表示するには、次のコードを使用します。

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

GetDigitalTwin() メソッドを使用してツインを取得すると、少なくとも 1 回は設定されたプロパティだけが返されます。

ヒント

ツインの displayName はモデル メタデータの一部であるため、ツイン インスタンスのデータを取得するときには表示されません。 この値を表示するには、モデルから取得することができます。

1 つの API 呼び出しを使用して複数のツインを取得するには、「ツインのクエリ」グラフクエリ API の例を参照してください。

Moon を定義する次のモデル (Digital Twins Definition Language (DTDL) で記述) について考えてみましょう。

{
    "@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
        }
    ]
}

Moon 型ツインを呼び出した object result = await client.GetDigitalTwinAsync("my-moon"); 結果は、次のようになります。

{
  "$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"
    }
  }
}

デジタル ツインの定義済みプロパティは、デジタル ツインの最上位プロパティとして返されます。 DTDL 定義に含まれていないメタデータやシステム情報は、$ プレフィックス付きで返されます。 メタデータ プロパティには以下の値が含まれます。

  • $dtId: この Azure Digital Twins インスタンスのデジタル ツインの ID
  • $etag: Web サーバーによって割り当てられた標準 HTTP フィールド。 これはツインが更新されるたびに新しい値に更新されます。これは、前回のチェックからサーバー上でツインのデータが更新されているかどうかを判断するのに役立ちます。 If-Match を使用すると、 エンティティの etag が指定された etag と一致する場合にのみ完了する更新と削除を実行できます。 これらの操作の詳細については、 DigitalTwins UpdateDigitalTwins Deleteのドキュメントを参照してください。
  • $metadata: メタデータ プロパティのセット。これには次のものが含まれます。
    • $model、デジタル ツインのモデルの DTMI。
    • lastUpdateTime ツインのプロパティの場合は 〗。 これは、Azure Digital Twins がプロパティの更新メッセージを処理した日時を示すタイムスタンプです
    • sourceTime ツインのプロパティの場合は 〗。 これは、プロパティの更新が実際に観察されたときのタイムスタンプを表す、省略可能な書き込み可能なプロパティです。

デジタル ツインに含まれるフィールドの詳細については、Digital Twin JSON 形式確認できます。 BasicDigitalTwin などのシリアル化ヘルパー クラスの詳細については、「Azure Digital Twins API と SDK」で参照してください。

すべてのデジタル ツインを表示する

インスタンス内のすべてのデジタル ツインを表示するには、クエリを使用します。 クエリは、Query API または CLI コマンドを使用して実行できます。

インスタンス内のすべてのデジタル ツインの一覧を返す基本的なクエリの本文を次に示します。

SELECT * FROM DIGITALTWINS

デジタル ツインを更新する

デジタル ツインのプロパティを更新するには、置き換える情報を JSON Patch 形式で書き込みます。 replaceaddremove などの使用可能な JSON Patch 操作の詳細なリストについては、「JSON Patch の操作」を参照してください。

更新情報を含む JSON Patch ドキュメントを作成した後、ドキュメントを UpdateDigitalTwin() メソッドに渡します。

await client.UpdateDigitalTwinAsync(twinId, updateTwinData);

パッチを 1 度呼び出すだけで、1 つのツインのプロパティを必要な数だけ (すべてであっても) 更新することができます。 複数のツイン間でプロパティを更新する必要がある場合は、ツインごとに個別の更新呼び出しが必要です。

ヒント

ツインを作成または更新した後、変更がクエリに反映されるまでに最大 10 秒の待機時間が発生する可能性があります。 GetDigitalTwin API (この記事の前の方で説明しました) ではこの遅延が発生しないため、迅速な応答が必要な場合は、クエリの代わりに API 呼び出しを使用して、新しく更新されたツインを確認してください。

JSON Patch コードの例を次に示します。 このドキュメントでは、適用先のデジタル ツインの mass および radius プロパティ値を置き換えます。 この例では、既存のプロパティの値を置き換える JSON Patch replace 操作を示しています。

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

.NET SDK を使用してコードプロジェクトからツインを更新する場合は、Azure .NET SDK の Jsonpatchdocument を使用して JSON 修正プログラムを作成できます。 JSON Patch ドキュメントを作成し、プロジェクト コードで UpdateDigitalTwin() を使用する例を次に示します。

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

ヒント

このセクションで説明するプロセスを使用して $metadata.<property-name>.sourceTime フィールドを更新することで、デジタル ツインのソース タイムスタンプを維持できます。 このフィールドとデジタル ツインで書き込み可能なその他のフィールドの詳細については、「デジタル ツインの JSON 形式」を参照してください。

デジタル ツイン コンポーネントのサブプロパティを更新する

モデルにコンポーネントが含まれている可能性があり、他のモデルで構成されることを思い出してください。

デジタル ツインのコンポーネントのプロパティにパッチを適用するには、JSON Patch で path 構文を使用することができます。

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

オブジェクト型プロパティのサブプロパティを更新する

モデルには、オブジェクト型のプロパティが含まれている場合があります。 これらのオブジェクトには独自のプロパティがあり、オブジェクト型プロパティに属するサブプロパティの 1 つを更新できます。 このプロセスは、コンポーネントのサブプロパティを更新するプロセスに似ていますが、追加の手順が必要になる場合があります。

オブジェクト型プロパティ ObjectProperty を持つモデルについて検討してみましょう。 ObjectProperty には StringSubProperty という名前の文字列プロパティがあります。

このモデルを使用してツインを作成する場合、その時点で ObjectProperty をインスタンス化する必要はありません。 ツインの作成中にオブジェクト プロパティがインスタンス化されない場合、パッチ操作用の ObjectProperty とその StringSubProperty にアクセスするための既定のパスは作成されません。 プロパティを更新する前に、自分に ObjectProperty パスを追加する必要があります。

これは、次のように JSON Patch add 操作 を使用して実行できます。

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

Note

ObjectProperty に複数のプロパティがある場合は、更新するものが 1 つだけのときでも、この操作の value フィールドにそれらすべてを含める必要があります。

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

これが 1 回実行された後は、StringSubProperty へのパスが存在するため、以降は通常の replace 操作で直接更新できます。

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

ツインの作成時にインスタンス化された場合 ObjectProperty は最初の手順は必要ありませんが、最初にサブプロパティを更新するたびに使用することをお勧めします。これは、オブジェクト プロパティが最初にインスタンス化されたかどうかが常に確実にわからない可能性があるためです。

デジタル ツインのモデルを更新する

UpdateDigitalTwin() 関数を使用して、デジタル ツインを別のモデルに移行することもできます。

たとえば、デジタル ツインのメタデータの $model フィールドを置き換える、次の JSON Patch ドキュメントについて考えてみましょう。

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

この操作は、修正プログラムによって変更されるデジタル ツインが新しいモデルに準拠している場合にのみ成功します。

次の例を考えてみましょう。

  1. foo_old というモデルを使用するデジタル ツインがあるとします。 foo_oldは、必要なプロパティ の質量を定義します
  2. 新しいモデルfoo_newプロパティ質量を定義し、新しい必須プロパティ 温度を追加します。
  3. パッチの適用後、このデジタル ツインには mass と temperature の両方のプロパティが含まれている必要があります。

この場合のパッチでは、次のように、モデルとツインの temperature プロパティを両方とも更新する必要があります。

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

プロパティの sourceTime を更新する

必要に応じて、ツイン プロパティのフィールドを sourceTime 使用して、実際にプロパティの更新が行われるタイミングのタイムスタンプを記録することもできます。 Azure Digital Twins は、各ツイン プロパティのメタデータの sourceTime をネイティブにサポートします。 sourceTime 値は、ISO 8601 の日付と時刻の形式に準拠している必要があります。 このフィールドとデジタル ツインのその他のフィールドの詳細については、「デジタル ツインの JSON 形式」を参照してください。

このフィールドをサポートする REST API の最小の安定バージョンは、2022-05-31 バージョンです。 Azure Digital Twins SDK を使用してこのフィールドを操作するには、最新バージョンの SDK を使用して、このフィールドが含まれていることを確認することをお勧めします。

Temperature プロパティの値と sourceTime フィールドの両方を更新する JSON Patch ドキュメントの例を次に示します。

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

コンポーネントの一部であるプロパティの sourceTime フィールドを更新するには、パスの最初にコンポーネントを含める必要があります。 上の例では、パス値を /$metadata/Temperature/sourceTime から myComponent/$metadata/Temperature/sourceTime に変更することでこれを実施しています。

Note

sourceTime とプロパティの値の両方を更新し、その後、プロパティの値だけを更新した場合、最初の更新からの sourceTime タイムスタンプが残ります。

競合する更新呼び出しを処理する

Azure Digital Twins では、すべての受信要求が確実に 1 つずつ処理されます。 つまり、複数の関数が同時にツイン上の同じプロパティを更新しようとする場合でも、競合を処理するための明示的なロック コードを記述する必要はありません

この動作はツインごとに行われます。

例として、これら 3 つの呼び出しが同時に到着するシナリオを考えてみましょう。

  • Twin1 でのプロパティ A の書き込み
  • Twin1 でのプロパティ B の書き込み
  • Twin2 でのプロパティ A の書き込み

Twin1 を変更する 2 つの呼び出しが 1 つずつ実行され、変更のたびに変更メッセージが生成されます。 Twin2 を変更する呼び出しは、到着するとすぐに競合なしで同時に実行できます。

デジタル ツインを削除する

DeleteDigitalTwin() メソッドを使用してツインを削除することができます。 ただし、ツインを削除できるのは、ツインにリレーションシップがない場合だけです。 そのため、最初にツインの受信と送信のリレーションシップを削除します。

ツインとそのリレーションシップを削除するコードの例を次に示します。 DeleteDigitalTwin SDK の呼び出しが強調表示されており、この例の幅広いコンテキストでの位置が明確になっています。

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

すべてのデジタル ツインを削除する

一度にすべてのツインを削除する方法の例については、サンプル クライアント アプリを使用した基本事項の確認に関するページで使用されているサンプル アプリをダウンロードしてください。 CommandLoop.cs ファイルでは、CommandDeleteAllTwins() 関数でこれを実行します。

Note

インスタンス内のすべてのモデル、ツイン、リレーションシップを一度に削除する場合は、Delete Jobs API使用します。

実行可能なデジタル ツインのコード サンプル

次の実行可能なコード サンプルを使用してツインを作成し、その詳細を更新して、ツインを削除することができます。

サンプル プロジェクト ファイルの設定

このスニペットでは、サンプル モデル定義 Room.json を使用します。 モデル ファイルをダウンロードしてコードで使用できるようにするには、このリンクを使用して GitHub のファイルに直接アクセスします。 次に、画面上の任意の場所を右クリックし、ブラウザーの右クリック メニューから [名前を付けて保存] を選択して、[名前を付けて保存] のウィンドウでファイルを Room.json で保存します。

次に、Visual Studio または任意のエディターで、新しいコンソール アプリ プロジェクトを作成します。

次に、実行可能なサンプルの次のコードをプロジェクトにコピーします。

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>

    }
}

Note

現在 DefaultAzureCredential ラッパー クラスに影響を与える既知の問題があり、それが原因で認証中にエラーが発生する可能性があります。 この問題が発生した場合は、次の省略可能なパラメーターを使用して DefaultAzureCredential のインスタンス化を試して解決することができます: new DefaultAzureCredential(new DefaultAzureCredentialOptions { ExcludeSharedTokenCacheCredential = true });

この問題の詳細については、Azure Digital Twins の既知の問題に関する記事を参照してください。

プロジェクトを構成する

次に、以下の手順を実行してプロジェクト コードを構成します。

  1. 以前ダウンロードした Room.json ファイルをプロジェクトに追加し、コード内部の <path-to> プレースホルダーを置き換えて、プログラムに検索する場所を指示します。

  2. プレースホルダー <your-instance-hostname> を Azure Digital Twins インスタンスのホスト名に置き換えます。

  3. Azure Digital Twins を使用するために必要な 2 つの依存関係をプロジェクトに追加します。 1 つ目は .NET 用 Azure Digital Twins SDK 用のパッケージであり、2 つ目では Azure に対する認証に役立つツールが提供されます。

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

サンプルを直接実行する場合は、ローカル資格情報を設定する必要もあります。 次のセクションでは、これについて説明します。

ローカルの Azure 資格情報を設定する

このサンプルでは、ローカル コンピューターで実行された Azure Digital Twins インスタンスに対し、(Azure.Identity ライブラリの) DefaultAzureCredential を使用してユーザーを認証します。 Azure Digital Twins に対してクライアント アプリの認証を行う各種の方法について詳しくは、「アプリ認証コードを作成する」を参照してください。

このサンプルでは、DefaultAzureCredential を使用して、ローカル環境から資格情報が検索されます。たとえば、ローカルの DefaultAzureCredential や Visual Studio または Visual Studio Code での Azure サインインなどです。 このため、サンプルの資格情報を設定するために、これらのメカニズムのいずれかを使用して、Azure にローカルでサインインする必要があります。

Visual Studio または Visual Studio Code を使用してコード サンプルを実行する場合は、Azure Digital Twins インスタンスへのアクセスに使用する Azure 資格情報でそのエディターにサインインしていることを確認してください。 ローカル CLI ウィンドウを使用している場合は、az login コマンドを実行して Azure アカウントにサインインします。 その後、コード サンプルを実行すれば、自動的に認証処理が行われます。

サンプルの実行

セットアップが完了したら、サンプル コード プロジェクトを実行できます。

上記のプログラムのコンソール出力を次に示します。

Screenshot of the console output showing that the twin is created, updated, and deleted.

次のステップ

デジタル ツイン間のリレーションシップを作成および管理する方法を確認します。