Udostępnij przez


Zarządzanie grafem cyfrowych bliźniaków przy użyciu relacji

Sercem usługi Azure Digital Twins jest graf bliźniaczy, który przedstawia całe środowisko. Wykres bliźniaczy jest wykonany z poszczególnych cyfrowych bliźniaków połączonych za pośrednictwem relacji. Ten artykuł koncentruje się na zarządzaniu relacjami i grafem jako całością; aby pracować z poszczególnymi reprezentacjami cyfrowymi, zobacz Manage digital twins (Zarządzanie cyfrowymi reprezentacjami bliźniaczymi).

Po utworzeniu działającego wystąpienia usługi Azure Digital Twins i skonfigurowaniu kodu uwierzytelniania w aplikacji klienckiej możesz tworzyć, modyfikować i usuwać cyfrowe reprezentacje bliźniacze oraz ich relacje w wystąpieniu usługi Azure Digital Twins.

Wymagania wstępne

Aby pracować z usługą Azure Digital Twins w tym artykule, potrzebujesz wystąpienia usługi Azure Digital Twins i wymaganych uprawnień do korzystania z tej usługi. Jeśli masz już skonfigurowane wystąpienie usługi Azure Digital Twins, możesz użyć tego wystąpienia i przejść do następnej sekcji. W przeciwnym razie postępuj zgodnie z instrukcjami w Skonfiguruj wystąpienie i uwierzytelnianie. Instrukcje zawierają informacje ułatwiające sprawdzenie, czy każdy krok został ukończony pomyślnie.

Po skonfigurowaniu wystąpienia, zanotuj nazwę hosta tego wystąpienia. Nazwę hosta można znaleźć w witrynie Azure Portal.

Interfejsy deweloperskie

W tym artykule opisano sposób wykonywania różnych operacji zarządzania przy użyciu zestawu SDK platformy .NET (C#). Możesz również utworzyć te same wywołania zarządzania używając innych językowych zestawów SDK opisanych w API i zestawach SDK usługi Azure Digital Twins.

Inne interfejsy deweloperskie, których można użyć do wykonania tych operacji, to:

Wizualizacja

Azure Digital Twins Explorer to wizualne narzędzie do eksplorowania danych na grafie usługi Azure Digital Twins. Eksplorator umożliwia wyświetlanie, wykonywanie zapytań i edytowanie modeli, reprezentacji bliźniaczych i relacji.

Aby dowiedzieć się więcej o narzędziu Azure Digital Twins Explorer, zobacz Azure Digital Twins Explorer. Aby uzyskać szczegółowe instrukcje dotyczące korzystania z jej funkcji, zobacz Korzystanie z eksploratora usługi Azure Digital Twins.

Oto jak wygląda wizualizacja:

Zrzut ekranu Azure Digital Twins Explorer przedstawiający przykładowe modele i bliźniaki.

Utwórz relacje

Relacje opisują, w jaki sposób różne cyfrowe bliźniaki są ze sobą połączone, co stanowi podstawę grafu bliźniaków.

Typy relacji, które można utworzyć między jednym bliźniakiem źródłowym a drugim bliźniakiem docelowym, są zdefiniowane jako część modelu DTDL bliźniaka źródłowego. Wystąpienie relacji można utworzyć przy użyciu CreateOrReplaceRelationshipAsync() wywołania zestawu SDK z danymi bliźniaczymi i szczegółami relacji, które są zgodne z definicją DTDL.

Aby utworzyć relację, należy określić:

  • Identyfikator źródłowego bliźniaka (srcId w poniższym przykładzie kodu): identyfikator bliźniaka, z którego pochodzi relacja.
  • Identyfikator docelowego bliźniaka (targetId w poniższym przykładzie kodu): identyfikator bliźniaka, do którego dociera relacja.
  • Nazwa relacji (relName w poniższym przykładzie kodu): jest to ogólny typ relacji, na przykład zawiera.
  • Identyfikator relacji (relId w poniższym przykładzie kodu): określona nazwa tej relacji, podobna do relacji Relationship1.

Identyfikator relacji musi być unikatowy w obrębie danego bliźniaka źródłowego. Nie musi być globalnie unikatowa. Na przykład dla bliźniaczej pary Foo każdy unikalny identyfikator relacji musi być jedyny w swoim rodzaju. Jednak inny bliźniaczy Bar może mieć relację wychodzącą, która pasuje do tego samego identyfikatora relacji Foo.

Poniższy przykładowy kod ilustruje sposób tworzenia relacji w wystąpieniu usługi Azure Digital Twins. Używa wywołania SDK (wyróżnionego) wewnątrz metody niestandardowej, która może pojawić się w kontekście większego programu.

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

}

Teraz można wywołać tę funkcję niestandardową, aby utworzyć relację contains w następujący sposób:

await CustomMethod_CreateRelationshipAsync(client, srcId, targetId, "contains", properties);

Jeśli chcesz utworzyć wiele relacji, możesz powtarzać wywołania tej samej metody, przekazując do argumentu różne typy relacji.

Aby uzyskać więcej informacji na temat klasy BasicRelationshippomocniczej, zobacz Interfejsy API i zestawy SDK usługi Azure Digital Twins.

Tworzenie wielu relacji między bliźniętami

Relacje mogą być klasyfikowane jako:

  • Relacje wychodzące: Relacje należące do tego bliźniaka, które wskazują na zewnątrz, aby połączyć go z innymi bliźniakami. Metoda GetRelationshipsAsync() służy do pobierania wychodzących relacji bliźniaka.
  • Relacje przychodzące: relacje należące do innych bliźniaków, które wskazują na tego bliźniaka, tworząc link przychodzący. Metoda GetIncomingRelationshipsAsync() służy do uzyskiwania wejściowych relacji bliźniaka.

Nie ma żadnych ograniczeń co do liczby relacji, jakie można mieć między dwojgiem bliźniąt — możesz mieć dowolną liczbę relacji między bliźniętami.

Oznacza to, że można wyrazić kilka różnych typów relacji między dwoma bliźniakami jednocześnie. Na przykład Bliźniak A może mieć zarówno przechowywaną relację, jak i utworzoną relację z Bliźniakiem B.

Możesz nawet utworzyć wiele wystąpień tego samego typu relacji między tymi samymi dwoma bliźniakami, jeśli chcesz. W tym przykładzie bliźniak A może mieć dwie różne przechowywane relacje z bliźniakiem B, pod warunkiem, że relacje mają różne identyfikatory.

Uwaga

Usługa nie obsługuje obecnie ani nie wymusza atrybutów minMultiplicitymaxMultiplicity DTDL dla relacji w usłudze Azure Digital Twins, nawet jeśli są one zdefiniowane jako część modelu. Aby uzyskać więcej informacji, zobacz Uwagi dotyczące języka DTDL specyficzne dla usługi.

Zbiorcze tworzenie relacji za pomocą interfejsu API importowania zadań

Możesz użyć Import Jobs API, aby utworzyć wiele relacji jednocześnie przy jednym wywołaniu API. Ta metoda wymaga użycia usługi Azure Blob Storage i uprawnień do zapisu w wystąpieniu usługi Azure Digital Twins na potrzeby relacji i zadań zbiorczych.

Napiwek

Interfejs API zadań importu umożliwia również importowanie modeli i bliźniaczych reprezentacji w tym samym wywołaniu w celu utworzenia wszystkich części grafu jednocześnie. Aby uzyskać więcej informacji na temat tego procesu, zobacz Masowe przekazywanie modeli, bliźniaków i relacji za pomocą Import Jobs API.

Aby zaimportować relacje zbiorczo, należy ustrukturyzować relacje (i wszystkie inne zasoby zawarte w zadaniu importu zbiorczego) w postaci pliku NDJSON. Sekcja Relationships jest późniejsza po Twins sekcji, co czyni ją ostatnią sekcją danych grafu w pliku. Relacje zdefiniowane w pliku mogą odwoływać się do bliźniaków zdefiniowanych w tym pliku lub już istniejących w instancji, i opcjonalnie mogą obejmować inicjalizację jakichkolwiek właściwości zawieranych w relacjach.

Przykładowy plik importu i projekt można zobaczyć w rozdziale Wprowadzenie do interfejsu API importu zadań.

Następnie należy przekazać plik do obiektu blob typu append w usłudze Azure Blob Storage. Aby uzyskać instrukcje dotyczące tworzenia kontenera usługi Azure Storage, zobacz Tworzenie kontenera. Następnie przekaż plik przy użyciu preferowanej metody przekazywania (niektóre opcje to polecenie AzCopy, interfejs wiersza polecenia platformy Azure lub witryna Azure Portal).

Po przekazaniu pliku NDJSON do kontenera, pobierz jego adres URL w kontenerze blob. Używasz tej wartości później w treści wywołania interfejsu API importu zbiorczego.

Oto zrzut ekranu przedstawiający wartość adresu URL pliku obiektu blob w witrynie Azure Portal:

Zrzut ekranu portalu Azure przedstawiający adres URL pliku w kontenerze przechowywania.

Następnie można użyć pliku w wywołaniu Import Jobs API. Udostępniasz adres URL magazynu obiektów blob dla pliku wejściowego oraz nowy adres URL magazynu obiektów blob, aby wskazać, gdzie ma być przechowywany dziennik wyjściowy po jego utworzeniu przez usługę.

Lista relacji

Lista właściwości pojedynczej relacji

Zawsze można deserializować dane relacji na wybrany typ. Aby uzyskać podstawowy dostęp do relacji, użyj typu BasicRelationship. Klasa BasicRelationship pomocnika zapewnia również dostęp do właściwości zdefiniowanych w relacji za pośrednictwem elementu IDictionary<string, object>. Aby wyświetlić listę właściwości, możesz użyć:

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

Lista relacji wychodzących z cyfrowego bliźniaka

Aby uzyskać dostęp do listy relacji wychodzących dla danego bliźniaka w grafie, możesz użyć GetRelationships() metody w następujący sposób:

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

Ta metoda zwraca Azure.Pageable<T> lub Azure.AsyncPageable<T>, w zależności od tego, czy używasz synchronicznej, czy asynchronicznej wersji wywołania.

Oto przykład, który pobiera listę relacji. Używa wywołania SDK (wyróżnionego) wewnątrz metody niestandardowej, która może pojawić się w kontekście większego programu.

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

Teraz możesz wywołać tę metodę niestandardową, aby zobaczyć relacje wychodzące bliźniaków w następujący sposób:

await CustomMethod_FindOutgoingRelationshipsAsync(client, twin_Id);

Możesz użyć pobranych relacji, aby przejść do innych bliźniaków w swoim grafie, odczytując pole target ze zwróconej relacji i używając go jako identyfikatora następnego wywołania funkcji GetDigitalTwin().

Lista relacji przychodzących do cyfrowego bliźniaka

Usługa Azure Digital Twins ma również wywołanie zestawu SDK w celu znalezienia wszystkich relacji przychodzących do danego bliźniaka. Ten zestaw SDK jest często przydatny do nawigacji odwrotnej lub podczas usuwania reprezentacji bliźniaczej.

Uwaga

IncomingRelationship wywołania nie zwracają pełnej treści relacji. Aby uzyskać więcej informacji na temat klasy IncomingRelationship, zobacz jej dokumentację referencyjną.

Przykładowy kod w poprzedniej sekcji koncentruje się na znajdowaniu relacji wychodzących z bliźniaka. Poniższy przykład ma podobną strukturę, ale zamiast tego znajduje relacje przychodzące do bliźniaka. W tym przykładzie użyto również wywołania pakietu SDK (wyróżnionego) wewnątrz niestandardowej metody, która może pojawić się w kontekście większego programu.

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

Teraz możesz wywołać tę niestandardową metodę, aby zobaczyć przychodzące relacje bliźniaków w następujący sposób:

await CustomMethod_FindIncomingRelationshipsAsync(client, twin_Id);

Wymień wszystkie właściwości i relacje bliźniacze

Korzystając z poprzednich metod wyświetlania relacji wychodzących i przychodzących do obiektu bliźniaczego, można stworzyć metodę, która wyświetla pełne informacje o tym obiekcie, w tym jego właściwości oraz oba typy relacji. Oto przykładowa metoda niestandardowa przedstawiająca sposób łączenia poprzednich metod niestandardowych w tym celu.

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

Teraz możesz wywołać tę funkcję niestandardową w następujący sposób:

await CustomMethod_FetchAndPrintTwinAsync(srcId, client);

Aktualizowanie relacji

Relacje są aktualizowane przy użyciu metody UpdateRelationship.

Uwaga

Ta metoda służy do aktualizowania właściwości relacji. Jeśli musisz zmienić źródłową reprezentację bliźniaczą lub docelową reprezentację bliźniaczą relacji, musisz usunąć relację i ponownie utworzyć je przy użyciu nowych reprezentacji bliźniaczych.

Wymagane parametry wywołania klienta to:

  • Identyfikator źródłowego bliźniaka (bliźniaka, z którego pochodzi relacja).
  • Identyfikator relacji do zaktualizowania.
  • Dokument poprawki JSON zawierający właściwości i nowe wartości, które chcesz zaktualizować.

Oto przykładowy fragment kodu pokazujący sposób użycia tej metody. W tym przykładzie użyto wywołania zestawu SDK (wyróżnionego) wewnątrz niestandardowej metody, która może pojawić się w kontekście większego programu.

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

}

Oto przykład wywołania tej metody niestandardowej, przekazujący dokument poprawki JSON z informacjami w celu aktualizacji właściwości.

var updatePropertyPatch = new JsonPatchDocument();
updatePropertyPatch.AppendAdd("/ownershipUser", "ownershipUser NEW value");
await CustomMethod_UpdateRelationshipAsync(client, srcId, $"{srcId}-contains->{targetId}", updatePropertyPatch);

Usuń relacje

Pierwszy parametr określa bliźniaka źródłowego (bliźniaka, z którego pochodzi relacja). Drugi parametr to identyfikator relacji. Potrzebujesz zarówno identyfikatora bliźniaka, jak i identyfikatora relacji, ponieważ identyfikatory relacji są unikatowe tylko w obrębie bliźniaka.

Oto przykładowy kod pokazujący, jak używać tej metody. W tym przykładzie użyto wywołania zestawu SDK (wyróżnionego) wewnątrz niestandardowej metody, która może pojawić się w kontekście większego programu.

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

Teraz możesz wywołać tę metodę niestandardową, aby usunąć relację w następujący sposób:

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

Uwaga

Jeśli chcesz usunąć wszystkie modele, reprezentacje bliźniacze i relacje w wystąpieniu jednocześnie, użyj interfejsu API usuwania zadań.

Tworzenie wielu elementów grafu jednocześnie

W tej sekcji opisano strategie tworzenia grafu z wieloma elementami jednocześnie, zamiast używania poszczególnych wywołań interfejsu API do przesyłania modeli, bliźniaków i relacji, jeden po drugim.

Zbiorcze przesyłanie modeli, bliźniaków i relacji za pomocą interfejsu API Importowania zadań

Możesz użyć Import Jobs API do przesyłania wielu modeli, bliźniaków i relacji do twojego wystąpienia w jednym wywołaniu API, co skutecznie tworzy graf za jednym razem. Ta metoda wymaga użycia usługi Azure Blob Storage i uprawnień zapisu w wystąpieniu usługi Azure Digital Twins dla elementów wykresu (modeli, bliźniaków i relacji) i zadań zbiorczych.

Aby zaimportować zasoby zbiorczo, zacznij od utworzenia pliku NDJSON zawierającego szczegóły zasobów. Plik rozpoczyna się od Header sekcji, a następnie opcjonalnych sekcji Models, Twinsi Relationships. Nie musisz uwzględniać wszystkich trzech typów danych grafu w pliku, ale wszystkie obecne sekcje muszą być zgodne z kolejnością. Bliźniaki zdefiniowane w pliku mogą odnosić się do modeli zdefiniowanych w tym pliku lub już istniejących w instancji i mogą opcjonalnie obejmować inicjalizację właściwości bliźniaków. Relacje zdefiniowane w pliku mogą odwoływać się do bliźniaczych reprezentacji zdefiniowanych w tym pliku lub już obecnych w wystąpieniu i opcjonalnie mogą obejmować inicjowanie właściwości relacji.

Przykładowy plik importu i projekt można zobaczyć w rozdziale Wprowadzenie do interfejsu API importu zadań.

Następnie należy przekazać plik do obiektu blob typu append w usłudze Azure Blob Storage. Aby uzyskać instrukcje dotyczące tworzenia kontenera usługi Azure Storage, zobacz Tworzenie kontenera. Następnie przekaż plik przy użyciu preferowanej metody przekazywania (niektóre opcje to polecenie AzCopy, interfejs wiersza polecenia platformy Azure lub witryna Azure Portal).

Po przekazaniu pliku NDJSON do kontenera, pobierz jego adres URL w kontenerze blob. Używasz tej wartości później w treści wywołania interfejsu API importu zbiorczego.

Oto zrzut ekranu przedstawiający wartość adresu URL pliku obiektu blob w witrynie Azure Portal:

Zrzut ekranu portalu Azure przedstawiający adres URL pliku w kontenerze przechowywania.

Następnie można użyć pliku w wywołaniu Import Jobs API. Udostępniasz adres URL magazynu obiektów blob dla pliku wejściowego oraz nowy adres URL magazynu obiektów blob, aby wskazać, gdzie ma być przechowywany dziennik wyjściowy po jego utworzeniu przez usługę.

Importowanie grafu za pomocą eksploratora usługi Azure Digital Twins

Azure Digital Twins Explorer to narzędzie wizualne do wyświetlania i interakcji z grafem bliźniaków. Zawiera funkcję importowania pliku grafu w formacie JSON lub Excel, który może zawierać wiele modeli, reprezentacji bliźniaczych i relacji.

Aby uzyskać szczegółowe informacje na temat korzystania z tej funkcji, zobacz Importowanie grafu w dokumentacji eksploratora usługi Azure Digital Twins.

Utwórz bliźniaki i relacje z pliku CSV

Czasami może być konieczne utworzenie hierarchii bliźniaczych na podstawie danych przechowywanych w innej bazie danych, arkuszu kalkulacyjnym lub pliku CSV. W tej sekcji pokazano, jak odczytywać dane z pliku CSV i tworzyć z nich graf powiązań.

Rozważmy poniższą tabelę danych opisującą zestaw cyfrowych reprezentacji bliźniaczych i relacji. Modele, do których odwołuje się ten plik, muszą już istnieć w wystąpieniu usługi Azure Digital Twins.

Identyfikator modelu Identyfikator bliźniaczej reprezentacji (musi być unikatowy) Nazwa relacji Identyfikator bliźniaczej reprezentacji docelowej Dane inicjowania reprezentacji bliźniaczej
dtmi:example:Floor; 1 Piętro 1 zawiera Pokój1
dtmi:example:Floor; 1 Podłoga0 zawiera Pokój0
dtmi:example:Room; 1 Pokój1 {"Temperatura": 80}
dtmi:example:Room; 1 Pokój0 {"Temperature": 70}

Jednym ze sposobów uzyskania tych danych do usługi Azure Digital Twins jest przekonwertowanie tabeli na plik CSV. Po przekonwertowaniu tabeli można napisać kod, aby przekształcić plik na polecenia do tworzenia bliźniaków i relacji. Poniższy przykładowy kod ilustruje odczytywanie danych z pliku CSV i tworzenie grafu bliźniaczej reprezentacji w usłudze Azure Digital Twins.

W poniższym kodzie plik CSV jest nazywany data.csvi istnieje symbol zastępczy reprezentujący nazwę hosta wystąpienia usługi Azure Digital Twins. Przykład korzysta również z kilku pakietów, które można dodać do projektu, aby ułatwić ten proces.

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

Przykładowa wykres bliźniaczy z możliwością uruchomienia.

Poniższy fragment kodu wykonywalnego używa operacji na relacjach z tego artykułu w celu utworzenia grafu bliźniaczego z cyfrowych bliźniaków i relacji.

Konfigurowanie przykładowych plików projektu

Fragment kodu używa dwóch przykładowych definicji modelu, Room.json i Floor.json. Aby pobrać pliki modelu, aby można było ich używać w kodzie, użyj tych linków, aby przejść bezpośrednio do plików w usłudze GitHub. Następnie kliknij prawym przyciskiem myszy dowolne miejsce na ekranie, wybierz polecenie Zapisz jako w menu prawym przyciskiem myszy przeglądarki i użyj okna Zapisz jako, aby zapisać pliki jako Room.json i Floor.json.

Następnie utwórz nowy projekt aplikacji konsolowej w programie Visual Studio lub wybranym edytorze.

Następnie skopiuj następujący kod przykładu możliwego do uruchomienia do projektu:

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

Uwaga

Obecnie znany problem wpływa na klasę wrapper DefaultAzureCredential, co może spowodować błąd podczas uwierzytelniania. Jeśli wystąpi ten problem, możesz spróbować utworzyć wystąpienie DefaultAzureCredential za pomocą następującego opcjonalnego parametru, aby rozwiązać ten problem: new DefaultAzureCredential(new DefaultAzureCredentialOptions { ExcludeSharedTokenCacheCredential = true });

Aby uzyskać więcej informacji na temat tego problemu, zobacz Znane problemy usługi Azure Digital Twins.

Konfigurowanie projektu

Następnie wykonaj następujące kroki, aby skonfigurować kod projektu:

  1. Dodaj pliki Room.json i Floor.json pobrane wcześniej do projektu, a następnie zastąp <path-to> symbole zastępcze w kodzie, aby poinformować program, gdzie je znaleźć.

  2. Zastąp symbol zastępczy <your-instance-hostname> nazwą hosta wystąpienia usługi Azure Digital Twins.

  3. Dodaj dwie zależności do projektu, które są potrzebne do pracy z usługą Azure Digital Twins. Pierwszy to pakiet zestawu SDK usługi Azure Digital Twins dla platformy .NET, a drugi udostępnia narzędzia ułatwiające uwierzytelnianie na platformie Azure.

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

Należy również skonfigurować poświadczenia lokalne, jeśli chcesz bezpośrednio uruchomić próbkę. W następnej sekcji przedstawiono ten proces.

Konfigurowanie lokalnych poświadczeń platformy Azure

W tym przykładzie użyto elementu DefaultAzureCredential (część Azure.Identity biblioteki) do uwierzytelniania w wystąpieniu usługi Azure Digital Twins podczas uruchamiania przykładu na komputerze lokalnym. DefaultAzureCredential jest jedną z wielu opcji uwierzytelniania. Aby uzyskać więcej informacji na temat różnych sposobów uwierzytelniania aplikacji klienckiej za pomocą usługi Azure Digital Twins, zobacz Pisanie kodu uwierzytelniania aplikacji.

W DefaultAzureCredentialpróbka wyszukuje poświadczenia w środowisku lokalnym, na przykład logowanie do platformy Azure w lokalnym interfejsie wiersza polecenia platformy Azure lub w programie Visual Studio czy Visual Studio Code. Z tego powodu należy zalogować się do platformy Azure lokalnie za pomocą jednego z tych mechanizmów, aby skonfigurować poświadczenia dla przykładu.

Jeśli używasz programu Visual Studio lub Visual Studio Code do uruchamiania przykładów kodu, upewnij się, że jesteś zalogowany do tego edytora przy użyciu tego samego poświadczenia platformy Azure, którego chcesz użyć do uzyskania dostępu do wystąpienia usługi Azure Digital Twins. Jeśli korzystasz z lokalnego okna interfejsu wiersza poleceń, uruchom polecenie az login, aby zalogować się do swojego konta platformy Azure. Po zalogowaniu przykładowy kod uwierzytelnia Cię automatycznie po uruchomieniu.

Uruchamianie aplikacji przykładowej

Po zakończeniu instalacji możesz uruchomić przykładowy projekt kodu.

Oto dane wyjściowe konsoli programu:

Zrzut ekranu pokazujący dane wyjściowe konsoli ze szczegółami bliźniaczymi oraz relacjami przychodzącymi i wychodzącymi bliźniaków.

Napiwek

Koncepcja bliźniaczego grafu polega na tworzeniu relacji między bliźniakami. Jeśli chcesz wyświetlić wizualizację grafu bliźniaczego, zobacz sekcję Wizualizacja tego artykułu.

Następne kroki

Dowiedz się, jak wykonywać zapytania względem grafu Azure Digital Twins.