자습서: Azure Cosmos DB for NoSQL을 사용하여 .NET 콘솔 애플리케이션 개발
적용 대상: NoSQL
.NET용 Azure SDK를 사용하면 비동기 개별 작업 또는 트랜잭션 일괄 처리로 API for NoSQL 컨테이너에 데이터를 추가할 수 있습니다. 이 자습서에서는 컨테이너에 여러 항목을 추가하는 새 .NET 콘솔 애플리케이션을 만드는 프로세스를 안내합니다.
이 자습서에서는 다음을 하는 방법을 알아볼 수 있습니다.
- API for NoSQL을 사용하여 데이터베이스 만들기
- .NET 콘솔 애플리케이션 만들기 및 .NET용 Azure SDK 추가
- API for NoSQL 컨테이너에 개별 항목 추가
- API for NoSQL 컨테이너에서 효율적인 항목 검색
- API for NoSQL 컨테이너에 대한 일괄 처리 변경 내용이 포함된 트랜잭션 만들기
필수 조건
- 기존 Azure Cosmos DB API for NoSQL 계정.
- 기존 Azure 구독이 있는 경우 새 계정을 만듭니다.
- Azure 구독이 없으신가요? 신용 카드 없이 Azure Cosmos DB를 무료로 사용해 볼 수 있습니다.
- Visual Studio Code
- .NET 8 이상
- C# 애플리케이션 작성 경험
NoSQL 리소스에 대한 API 만들기
먼저 기존 API for NoSQL 계정에 빈 데이터베이스를 만듭니다. 나중에 .NET용 Azure SDK를 사용하여 컨테이너를 만듭니다.
Azure Portal 기존 API for NoSQL 계정으로 이동합니다.
리소스 메뉴에서 키를 선택합니다.
키 페이지에서 URI 및 기본 키 필드의 값을 관찰하고 기록합니다. 이러한 값은 자습서 전체에서 사용됩니다.
리소스 메뉴에서 데이터 탐색기를 선택합니다.
Data Explorer 페이지의 명령 모음에서 새 데이터베이스 옵션을 선택합니다.
새 데이터베이스 대화 상자에서 다음 설정을 사용하여 새 컨테이너를 만듭니다.
값 데이터베이스 ID cosmicworks
데이터베이스 처리량 유형 수동 데이터베이스 처리량 400
데이터베이스를 만들려면 확인을 선택합니다.
.NET 콘솔 애플리케이션 생성
이제 새 .NET 콘솔 애플리케이션을 만들고 NuGet의 라이브러리를 사용하여 .NET용 Azure SDK를 Microsoft.Azure.Cosmos
가져옵니다.
빈 디렉터리에서 터미널을 엽니다.
console
기본 제공 템플릿을 사용하여 새 콘솔 애플리케이션 만들기dotnet new console --langVersion preview
NuGet에서
Microsoft.Azure.Cosmos
패키지의 3.31.1-preview 버전을 추가합니다.dotnet add package Microsoft.Azure.Cosmos --version 3.31.1-preview
NuGet에서
System.CommandLine
패키지의 pre-release 버전을 추가합니다.dotnet add package System.CommandLine --prerelease
또한 NuGet에서
Humanizer
패키지를 추가합니다.dotnet add package Humanizer
콘솔 애플리케이션을 빌드합니다.
dotnet build
현재 프로젝트 폴더를 작업 영역으로 사용하여 Visual Studio Code 엽니다.
팁
터미널에서
code .
을(를) 실행하여 Visual Studio Code를 열고 자동으로 작업 디렉터리를 현재 작업 영역으로 열 수 있습니다.Program.cs 파일로 이동하여 엽니다. 파일의 기존 코드를 모두 삭제합니다.
System.CommandLine 라이브러리를 사용하여
--first
및--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);
참고 항목
이 자습서에서는 명령줄 파서의 작동 방식을 이해하는 것이 전혀 중요하지 않습니다. 파서에는 애플리케이션이 실행 중일 때 지정할 수 있는 네 가지 옵션이 있습니다. ID 및 파티션 키 필드를 생성하는 데 사용되므로 세 가지 옵션이 필요합니다.
이 시점에서는 정적
CosmosHandler.ManageCustomerAsync
메서드를 아직 정의하지 않았으므로 프로젝트가 빌드되지 않습니다.Program.cs 파일을 저장합니다.
SDK를 사용하여 컨테이너에 항목 추가
다음으로 개별 작업을 사용하여 NoSQL 컨테이너용 API에 항목을 추가합니다. 이 섹션에서는 메서드를 정의합니다 CosmosHandler.ManageCustomerAsync
.
새 CosmosHandler.cs 파일을 생성합니다.
CosmosHandler.cs 파일에서
Humanizer
및Microsoft.Azure.Cosmos
네임스페이스에 대한 새 using 지시문을 추가합니다.using Humanizer; using Microsoft.Azure.Cosmos;
이름이
CosmosHandler
인 새 정적 메서드를 만듭니다.public static class CosmosHandler { }
이 앱이 작동하는지 확인하려면 명령줄 입력을 인쇄하는 정적
ManageCustomerAsync
메서드의 짧은 구현을 만듭니다.public static async Task ManageCustomerAsync(string name, string email, string state, string country) { await Console.Out.WriteLineAsync($"Hello {name} of {state}, {country}!"); }
CosmosHandler.cs 파일을 저장합니다.
터미널로 돌아가 애플리케이션을 실행합니다.
dotnet run -- --name 'Mica Pereira' --state 'Washington' --country 'United States'
명령의 출력은 재미있는 인사말이어야 합니다.
Hello Mica Pereira of Washington, United States!
CosmosHandler.cs 파일로 돌아갑니다.
정적 CosmosHandler 클래스 내에서 이름이
_client
인CosmosClient
형식의 새private static readonly
멤버를 추가합니다.private static readonly CosmosClient _client;
CosmosHandler
클래스에 대한 새 정적 생성자를 만듭니다.static CosmosHandler() { }
생성자 내에서 이전에 랩에서 기록한 URI 및 기본 키 값을 사용하여 두 문자열 매개 변수를 전달하는
CosmosClient
클래스의 새 인스턴스를 만듭니다. 이 새 인스턴스를_client
멤버에 저장합니다.static CosmosHandler() { _client = new CosmosClient( accountEndpoint: "<uri>", authKeyOrResourceToken: "<primary-key>" ); }
정적 CosmosHandler 클래스 내에서
Container
을(를) 반환하는 이름이GetContainerAsync
인 새 비동기 메서드를 만듭니다.private static async Task<Container> GetContainerAsync() { }
다음 단계에서는
GetContainerAsync
메서드 내에 이 코드를 추가합니다.cosmicworks
데이터베이스를 가져와서database
변수에 저장합니다.Database database = _client.GetDatabase("cosmicworks");
계층적 파티션 키 경로 목록 내에
string
값의 새 제네릭List<>
을(를) 만들고keyPaths
변수에 저장합니다.List<string> keyPaths = new() { "/address/country", "/address/state" };
컨테이너 이름(
customers
) 및 파티션 키 경로 목록을 사용하여 새ContainerProperties
변수를 만듭니다.ContainerProperties properties = new( id: "customers", partitionKeyPaths: keyPaths );
CreateContainerIfNotExistsAsync
메서드를 사용하여 컨테이너 속성을 제공하고 컨테이너를 검색합니다. 이 메서드는 이름에 따라 컨테이너가 데이터베이스 내에 아직 없는 경우 비동기적으로 만듭니다. 결과를GetContainerAsync
메서드의 출력으로 반환합니다.return await database.CreateContainerIfNotExistsAsync( containerProperties: properties );
ManageCustomerAsync
메서드 내의 모든 코드를 삭제합니다.다음 단계에서는
ManageCustomerAsync
메서드 내에 이 코드를 추가합니다.기본 프로그램 흐름 내에서 다시
GetContainerAsync
메서드를 비동기적으로 호출하고 결과를container
변수에 저장합니다.Container container = await GetContainerAsync();
Humanizer의
Kebaberize
메서드를 사용하여name
메서드 매개 변수를 변환하는id
새 변수를 만듭니다.string id = name.Kebaberize();
참고 항목
Kebaberize
메서드는 모든 공백을 하이픈으로 바꾸고 텍스트를 소문자로 변환합니다.name
,state
,country
메서드 매개 변수 및id
변수를 사용하여 익명 형식의 새 항목을 만듭니다. 항목을customer
변수로 저장합니다.var customer = new { id = id, name = name, address = new { state = state, country = country } };
컨테이너의 비동기
CreateItemAsync
메서드를 사용하여 컨테이너에 새 항목을 만들고 라는response
변수에 HTTP 응답 메타데이터를 할당합니다.var response = await container.CreateItemAsync(customer);
response
변수StatusCode
및RequestCharge
속성 값을 콘솔에 씁니다.id
변수의 값도 씁니다.Console.WriteLine($"[{response.StatusCode}]\t{id}\t{response.RequestCharge} RUs");
CosmosHandler.cs 파일을 저장합니다.
터미널로 돌아가서 애플리케이션을 실행합니다.
dotnet run -- --name 'Mica Pereira' --state 'Washington' --country 'United States'
명령의 출력에는 작업에 대한 상태 및 요청 요금이 포함되어야 합니다.
[Created] mica-pereira 7.05 RUs
참고 항목
요청 요금은 다를 수 있습니다.
애플리케이션을 한 번 더 실행합니다.
dotnet run -- --name 'Mica Pereira' --state 'Washington' --country 'United States'
이번에는 프로그램이 크래시를 일으킬 것입니다. 오류 메시지를 스크롤하면 항목의 고유 식별자에서 충돌이 발생하여 충돌이 발생한 것을 볼 수 있습니다.
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." ] );
SDK를 사용하여 항목 검색
이제 컨테이너에서 첫 번째 항목을 만들었으므로 동일한 SDK를 사용하여 항목을 검색할 수 있습니다. 여기서는 항목을 쿼리하고 포인트 읽기를 하여 RU(요청 단위) 사용량의 차이를 비교합니다.
CosmosHandler.cs 파일로 돌아갑니다.
처음 두 줄을 제외하고
ManageCustomerAsync
메서드에서 모든 코드 줄을 삭제합니다.public static async Task ManageCustomerAsync(string name, string email, string state, string country) { Container container = await GetContainerAsync(); string id = name.Kebaberize(); }
다음 단계에서는
ManageCustomerAsync
메서드 내에 이 코드를 추가합니다.컨테이너의 비동기
CreateItemAsync
메서드를 사용하여 컨테이너에 새 항목을 만들고 라는response
변수에 HTTP 응답 메타데이터를 할당합니다.var response = await container.CreateItemAsync(customer);
SQL 쿼리를 사용하여 이름이
sql
인 새 문자열을 만들어 필터(@id
)가 일치하는 항목을 검색합니다.string sql = @" SELECT * FROM customers c WHERE c.id = @id ";
sql
문자열을 유일한 쿼리 매개 변수로 전달하는 이름이query
인 새QueryDefinition
변수를 만듭니다. 또한WithParameter
fluid 메서드를 사용하여id
변수 값을@id
매개 변수에 적용합니다.var query = new QueryDefinition( query: sql ) .WithParameter("@id", id);
GetItemQueryIterator<>
제네릭 메서드와query
변수를 사용하여 Azure Cosmos DB에서 데이터를 가져오는 반복기를 만듭니다.feed
변수에 반복자를 저장합니다. 이 전체 식을 using 문으로 래핑하여 나중에 반복기를 삭제합니다.using var feed = container.GetItemQueryIterator<dynamic>( queryDefinition: query );
feed
변수의ReadNextAsync
메서드를 비동기적으로 호출하고 결과를response
라는 변수에 저장합니다.var response = await feed.ReadNextAsync();
response
변수StatusCode
및RequestCharge
속성 값을 콘솔에 씁니다.id
변수의 값도 씁니다.Console.WriteLine($"[{response.StatusCode}]\t{id}\t{response.RequestCharge} RUs");
CosmosHandler.cs 파일을 저장합니다.
터미널로 돌아가서 애플리케이션을 실행하여 SQL 쿼리를 사용하여 단일 항목을 읽습니다.
dotnet run -- --name 'Mica Pereira' --state 'Washington' --country 'United States'
명령의 출력은 쿼리에 RU(여러 요청 단위)가 필요함을 나타내야 합니다.
[OK] mica-pereira 2.82 RUs
CosmosHandler.cs 파일로 돌아가서 처음 두 줄을 제외하고
ManageCustomerAsync
메서드에서 모든 코드 줄을 다시 삭제합니다.public static async Task ManageCustomerAsync(string name, string email, string state, string country) { Container container = await GetContainerAsync(); string id = name.Kebaberize(); }
다음 단계에서는
ManageCustomerAsync
메서드 내에 이 코드를 추가합니다.state
및country
매개 변수를 다중 파트 파티션 키 값으로 추가하여 새PartitionKeyBuilder
인스턴스를 만듭니다.var partitionKey = new PartitionKeyBuilder() .Add(country) .Add(state) .Build();
컨테이너의
ReadItemAsync<>
메서드를 사용하여id
및partitionKey
변수로 컨테이너에서 항목을 읽습니다. 결과를response
변수에 저장합니다.var response = await container.ReadItemAsync<dynamic>( id: id, partitionKey: partitionKey );
response
변수StatusCode
및RequestCharge
속성 값을 콘솔에 씁니다.id
변수의 값도 씁니다.Console.WriteLine($"[{response.StatusCode}]\t{id}\t{response.RequestCharge} RU");
CosmosHandler.cs 파일을 다시 저장합니다.
터미널로 돌아가서 애플리케이션을 한 번 더 실행하여 단일 항목을 가리킵니다.
dotnet run -- --name 'Mica Pereira' --state 'Washington' --country 'United States'
명령의 출력은 쿼리에 단일 RU가 필요함을 나타낼 것입니다.
[OK] mica-pereira 1 RUs
SDK를 사용하여 트랜잭션 만들기
마지막으로 만든 항목을 가져와서 해당 항목을 읽고 .NET용 Azure SDK를 사용하여 단일 트랜잭션의 일부로 다른 관련 항목을 만듭니다.
CosmosHandler.cs 파일로 돌아갑니다.
ManageCustomerAsync
메서드에서 코드 줄을 삭제합니다.var response = await container.ReadItemAsync<dynamic>( id: id, partitionKey: partitionKey ); Console.WriteLine($"[{response.StatusCode}]\t{id}\t{response.RequestCharge} RUs");
다음 단계에서는
ManageCustomerAsync
메서드 내에 이 코드를 추가합니다.name
,state
,country
메서드 매개 변수 및id
변수를 사용하여 익명 형식의 새 항목을 만듭니다. 항목을customerCart
변수로 저장합니다. 이 항목은 현재 비어 있는 고객의 실시간 쇼핑 카트를 나타냅니다.var customerCart = new { id = $"{Guid.NewGuid()}", customerId = id, items = new string[] {}, address = new { state = state, country = country } };
name
,state
,country
메서드 매개 변수 및id
변수를 사용하여 익명 형식의 새 항목을 하나 더 만듭니다. 항목을customerCart
변수로 저장합니다. 이 항목은 고객의 배송 및 연락처 정보를 나타냅니다.var customerContactInfo = new { id = $"{id}-contact", customerId = id, email = email, location = $"{state}, {country}", address = new { state = state, country = country } };
partitionKey
변수를 전달하는 컨테이너의CreateTransactionalBatch
메서드를 사용하여 새 일괄 처리를 만듭니다.batch
변수에 일괄 처리를 저장합니다. 흐름 메서드를 사용하여 다음 작업을 수행합니다.메서드 매개 변수 ReadItem
id
문자열 변수CreateItem
customerCart
익명 형식 변수CreateItem
customerContactInfo
익명 형식 변수var batch = container.CreateTransactionalBatch(partitionKey) .ReadItem(id) .CreateItem(customerCart) .CreateItem(customerContactInfo);
일괄 처리의
ExecuteAsync
메서드를 사용하여 트랜잭션을 시작합니다. 결과를response
변수에 저장합니다.using var response = await batch.ExecuteAsync();
response
변수StatusCode
및RequestCharge
속성 값을 콘솔에 씁니다.id
변수의 값도 씁니다.Console.WriteLine($"[{response.StatusCode}]\t{response.RequestCharge} RUs");
CosmosHandler.cs 파일을 다시 저장합니다.
터미널로 돌아가서 애플리케이션을 한 번 더 실행하여 단일 항목을 가리킵니다.
dotnet run -- --name 'Mica Pereira' --state 'Washington' --country 'United States'
명령의 출력에는 전체 트랜잭션에 사용되는 요청 단위가 표시됩니다.
[OK] 16.05 RUs
참고 항목
요청 요금은 다를 수 있습니다.
Data Explorer 최종 데이터의 유효성을 검사합니다.
작업을 마무리하려면 Azure Portal의 데이터 탐색기를 사용하여 이 자습서에서 만든 데이터 및 컨테이너를 봅니다.
Azure Portal 기존 API for NoSQL 계정으로 이동합니다.
리소스 메뉴에서 데이터 탐색기를 선택합니다.
Data Explorer 페이지에서
cosmicworks
데이터베이스를 펼치고customers
컨테이너를 선택합니다.명령 모음에서 새 SQL 쿼리를 선택합니다.
쿼리 편집기에서 이 SQL 쿼리 문자열을 관찰합니다.
SELECT * FROM c
쿼리 실행을 선택하여 쿼리를 실행하고 결과를 관찰합니다.
결과에는 이 자습서에서 만든 세 개의 항목이 있는 JSON 배열이 포함되어야 합니다. 모든 항목에 동일한 계층적 파티션 키 값이 있지만 고유 ID 필드가 있는지 확인합니다. 포함된 예제 출력은 간단히 나타내기 위해 잘립니다.
[ { "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" }, ... } ]
리소스 정리
더 이상 필요하지 않은 경우 이 문서에서 사용된 데이터베이스를 삭제합니다. 이렇게 하려면 계정 페이지로 이동하고 Data Explorer를 선택하고, cosmicworks
데이터베이스를 선택한 다음, 삭제를 선택합니다.