Esercitazione: Sviluppare un'applicazione console .NET con Azure Cosmos DB per NoSQL
SI APPLICA A: NoSQL
Azure SDK per .NET consente di aggiungere dati a un contenitore API per NoSQL sia con singole operazioni asincrone che con un batch transazionale. Questa esercitazione illustra il processo di creazione di una nuova applicazione console .NET che aggiunge più elementi a un contenitore.
In questa esercitazione apprenderai a:
- Creare un database usando l'API per NoSQL
- Creare un'applicazione console .NET e aggiungere Azure SDK per .NET
- Aggiungere singoli elementi in un contenitore API per NoSQL
- Recuperare elementi efficienti da un contenitore API per NoSQL
- Creare una transazione con modifiche batch per il contenitore API per NoSQL
Prerequisiti
- Un account Azure Cosmos DB for NoSQL già presente.
- Se si ha già una sottoscrizione di Azure, creare un nuovo account.
- Se non si ha una sottoscrizione di Azure, è possibile provare Azure Cosmos DB gratuitamente senza necessità di carta di credito.
- Visual Studio Code
- .NET 8 o versione successiva
- Esperienza nella scrittura di applicazioni C#.
Creare un'API per le risorse NoSQL
Creare prima di tutto un database vuoto nell'account API esistente per NoSQL. Si crea un contenitore usando Azure SDK per .NET in un secondo momento.
Passare all'account API esistente per NoSQL nel portale di Azure.
Nel menu della risorsa selezionare Chiavi.
Nella pagina Chiavi osservare e registrare il valore dei campi URI e CHIAVE PRIMARIA. Questi valori vengono usati in tutta l'esercitazione.
Nel menu della risorsa selezionare Esplora dati.
Nella pagina Esplora dati selezionare l'opzione Nuovo database nella barra dei comandi.
Nella finestra di dialogo Nuovo database creare un nuovo contenitore con le impostazioni seguenti:
Valore ID database cosmicworks
Tipo di velocità effettiva del database Manualee Quantità di velocità effettiva del database 400
Selezionare OK per creare il database.
Creare un'applicazione console .NET
A questo punto, si crea una nuova applicazione console .NET e si importa Azure SDK per .NET usando la Microsoft.Azure.Cosmos
libreria da NuGet.
Aprire un terminale in una directory vuota.
Creare una nuova applicazione console usando il modello predefinito
console
dotnet new console --langVersion preview
Aggiungere la versione 3.31.1-preview del pacchetto
Microsoft.Azure.Cosmos
da NuGet.dotnet add package Microsoft.Azure.Cosmos --version 3.31.1-preview
Aggiungere anche la versione non definitiva del pacchetto
System.CommandLine
da NuGet.dotnet add package System.CommandLine --prerelease
Aggiungere anche il pacchetto
Humanizer
da NuGet.dotnet add package Humanizer
Compilare il progetto dell'applicazione console.
dotnet build
Aprire Visual Studio Code usando la cartella del progetto corrente come area di lavoro.
Suggerimento
È possibile eseguire
code .
nel terminale per aprire Visual Studio Code e aprire automaticamente la directory di lavoro come area di lavoro corrente.Passare a e aprire il file Program.cs. Eliminare tutto il codice esistente nel file.
Aggiungere questo codice al file per usare la libreria System.CommandLine per analizzare la riga di comando per due stringhe passate tramite le opzioni
--first
e--last
.using System.CommandLine; var command = new RootCommand(); var nameOption = new Option<string>("--name") { IsRequired = true }; var emailOption = new Option<string>("--email"); var stateOption = new Option<string>("--state") { IsRequired = true }; var countryOption = new Option<string>("--country") { IsRequired = true }; command.AddOption(nameOption); command.AddOption(emailOption); command.AddOption(stateOption); command.AddOption(countryOption); command.SetHandler( handle: CosmosHandler.ManageCustomerAsync, nameOption, emailOption, stateOption, countryOption ); await command.InvokeAsync(args);
Nota
Per questa esercitazione, non è del tutto importante comprendere il funzionamento del parser della riga di comando. Il parser dispone di quattro opzioni che possono essere specificate quando l'applicazione è in esecuzione. Sono necessarie tre delle opzioni perché verranno usate per costruire i campi ID e chiave di partizione.
A questo punto, il progetto non verrà compilato perché non è ancora stato definito il metodo
CosmosHandler.ManageCustomerAsync
statico.Salvare il file Program.cs.
Aggiungere elementi a un contenitore usando l'SDK
Successivamente, si usano singole operazioni per aggiungere elementi al contenitore API per NoSQL. In questa sezione viene definito il CosmosHandler.ManageCustomerAsync
metodo .
Creare un nuovo file CosmosHandler.cs.
Nel file CosmosHandler.cs aggiungere una nuova direttiva using per gli spazi dei nomi
Humanizer
eMicrosoft.Azure.Cosmos
.using Humanizer; using Microsoft.Azure.Cosmos;
Creare una nuova classe statica denominata
CosmosHandler
.public static class CosmosHandler { }
Per convalidare il funzionamento di questa app, creare una breve implementazione del metodo statico
ManageCustomerAsync
per stampare l'input della riga di comando.public static async Task ManageCustomerAsync(string name, string email, string state, string country) { await Console.Out.WriteLineAsync($"Hello {name} of {state}, {country}!"); }
Salvare il file CosmosHandler.cs.
Tornare al terminale ed eseguire l'applicazione.
dotnet run -- --name 'Mica Pereira' --state 'Washington' --country 'United States'
L'output del comando deve essere un messaggio di saluto divertente.
Hello Mica Pereira of Washington, United States!
Tornare al file CosmosHandler.cs.
All'interno della classe CosmosHandler statica, aggiungere un nuovo membro
private static readonly
di tipoCosmosClient
denominato_client
.private static readonly CosmosClient _client;
Creare un nuovo costruttore statico per la classe
CosmosHandler
.static CosmosHandler() { }
All'interno del costruttore creare una nuova istanza della classe
CosmosClient
passando due parametri stringa con i valori URI e PRIMARY KEY registrati in precedenza nel lab. Archiviare questa nuova istanza nel membro_client
.static CosmosHandler() { _client = new CosmosClient( accountEndpoint: "<uri>", authKeyOrResourceToken: "<primary-key>" ); }
Tornare all'interno della classe CosmosHandler statica, creare un nuovo metodo asincrono denominato
GetContainerAsync
che restituisce un oggettoContainer
.private static async Task<Container> GetContainerAsync() { }
Per i passaggi successivi, aggiungere questo codice all'interno del metodo
GetContainerAsync
.Ottenere il database
cosmicworks
e archiviarlo in una variabile denominatadatabase
.Database database = _client.GetDatabase("cosmicworks");
Creare una nuova
List<>
generica di valoristring
all'interno di un elenco di percorsi di chiave di partizione gerarchici e archiviarla in una variabile denominatakeyPaths
.List<string> keyPaths = new() { "/address/country", "/address/state" };
Creare una nuova variabile
ContainerProperties
con il nome del contenitore (customers
) e l'elenco dei percorsi delle chiavi di partizione.ContainerProperties properties = new( id: "customers", partitionKeyPaths: keyPaths );
Usare il metodo
CreateContainerIfNotExistsAsync
per fornire le proprietà del contenitore e recuperare il contenitore. Questo metodo, come dice il nome, creerà in modo asincrono il contenitore se non esiste già all'interno del database. Restituisce il risultato come output del metodoGetContainerAsync
.return await database.CreateContainerIfNotExistsAsync( containerProperties: properties );
Eliminare tutto il codice all'interno del metodo
ManageCustomerAsync
.Per i passaggi successivi, aggiungere questo codice all'interno del metodo
ManageCustomerAsync
.Chiamare in modo asincrono il metodo
GetContainerAsync
e archiviare il risultato in una variabile denominatacontainer
.Container container = await GetContainerAsync();
Creare una nuova variabile denominata
id
che usa il metodoKebaberize
da Humanizer per trasformare il parametro del metodoname
.string id = name.Kebaberize();
Nota
Il metodo
Kebaberize
sostituirà tutti gli spazi con trattini e convertirà il testo in minuscolo.Creare un nuovo elemento tipizzato anonimo usando i parametri del metodo
name
,state
ecountry
e la variabileid
. Archiviare l'elemento come variabile denominatacustomer
.var customer = new { id = id, name = name, address = new { state = state, country = country } };
Usare il metodo
CreateItemAsync
asincrono del contenitore per creare un nuovo elemento nel contenitore e assegnare i metadati della risposta HTTP a una variabile denominataresponse
.var response = await container.CreateItemAsync(customer);
Scrivere i valori delle proprietà
StatusCode
eRequestCharge
della variabile diresponse
nella console. Scrivere anche il valore della variabileid
.Console.WriteLine($"[{response.StatusCode}]\t{id}\t{response.RequestCharge} RUs");
Salvare il file CosmosHandler.cs.
Tornare al terminale, eseguire di nuovo l'applicazione.
dotnet run -- --name 'Mica Pereira' --state 'Washington' --country 'United States'
L'output del comando deve includere uno stato e un addebito per la richiesta per l'operazione.
[Created] mica-pereira 7.05 RUs
Nota
L'addebito della richiesta può variare.
Eseguire l'applicazione ancora una volta.
dotnet run -- --name 'Mica Pereira' --state 'Washington' --country 'United States'
Questa volta, il programma dovrebbe arrestarsi in modo anomalo. Se si scorre il messaggio di errore, viene visualizzato l'arresto anomalo a causa di un conflitto nell'identificatore univoco per gli elementi.
Unhandled exception: Microsoft.Azure.Cosmos.CosmosException : Response status code does not indicate success: Conflict (409);Reason: ( Errors : [ "Resource with specified id or name already exists." ] );
Recuperare un elemento usando l'SDK
Dopo aver creato il primo elemento nel contenitore, è possibile usare lo stesso SDK per recuperare l'elemento. In questo caso, si eseguirà una query e si leggerà l'elemento per confrontare la differenza nel consumo di unità richiesta (UR).
Tornare o aprire il file CosmosHandler.cs.
Eliminare tutte le righe di codice dal metodo
ManageCustomerAsync
ad eccezione delle prime due righe.public static async Task ManageCustomerAsync(string name, string email, string state, string country) { Container container = await GetContainerAsync(); string id = name.Kebaberize(); }
Per i passaggi successivi, aggiungere questo codice all'interno del metodo
ManageCustomerAsync
.Usare il metodo
CreateItemAsync
asincrono del contenitore per creare un nuovo elemento nel contenitore e assegnare i metadati della risposta HTTP a una variabile denominataresponse
.var response = await container.CreateItemAsync(customer);
Creare una nuova stringa denominata
sql
con una query SQL per recuperare gli elementi in cui corrisponde un filtro (@id
).string sql = @" SELECT * FROM customers c WHERE c.id = @id ";
Creare una nuova variabile di
QueryDefinition
denominataquery
passando la stringasql
come unico parametro di query. Usare inoltre il metodo fluidoWithParameter
per applicare il valore della variabileid
al parametro@id
.var query = new QueryDefinition( query: sql ) .WithParameter("@id", id);
Usare il metodo generico
GetItemQueryIterator<>
e la variabilequery
per creare un iteratore che ottiene dati da Azure Cosmos DB. Archiviare l'iteratore in una variabile denominatafeed
. Eseguire il wrapping di questa intera espressione in un'istruzione using per eliminare l'iteratore in un secondo momento.using var feed = container.GetItemQueryIterator<dynamic>( queryDefinition: query );
Chiamare in modo asincrono il metodo
ReadNextAsync
della variabilefeed
e archiviare il risultato in una variabile denominataresponse
.var response = await feed.ReadNextAsync();
Scrivere i valori delle proprietà
StatusCode
eRequestCharge
della variabile diresponse
nella console. Scrivere anche il valore della variabileid
.Console.WriteLine($"[{response.StatusCode}]\t{id}\t{response.RequestCharge} RUs");
Salvare il file CosmosHandler.cs.
Tornando al terminale, eseguire l'applicazione per leggere il singolo elemento usando una query SQL.
dotnet run -- --name 'Mica Pereira' --state 'Washington' --country 'United States'
L'output del comando deve indicare che la query richiede più unità richiesta (UR).
[OK] mica-pereira 2.82 RUs
Tornare al file CosmosHandler.cs, eliminare di nuovo tutte le righe di codice dal metodo
ManageCustomerAsync
ad eccezione delle prime due righe.public static async Task ManageCustomerAsync(string name, string email, string state, string country) { Container container = await GetContainerAsync(); string id = name.Kebaberize(); }
Per i passaggi successivi, aggiungere questo codice all'interno del metodo
ManageCustomerAsync
.Creare una nuova istanza di
PartitionKeyBuilder
aggiungendo i parametristate
ecountry
come valore della chiave di partizione in più parti.var partitionKey = new PartitionKeyBuilder() .Add(country) .Add(state) .Build();
Usare il metodo
ReadItemAsync<>
del contenitore per puntare a leggere l'elemento dal contenitore usando le variabiliid
epartitionKey
. Salvare il risultato in una variabile denominataresponse
.var response = await container.ReadItemAsync<dynamic>( id: id, partitionKey: partitionKey );
Scrivere i valori delle proprietà
StatusCode
eRequestCharge
della variabile diresponse
nella console. Scrivere anche il valore della variabileid
.Console.WriteLine($"[{response.StatusCode}]\t{id}\t{response.RequestCharge} RU");
Salvare di nuovo il file CosmosHandler.cs.
Tornare al terminale, eseguire l'applicazione ancora una volta per puntare a leggere il singolo elemento.
dotnet run -- --name 'Mica Pereira' --state 'Washington' --country 'United States'
L'output del comando deve indicare che la query richiede una singola UR.
[OK] mica-pereira 1 RUs
Creare una transazione usando l'SDK
Infine, si prende l'elemento creato, si legge tale elemento e si crea un elemento correlato diverso come parte di una singola transazione usando Azure SDK per .NET.
Tornare o aprire il file CosmosHandler.cs.
Eliminare queste righe di codice dal metodo
ManageCustomerAsync
.var response = await container.ReadItemAsync<dynamic>( id: id, partitionKey: partitionKey ); Console.WriteLine($"[{response.StatusCode}]\t{id}\t{response.RequestCharge} RUs");
Per i passaggi successivi, aggiungere questo nuovo codice all'interno del metodo
ManageCustomerAsync
.Creare un nuovo elemento tipizzato anonimo usando i parametri del metodo
name
,state
ecountry
e la variabileid
. Archiviare l'elemento come variabile denominatacustomerCart
. Questo articolo rappresenta un carrello acquisti in tempo reale per il cliente attualmente vuoto.var customerCart = new { id = $"{Guid.NewGuid()}", customerId = id, items = new string[] {}, address = new { state = state, country = country } };
Creare un altro nuovo elemento tipizzato anonimo usando i parametri del metodo
name
,state
ecountry
e la variabileid
. Archiviare l'elemento come variabile denominatacustomerCart
. Questo articolo rappresenta le informazioni di spedizione e contatto per il cliente.var customerContactInfo = new { id = $"{id}-contact", customerId = id, email = email, location = $"{state}, {country}", address = new { state = state, country = country } };
Creare un nuovo batch usando il metodo
CreateTransactionalBatch
del contenitore passando la variabilepartitionKey
. Archiviare il batch in una variabile denominatabatch
. Usare i metodi Fluent per eseguire le azioni seguenti:metodo Parametro ReadItem
id
variabile stringaCreateItem
customerCart
variabile di tipo anonimoCreateItem
customerContactInfo
variabile di tipo anonimovar batch = container.CreateTransactionalBatch(partitionKey) .ReadItem(id) .CreateItem(customerCart) .CreateItem(customerContactInfo);
Usare il metodo
ExecuteAsync
del batch per avviare la transazione. Salvare il risultato in una variabile denominataresponse
.using var response = await batch.ExecuteAsync();
Scrivere i valori delle proprietà
StatusCode
eRequestCharge
della variabile diresponse
nella console. Scrivere anche il valore della variabileid
.Console.WriteLine($"[{response.StatusCode}]\t{response.RequestCharge} RUs");
Salvare di nuovo il file CosmosHandler.cs.
Tornare al terminale, eseguire l'applicazione ancora una volta per puntare a leggere il singolo elemento.
dotnet run -- --name 'Mica Pereira' --state 'Washington' --country 'United States'
L'output del comando deve mostrare le unità richiesta usate per l'intera transazione.
[OK] 16.05 RUs
Nota
L'addebito della richiesta può variare.
Convalidare i dati finali in Esplora dati
Per riepilogare gli elementi, usare il Esplora dati nella portale di Azure per visualizzare i dati e il contenitore creati in questa esercitazione.
Passare all'account API esistente per NoSQL nel portale di Azure.
Nel menu della risorsa selezionare Esplora dati.
Nella pagina Esplora dati espandere il database
cosmicworks
e quindi selezionare il contenitorecustomers
.Nella barra dei comandi selezionare Nuova query SQL.
Nell'editor di query osservare questa stringa di query SQL.
SELECT * FROM c
Selezionare Esegui query per eseguire la query e osservare i risultati.
I risultati devono includere una matrice JSON con tre elementi creati in questa esercitazione. Si noti che tutti gli elementi hanno lo stesso valore di chiave di partizione gerarchica, ma campi ID univoci. L'output di esempio incluso viene troncato per brevità.
[ { "id": "mica-pereira", "name": "Mica Pereira", "address": { "state": "Washington", "country": "United States" }, ... }, { "id": "33d03318-6302-4559-b5c0-f3cc643b2f38", "customerId": "mica-pereira", "items": [], "address": { "state": "Washington", "country": "United States" }, ... }, { "id": "mica-pereira-contact", "customerId": "mica-pereira", "email": null, "location": "Washington, United States", "address": { "state": "Washington", "country": "United States" }, ... } ]
Pulire le risorse
Quando non è più necessario, eliminare il database usato in questa esercitazione. A tale scopo, passare alla pagina account, selezionare Esplora dati, selezionare il database cosmicworks
e quindi selezionare Elimina.