자습서: 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 컨테이너에 대한 일괄 처리 변경 내용이 포함된 트랜잭션 만들기

필수 조건

NoSQL 리소스에 대한 API 만들기

먼저 기존 API for NoSQL 계정에 빈 데이터베이스를 만듭니다. 나중에 .NET용 Azure SDK를 사용하여 컨테이너를 만듭니다.

  1. Azure Portal 기존 API for NoSQL 계정으로 이동합니다.

  2. 리소스 메뉴에서 를 선택합니다.

    Screenshot of an API for NoSQL account page. The Keys option is highlighted in the resource menu.

  3. 페이지에서 URI기본 키 필드의 값을 관찰하고 기록합니다. 이러한 값은 자습서 전체에서 사용됩니다.

    Screenshot of the Keys page with the URI and Primary Key fields highlighted.

  4. 리소스 메뉴에서 데이터 탐색기를 선택합니다.

    Screenshot of the Data Explorer option highlighted in the resource menu.

  5. Data Explorer 페이지의 명령 모음에서 새 데이터베이스 옵션을 선택합니다.

    Screenshot of the New Database option in the Data Explorer command bar.

  6. 새 데이터베이스 대화 상자에서 다음 설정을 사용하여 새 컨테이너를 만듭니다.

    데이터베이스 ID cosmicworks
    데이터베이스 처리량 유형 수동
    데이터베이스 처리량 400

    Screenshot of the New Database dialog in the Data Explorer with various values in each field.

  7. 데이터베이스를 만들려면 확인을 선택합니다.

.NET 콘솔 애플리케이션 생성

이제 NuGet의 Microsoft.Azure.Cosmos 라이브러리를 사용하여 새 .NET 콘솔 애플리케이션을 만들고 .NET용 Azure SDK를 가져옵니다.

  1. 빈 디렉터리에서 터미널을 엽니다.

  2. console 기본 제공 템플릿을 사용하여 새 콘솔 애플리케이션 만들기

    dotnet new console --langVersion preview
    
  3. NuGet에서 Microsoft.Azure.Cosmos 패키지의 3.31.1-preview 버전을 추가합니다.

    dotnet add package Microsoft.Azure.Cosmos --version 3.31.1-preview
    
  4. NuGet에서 System.CommandLine 패키지의 pre-release 버전을 추가합니다.

    dotnet add package System.CommandLine --prerelease
    
  5. 또한 NuGet에서 Humanizer 패키지를 추가합니다.

    dotnet add package Humanizer
    
  6. 콘솔 애플리케이션을 빌드합니다.

    dotnet build
    
  7. 현재 프로젝트 폴더를 작업 영역으로 사용하여 Visual Studio Code 엽니다.

    터미널에서 code .을(를) 실행하여 Visual Studio Code를 열고 자동으로 작업 디렉터리를 현재 작업 영역으로 열 수 있습니다.

  8. Program.cs 파일로 이동하여 엽니다. 파일의 기존 코드를 모두 삭제합니다.

  9. 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 및 파티션 키 필드를 생성하는 데 사용되므로 세 가지 옵션이 필요합니다.

  10. 이 시점에서는 정적 CosmosHandler.ManageCustomerAsync 메서드를 아직 정의하지 않았으므로 프로젝트가 빌드되지 않습니다.

  11. Program.cs 파일을 저장합니다.

SDK를 사용하여 컨테이너에 항목 추가

다음으로 개별 작업을 사용하여 API for NoSQL 컨테이너에 항목을 추가합니다. 이 섹션에서는 CosmosHandler.ManageCustomerAsync 메서드를 정의합니다.

  1. CosmosHandler.cs 파일을 생성합니다.

  2. CosmosHandler.cs 파일에서 HumanizerMicrosoft.Azure.Cosmos 네임스페이스에 대한 새 using 지시문을 추가합니다.

    using Humanizer;
    using Microsoft.Azure.Cosmos;
    
  3. 이름이 CosmosHandler인 새 정적 메서드를 만듭니다.

    public static class CosmosHandler
    { }
    
  4. 이 앱이 작동하는지 유효성을 검사하기 위해 명령줄 입력을 인쇄하는 정적 ManageCustomerAsync 메서드의 짧은 구현을 만듭니다.

    public static async Task ManageCustomerAsync(string name, string email, string state, string country)
    {
        await Console.Out.WriteLineAsync($"Hello {name} of {state}, {country}!");
    }
    
  5. CosmosHandler.cs 파일을 저장합니다.

  6. 터미널로 돌아가 애플리케이션을 실행합니다.

    dotnet run -- --name 'Mica Pereira' --state 'Washington' --country 'United States'
    
  7. 명령의 출력은 재미있는 인사말이어야 합니다.

    Hello Mica Pereira of Washington, United States!
    
  8. CosmosHandler.cs 파일로 돌아갑니다.

  9. 정적 CosmosHandler 클래스 내에서 이름이 _clientCosmosClient 형식의 새 private static readonly 멤버를 추가합니다.

    private static readonly CosmosClient _client;
    
  10. CosmosHandler 클래스에 대한 새 정적 생성자를 만듭니다.

    static CosmosHandler()
    { }
    
  11. 생성자 내에서 이전에 랩에서 기록한 URI기본 키 값을 사용하여 두 문자열 매개 변수를 전달하는 CosmosClient 클래스의 새 인스턴스를 만듭니다. 이 새 인스턴스를 _client 멤버에 저장합니다.

    static CosmosHandler()
    {
        _client = new CosmosClient(
            accountEndpoint: "<uri>", 
            authKeyOrResourceToken: "<primary-key>"
        );
    }
    
  12. 정적 CosmosHandler 클래스 내에서 Container을(를) 반환하는 이름이 GetContainerAsync인 새 비동기 메서드를 만듭니다.

    private static async Task<Container> GetContainer()
    { }
    
  13. 다음 단계에서는 GetContainerAsync 메서드 내에 이 코드를 추가합니다.

    1. cosmicworks 데이터베이스를 가져와서 database 변수에 저장합니다.

      Database database = _client.GetDatabase("cosmicworks");
      
    2. 계층적 파티션 키 경로 목록 내에 string 값의 새 제네릭 List<>을(를) 만들고 keyPaths 변수에 저장합니다.

      List<string> keyPaths = new()
      {
          "/address/country",
          "/address/state"
      };
      
    3. 컨테이너 이름(customers) 및 파티션 키 경로 목록을 사용하여 새 ContainerProperties 변수를 만듭니다.

      ContainerProperties properties = new(
          id: "customers",
          partitionKeyPaths: keyPaths
      );
      
    4. CreateContainerIfNotExistsAsync 메서드를 사용하여 컨테이너 속성을 제공하고 컨테이너를 검색합니다. 이 메서드는 이름에 따라 컨테이너가 데이터베이스 내에 아직 없는 경우 비동기적으로 만듭니다. 결과를 GetContainerAsync 메서드의 출력으로 반환합니다.

      return await database.CreateContainerIfNotExistsAsync(
          containerProperties: properties
      );
      
  14. ManageCustomerAsync 메서드 내의 모든 코드를 삭제합니다.

  15. 다음 단계에서는 ManageCustomerAsync 메서드 내에 이 코드를 추가합니다.

    1. 기본 프로그램 흐름 내에서 다시 GetContainerAsync 메서드를 비동기적으로 호출하고 결과를 container 변수에 저장합니다.

      Container container = await GetContainerAsync();
      
    2. HumanizerKebaberize 메서드를 사용하여 name 메서드 매개 변수를 변환하는 id 새 변수를 만듭니다.

      string id = name.Kebaberize();
      

      참고 항목

      Kebaberize 메서드는 모든 공백을 하이픈으로 바꾸고 텍스트를 소문자로 변환합니다.

    3. name, state, country 메서드 매개 변수 및 id 변수를 사용하여 익명 형식의 새 항목을 만듭니다. 항목을 customer 변수로 저장합니다.

      var customer = new {
          id = id,
          name = name,
          address = new {
              state = state,
              country = country
          }
      };
      
    4. 컨테이너의 비동기 CreateItemAsync 메서드를 사용하여 컨테이너에 새 항목을 만들고 라는 response 변수에 HTTP 응답 메타데이터를 할당합니다.

      var response = await container.CreateItemAsync(customer);
      
    5. response 변수 StatusCodeRequestCharge 속성 값을 콘솔에 씁니다. id 변수의 값도 씁니다.

      Console.WriteLine($"[{response.StatusCode}]\t{id}\t{response.RequestCharge} RUs");
      
  16. CosmosHandler.cs 파일을 저장합니다.

  17. 터미널로 돌아가서 애플리케이션을 실행합니다.

    dotnet run -- --name 'Mica Pereira' --state 'Washington' --country 'United States'
    
  18. 명령의 출력에는 작업에 대한 상태 및 요청 요금이 포함되어야 합니다.

    [Created]       mica-pereira    7.05 RUs
    

    참고 항목

    요청 요금은 다를 수 있습니다.

  19. 애플리케이션을 한 번 더 실행합니다.

    dotnet run -- --name 'Mica Pereira' --state 'Washington' --country 'United States'
    
  20. 이번에는 프로그램이 크래시를 일으킬 것입니다. 오류 메시지를 스크롤하면 항목의 고유 식별자에서 충돌이 발생하여 크래시가 일어난 것을 볼 수 있습니다.

    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(요청 단위) 사용량의 차이를 비교합니다.

  1. CosmosHandler.cs 파일로 돌아갑니다.

  2. 처음 두 줄을 제외하고 ManageCustomerAsync 메서드에서 모든 코드 줄을 삭제합니다.

    public static async Task ManageCustomerAsync(string name, string email, string state, string country)
    {
        Container container = await GetContainerAsync();
    
        string id = name.Kebaberize();
    }
    
  3. 다음 단계에서는 ManageCustomerAsync 메서드 내에 이 코드를 추가합니다.

    1. 컨테이너의 비동기 CreateItemAsync 메서드를 사용하여 컨테이너에 새 항목을 만들고 라는 response 변수에 HTTP 응답 메타데이터를 할당합니다.

      var response = await container.CreateItemAsync(customer);
      
    2. SQL 쿼리를 사용하여 이름이 sql인 새 문자열을 만들어 필터(@id)가 일치하는 항목을 검색합니다.

      string sql = """
      SELECT
          *
      FROM customers c
      WHERE c.id = @id
      """;
      
    3. sql 문자열을 유일한 쿼리 매개 변수로 전달하는 이름이 query인 새 QueryDefinition 변수를 만듭니다. 또한 WithParameter fluid 메서드를 사용하여 id 변수 값을 @id 매개 변수에 적용합니다.

      var query = new QueryDefinition(
          query: sql
      )
          .WithParameter("@id", id);
      
    4. GetItemQueryIterator<> 제네릭 메서드와query 변수를 사용하여 Azure Cosmos DB에서 데이터를 가져오는 반복기를 만듭니다. feed 변수에 반복자를 저장합니다. 이 전체 식을 using 문으로 래핑하여 나중에 반복기를 삭제합니다.

      using var feed = container.GetItemQueryIterator<dynamic>(
          queryDefinition: query
      );
      
    5. feed 변수의 ReadNextAsync 메서드를 비동기적으로 호출하고 결과를 response라는 변수에 저장합니다.

      var response = await feed.ReadNextAsync();
      
    6. response 변수 StatusCodeRequestCharge 속성 값을 콘솔에 씁니다. id 변수의 값도 씁니다.

      Console.WriteLine($"[{response.StatusCode}]\t{id}\t{response.RequestCharge} RUs");
      
  4. CosmosHandler.cs 파일을 저장합니다.

  5. 터미널로 돌아가서 애플리케이션을 실행하여 SQL 쿼리를 사용하여 단일 항목을 읽습니다.

    dotnet run -- --name 'Mica Pereira'
    
  6. 명령의 출력은 쿼리에 여러 RU가 필요함을 나타낼 것입니다.

    [OK]    mica-pereira    2.82 RUs
    
  7. CosmosHandler.cs 파일로 돌아가서 처음 두 줄을 제외하고 ManageCustomerAsync 메서드에서 모든 코드 줄을 다시 삭제합니다.

    public static async Task ManageCustomerAsync(string name, string email, string state, string country)
    {
        Container container = await GetContainerAsync();
    
        string id = name.Kebaberize();
    }
    
  8. 다음 단계에서는 ManageCustomerAsync 메서드 내에 이 코드를 추가합니다.

    1. statecountry 매개 변수를 다중 파트 파티션 키 값으로 추가하여 새 PartitionKeyBuilder 인스턴스를 만듭니다.

      var partitionKey = new PartitionKeyBuilder()
          .Add(country)
          .Add(state)
          .Build();
      
    2. 컨테이너의 ReadItemAsync<> 메서드를 사용하여 idpartitionKey 변수로 컨테이너에서 항목을 읽습니다. 결과를 response 변수에 저장합니다.

      var response = await container.ReadItemAsync<dynamic>(
          id: id, 
          partitionKey: partitionKey
      );
      
    3. response 변수 StatusCodeRequestCharge 속성 값을 콘솔에 씁니다. id 변수의 값도 씁니다.

      Console.WriteLine($"[{response.StatusCode}]\t{id}\t{response.RequestCharge} RU");
      
  9. CosmosHandler.cs 파일을 다시 저장합니다.

  10. 터미널로 돌아가서 애플리케이션을 한 번 더 실행하여 단일 항목을 가리킵니다.

    dotnet run -- --name 'Mica Pereira' --state 'Washington' --country 'United States'
    
  11. 명령의 출력은 쿼리에 단일 RU가 필요함을 나타낼 것입니다.

    [OK]    mica-pereira    1 RUs
    

SDK를 사용하여 트랜잭션 만들기

마지막으로, 만든 항목을 가져와서 해당 항목을 읽고 .NET용 Azure SDK를 사용하여 단일 트랜잭션의 일부로 다른 관련 항목을 만듭니다.

  1. CosmosHandler.cs 파일로 돌아갑니다.

  2. ManageCustomerAsync 메서드에서 코드 줄을 삭제합니다.

    var response = await container.ReadItemAsync<dynamic>(
        id: id, 
        partitionKey: partitionKey
    );
    
    Console.WriteLine($"[{response.StatusCode}]\t{id}\t{response.RequestCharge} RUs");
    
  3. 다음 단계에서는 ManageCustomerAsync 메서드 내에 이 코드를 추가합니다.

    1. name, state, country 메서드 매개 변수 및 id 변수를 사용하여 익명 형식의 새 항목을 만듭니다. 항목을 customerCart 변수로 저장합니다. 이 항목은 현재 비어 있는 고객의 실시간 쇼핑 카트를 나타냅니다.

      var customerCart = new {
          id = $"{Guid.NewGuid()}",
          customerId = id,
          items = new string[] {},
          address = new {
              state = state,
              country = country
          }
      };
      
    2. name, state, country 메서드 매개 변수 및 id 변수를 사용하여 익명 형식의 새 항목을 하나 더 만듭니다. 항목을 customerCart 변수로 저장합니다. 이 항목은 고객의 배송 및 연락처 정보를 나타냅니다.

      var customerContactInfo = new {
          id = $"{id}-contact",
          customerId = id,
          email = email,
          location = $"{state}, {country}",
          address = new {
              state = state,
              country = country
          }
      };
      
    3. partitionKey 변수를 전달하는 컨테이너의 CreateTransactionalBatch 메서드를 사용하여 새 일괄 처리를 만듭니다. batch 변수에 일괄 처리를 저장합니다. 흐름 메서드를 사용하여 다음 작업을 수행합니다.

      메서드 매개 변수
      ReadItem id 문자열 변수
      CreateItem customerCart 익명 형식 변수
      CreateItem customerContactInfo 익명 형식 변수
      var batch = container.CreateTransactionalBatch(partitionKey)
          .ReadItem(id)
          .CreateItem(customerCart)
          .CreateItem(customerContactInfo);
      
    4. 일괄 처리의 ExecuteAsync 메서드를 사용하여 트랜잭션을 시작합니다. 결과를 response 변수에 저장합니다.

      using var response = await batch.ExecuteAsync();
      
    5. response 변수 StatusCodeRequestCharge 속성 값을 콘솔에 씁니다. id 변수의 값도 씁니다.

      Console.WriteLine($"[{response.StatusCode}]\t{response.RequestCharge} RUs");
      
  4. CosmosHandler.cs 파일을 다시 저장합니다.

  5. 터미널로 돌아가서 애플리케이션을 한 번 더 실행하여 단일 항목을 가리킵니다.

    dotnet run -- --name 'Mica Pereira' --state 'Washington' --country 'United States'
    
  6. 명령의 출력에는 전체 트랜잭션에 사용되는 요청 단위가 표시됩니다.

    [OK]    16.05 RUs
    

    참고 항목

    요청 요금은 다를 수 있습니다.

Data Explorer 최종 데이터의 유효성을 검사합니다.

작업을 마무리하려면 Azure Portal의 Data Explorer를 사용하여 이 자습서에서 만든 데이터와 컨테이너를 봅니다.

  1. Azure Portal 기존 API for NoSQL 계정으로 이동합니다.

  2. 리소스 메뉴에서 데이터 탐색기를 선택합니다.

    Screenshot of the Data Explorer option highlighted in the resource menu.

  3. Data Explorer 페이지에서 cosmicworks 데이터베이스를 펼치고 customers 컨테이너를 선택합니다.

    Screenshot of the selected container node within the database node.

  4. 명령 모음에서 새 SQL 쿼리를 선택합니다.

    Screenshot of the New SQL Query option in the Data Explorer command bar.

  5. 쿼리 편집기에서 이 SQL 쿼리 문자열을 관찰합니다.

    SELECT * FROM c
    
  6. 쿼리 실행을 선택하여 쿼리를 실행하고 결과를 관찰합니다.

    Screenshot of the Execute Query option in the Data Explorer command bar.

  7. 결과에는 이 자습서에서 만든 세 개의 항목이 있는 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 데이터베이스를 선택한 다음, 삭제를 선택합니다.

다음 단계

이제 Azure Cosmos DB를 사용하여 첫 번째 .NET 콘솔 애플리케이션을 만들었으므로 Azure Cosmos DB 데이터를 사용하도록 기존 웹 애플리케이션을 업데이트하는 다음 자습서를 시도해 보세요.