教學課程:使用 Azure Digital Twins SDK 撰寫程式碼

使用 Azure Digital Twins 的開發人員通常會撰寫用戶端應用程式,以與其 Azure Digital Twins 服務的實例互動。 此以開發人員為主的教學課程會使用 適用于 .NET 的 Azure Digital Twins SDK(C#) ,提供針對 Azure Digital Twins 服務進行程式設計簡介。 它會逐步引導您撰寫 C# 主控台用戶端應用程式,從頭開始。

  • 設定專案
  • 開始使用專案程式碼
  • 完整的程式碼範例
  • 清除資源
  • 下一步

必要條件

本 Azure Digital Twins 教學課程會使用命令列進行安裝和專案工作。 因此,您可以使用任何程式碼編輯器逐步解說練習。

您需要開始的內容:

  • 任何程式碼編輯器
  • 開發電腦上的 .NET Core 3.1。 您可以從下載 .NET Core 3.1 下載多個平臺 的這個 .NET Core SDK 版本。

準備 Azure Digital Twins 實例

若要使用本文中的 Azure Digital Twins,您需要 Azure Digital Twins 實例和使用它所需的許可權。 如果您已設定 Azure Digital Twins 實例,您可以使用該實例並跳至下一節。 否則,請遵循設定實例和驗證 中的 指示。 這些指示包含可協助您確認已順利完成每個步驟的資訊。

設定實例之後,請記下實例的主機名稱。 您可以在 Azure 入口網站 中找到主機名稱。

設定本機 Azure 認證

此範例會在本機電腦上執行 Azure Digital Twins 實例時,使用 DefaultAzureCredential (程式庫的 Azure.Identity 一部分)來驗證使用者。 如需用戶端應用程式使用 Azure Digital Twins 進行驗證之不同方式的詳細資訊,請參閱 撰寫應用程式驗證碼

使用 DefaultAzureCredential 時,此範例會在本機環境中搜尋認證,例如在本機 Azure CLI Visual Studio 或 Visual Studio Code 中登入。 基於這個理由,您應該 透過下列其中一個機制在本機登入 Azure ,以設定範例的認證。

如果您使用 Visual Studio 或 Visual Studio Code 來執行程式碼範例,請確定您已 使用您想要用來存取 Azure Digital Twins 實例的相同 Azure 認證登入該編輯器 。 如果您使用本機 CLI 視窗,請執行 az login 命令來登入您的 Azure 帳戶。 之後,當您執行程式碼範例時,應該會自動進行驗證。

設定專案

準備好使用 Azure Digital Twins 實例之後,請開始設定用戶端應用程式專案。

在您的電腦上開啟主控台視窗,並在本教學課程期間建立您要在其中儲存工作的空白專案目錄。 將目錄命名為您想要的任何專案(例如 DigitalTwinsCodeTutorial )。

流覽至新的目錄。

在專案目錄中,建立空的 .NET 主控台應用程式專案。 在命令視窗中,您可以執行下列命令來建立主控台的最低 C# 專案:

dotnet new console

此命令會在您的目錄中建立數個檔案,包括一個名為 Program.cs 的檔案,您將在其中撰寫大部分的程式碼。

讓命令視窗保持開啟,因為您將在整個教學課程中繼續使用。

接下來,將兩個相依性新增至您的專案,以搭配 Azure Digital Twins 使用。 第一個是適用于 .NET Azure Digital Twins SDK 套件,第二個套件提供工具來協助驗證 Azure。

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

開始使用專案程式碼

在本節中,您將開始撰寫新應用程式專案的程式碼,以使用 Azure Digital Twins。 涵蓋的動作包括:

  • 針對服務進行驗證
  • 上傳模型
  • 攔截錯誤
  • 建立數位對應項
  • 建立關聯性
  • 查詢數位對應項

另外還有一節顯示教學課程結尾的完整程式碼。 您可以使用本節作為參考,在進行時檢查程式。

若要開始,請在任何程式碼編輯器中開啟 Program.cs 檔案 。 您會看到如下所示的最小程式代碼範本:

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

首先,在程式碼頂端新增一些 using 行,以提取必要的相依性。

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

接下來,您會將程式碼新增至此檔案,以填寫某些功能。

針對服務進行驗證

您的應用程式需要做的第一件事是針對 Azure Digital Twins 服務進行驗證。 然後,您可以建立服務用戶端類別來存取 SDK 函式。

若要進行驗證,您需要 Azure Digital Twins 實例的主機名稱。

Program.cs 中,將下列程式碼貼到 方法中的 Main 「Hello, World!」 列印行下方。 將 的值 adtInstanceUrl 設定為您的 Azure Digital Twins 實例主機名稱。

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

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

儲存檔案。

在您的命令視窗中,使用此命令執行程式碼:

dotnet run

此命令會在初次執行時還原相依性,然後執行程式。

  • 如果沒有發生錯誤,程式將會列印:「已建立的服務用戶端 - 已準備好進行」。
  • 由於此專案中還沒有錯誤處理,如果發生任何問題,您將會看到程式碼擲回的例外狀況。

注意

目前有已知問題會影響 DefaultAzureCredential 包裝函式類別,可能會在驗證時產生錯誤。 如果您遇到此問題,您可以嘗試使用下列選擇性參數進行具現化 DefaultAzureCredential 以解決此問題: new DefaultAzureCredential(new DefaultAzureCredentialOptions { ExcludeSharedTokenCacheCredential = true });

如需此問題的詳細資訊,請參閱 Azure Digital Twins 已知問題

上傳模型

Azure Digital Twins 沒有內建領域詞彙。 您可以在 Azure Digital Twins 中代表的環境元素類型,是使用 模型 來定義。 模型 與物件導向程式設計語言中的類別類似;它們會提供使用者定義範本,讓 數位對應項 稍後遵循並具現化。 它們是以類似 JSON 的語言撰寫,稱為 Digital Twins 定義語言 (DTDL)

建立 Azure Digital Twins 解決方案的第一個步驟是在 DTDL 檔案中定義至少一個模型。

在您建立專案的目錄中,建立名為 SampleModel.json 的新 .json 檔案。 貼上下列檔案本文:

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

提示

如果您使用 Visual Studio 進行本教學課程,您可能會想要選取新建立的 JSON 檔案,並將屬性偵測器中的 [複製到輸出目錄 ] 屬性設定 [新增] [一律複製] 時複製。 這可讓 Visual Studio 在教學課程的其餘部分使用 F5 執行程式時,尋找具有預設路徑的 JSON 檔案。

提示

您可以使用 DTDLParser 程式庫 來檢查模型檔,以確定 DTDL 有效 。 如需使用此程式庫的詳細資訊,請參閱 剖析和驗證模型

接下來,將更多程式碼新增至 Program.cs ,以將您建立的模型上傳至 Azure Digital Twins 實例。

首先,將幾個 using 語句新增至檔案頂端:

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

接下來,藉由變更 Main 方法簽章以允許非同步執行,準備使用 C# 服務 SDK 中的非同步方法。

static async Task Main(string[] args)
{

注意

使用 async 並非絕對必要,因為 SDK 也會提供所有呼叫的同步版本。 本教學課程做法使用 async

接下來是與 Azure Digital Twins 服務互動的第一位程式碼。 此程式碼會載入您從磁片建立的 DTDL 檔案,然後將它上傳至您的 Azure Digital Twins 服務實例。

將下列程式碼貼到您稍早新增的授權碼底下。

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

在您的命令視窗中,使用此命令執行程式:

dotnet run

「上傳模型」將會列印在輸出中,指出已達到此程式碼,但尚未輸出指出上傳是否成功。

若要新增列印語句,其中顯示已成功上傳至 實例的所有模型,請在上一節之後新增下列程式碼:

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

再次執行程式以測試這個新程式碼之前,請記得您上次執行程式時,您已上傳模型。 Azure Digital Twins 不會讓您上傳相同的模型兩次,因此如果您再次嘗試上傳相同的模型,程式應該會擲回例外狀況。

考慮到這項資訊,請在命令視窗中使用此命令再次執行程式:

dotnet run

程式應該擲回例外狀況。 當您嘗試上傳已上傳的模型時,服務會透過 REST API 傳回「不正確的要求」錯誤。 因此,Azure Digital Twins 用戶端 SDK 會針對成功以外的每個服務傳回碼擲回例外狀況。

下一節將討論這類例外狀況,以及如何在程式碼中處理例外狀況。

攔截錯誤

若要讓程式無法當機,您可以在模型上傳程式碼周圍新增例外狀況程式碼。 將現有的用戶端呼叫 await client.CreateModelsAsync(typeList) 包裝在 try/catch 處理常式中,如下所示:

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

在命令視窗中使用 dotnet run 再次執行程式。 您會看到您取得模型上傳問題的詳細數據,包括指出的錯誤 ModelIdAlreadyExists碼。

從這一點開始,本教學課程會在 try/catch 處理程式中包裝對服務方法的所有呼叫。

建立數字對應項

既然您已將模型上傳至 Azure Digital Twins,您可以使用此模型定義來建立 數位對應項數字對應項是模型的實例,並代表商務環境內的實體,例如農場上的感測器、建築物中的會議室或汽車中的燈光。 本節會根據您稍早上傳的模型建立一些數字對應項。

將下列程式代碼新增至 方法的 Main 結尾,以根據此模型建立和初始化三個數字對應項。

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

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

在您的命令視窗中,使用 dotnet run執行程式。 在輸出中,尋找範例Twin-0、sampleTwin-1 和 sampleTwin-2 已建立的列印訊息。

然後,再次執行程式。

請注意,第二次建立對應項時不會擲回任何錯誤,即使第一次執行之後已有對應項存在也一樣。 與模型建立不同,對應項建立是在 REST 層級上建立具有 upsert 語意的 PUT 呼叫。 使用這種 REST 呼叫表示如果對應項已經存在,再次嘗試建立相同的對應項只會取代原始對應項。 不會擲回錯誤。

建立關聯

接下來,您可以在 您所建立的對應項之間建立關聯 性,以將它們連線到對應項 圖形對應項圖形 可用來代表整個環境。

在 方法底下Main,將新的靜態方法新增至 Program 類別(程式代碼現在有兩種方法):

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

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

接下來,將下列程式代碼新增至 方法的 Main 結尾,以呼叫 CreateRelationship 方法,並使用您剛才撰寫的程序代碼:

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

在您的命令視窗中,使用 dotnet run執行程式。 在輸出中,尋找列印語句,指出已成功建立兩個關聯性。

如果已有相同標識碼的另一個關聯性存在,Azure Digital Twins 將不會讓您建立關聯性,因此,如果您多次執行程式,就會在建立關聯性時看到例外狀況。 此程式代碼會攔截例外狀況並忽略它們。

列出關聯性

您將新增的下一個程式代碼可讓您查看您已建立的關聯性清單。

將下列新方法新增至 Program 類別:

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

然後,將下列程式代碼新增至 方法的 Main 結尾,以呼叫程式 ListRelationships 代碼:

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

在您的命令視窗中,使用 dotnet run執行程式。 您應該會看到您在輸出語句中建立的所有關聯性清單,如下所示:

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

查詢數字對應項

Azure Digital Twins 的主要功能是 能夠輕鬆且有效率地查詢 對應項圖形,以回答有關您環境的問題。

本教學課程中要新增的程式代碼最後一節會針對 Azure Digital Twins 實例執行查詢。 此範例中使用的查詢會傳回 實例中的所有數字對應項。

新增此 using 語句,讓 類別能夠 JsonSerializer 協助呈現數字對應項資訊:

using System.Text.Json;

然後,將下列程式代碼新增至 方法的 Main 結尾:

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

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

在您的命令視窗中,使用 dotnet run執行程式。 您應該會在輸出中看到此實體中的所有數字對應項。

注意

對圖形中的數據進行變更之後,可能會有最多 10 秒的延遲,變更才會反映在查詢中。

DigitalTwins API 會立即反映變更,因此如果您需要立即回應,請使用 API 要求 (DigitalTwins GetById) 或 SDK 呼叫 (GetDigitalTwin) 來取得對應項數據,而不是查詢。

完成程式代碼範例

在本教學課程中,您有完整的用戶端應用程式,可針對 Azure Digital Twins 執行基本動作。 如需參考,Program.cs程式的完整程序代碼如下所列:

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

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

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

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

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

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

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

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

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

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

清除資源

完成本教學課程之後,您可以根據接下來要執行的動作,選擇要移除的資源。

  • 如果您打算繼續進行下一個教學課程,本教學課程中使用的實例可以在下一個教學課程中重複使用。 您可以保留您在這裡設定的 Azure Digital Twins 資源,並略過本節的其餘部分。
  • 如果您想要繼續使用本文中的 Azure Digital Twins 實例,但清除 其所有 模型、對應項和關聯性,請執行下列 az dt job delete CLI 命令:

    az dt job deletion create -n <name-of-Azure-Digital-Twins-instance> -y
    

    如果您只想要刪除其中一些元素,您可以使用 az dt twin relationship deleteaz dt twin delete 和 az dt model delete 命令,選擇性地刪除您想要移除的元素。

  • 如果您不需要在本教學課程中建立的任何資源,您可以使用 az group delete CLI 命令,刪除本文中的 Azure Digital Twins 實例和所有其他資源。 這會刪除資源群組中的所有 Azure 資源,以及資源群組本身。

    重要

    刪除資源群組是無法復原的。 資源群組及其中包含的所有資源都會永久刪除。 請確定您不會不小心刪除錯誤的資源群組或資源。

    開啟 Azure Cloud Shell 或本機 CLI 視窗,然後執行下列命令來刪除資源群組及其包含的所有專案。

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

您也可以從本機電腦刪除項目資料夾。

下一步

在本教學課程中,您已從頭開始建立 .NET 控制台用戶端應用程式。 您已撰寫此用戶端應用程式的程式碼,以在 Azure Digital Twins 實例上執行基本動作。

繼續進行下一個教學課程,以探索您可以使用這類範例用戶端應用程式執行的動作: