Work With Azure Digital Twin - code a client app
Tags: digitaltwin, tutorial, beginners, csharp
Published: 2022-07-25 15:55:31

> In this article, I will use **DT** for **Digital Twin**.

### Prerequisites

- [knowledge](https://docs.microsoft.com/en-us/azure/digital-twins/overview) about Azure DT

- Any code editor (_[VS Code](https://code.visualstudio.com/) is preferable, and you don't need to install .NET Core_)

- [.NET Core](https://dotnet.microsoft.com/) installed

- Azure DT [instance](https://docs.microsoft.com/en-us/azure/digital-twins/quickstart-azure-digital-twins-explorer) created

## Getting Started

#### Project Setup

The very first is to create a minimal C# project in the created directory:
```linux
mkdir new-dt
cd new-dt
dotnet new console
```
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bve9kdevjh0uxnqp8hbg.png)
 
Then you want to add two dependencies to your project: one is the package for the Azure DT SDK for .NET, and the other is tools to help with authentication management:

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

Then we add the following two pieces of code to **`Program.cs`** to add necessary dependencies and authentication functionality:

> Add this on the top

```csharp
using Azure.DigitalTwins.Core;
using Azure.Identity;
```
> And add this below the 'hello world' line in `Main` method, and **do not** forget to set the value of `adtInstanceUrl` to your instance host name:

```csharp
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");
```
Now we can save the project (or preferablly commit to your git) and run the command below to test our previous work:

```linux
dotnet run
```
The console will print the following if no error occurs:
`Hello World!`
`Service client created – ready to go`

#### Model Upload

We can create models using Digital Twins Definition Language  (**_DTDL_**, a kind of language similar with OOP programming language). 

Let's create a new file named **`SampleModel.json`**, and paste the following code into it:

```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;2"
}
```

After that, you want to add the rest of the code in **`Program.cs`** in order to interact with your DT instance (including code previously pasted):


```csharp
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>
    }
}
```

You should now interact with your digital twins using `dotnet run`. 

#### Part of the result

Hello World!
Service client created – ready to go

Upload a model
Models uploaded to the instance:
Model: dtmi:example:Floor;1
Model: dtmi:example:Room;1
Model: dtmi:example:SampleModel;1
Created twin: sampleTwin-0
Created twin: sampleTwin-1
Created twin: sampleTwin-2
Created relationship successfully
Created relationship successfully
Twin sampleTwin-0 is connected to:
 -contains->sampleTwin-1
 -contains->sampleTwin-2
....


### References
Code a client app
https://docs.microsoft.com/en-us/azure/digital-twins/tutorial-code
Cover image
https://www.ciobulletin.com/cloud/azure-digital-twins-generally-available