

기계 번역으로 제공되는 번역입니다. 제공된 번역과 원본 영어의 내용이 상충하는 경우에는 영어 버전이 우선합니다.

# .NET용 Amazon QLDB 드라이버
<a name="getting-started.dotnet"></a>

**중요**  
지원 종료 공지: 기존 고객은 07/31/2025에 지원이 종료될 때까지 Amazon QLDB를 사용할 수 있습니다. 자세한 내용은 [Amazon QLDB 원장을 Amazon Aurora PostgreSQL로 마이그레이션](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/)을 참조하세요.

원장의 데이터를 사용하려면 AWS 제공된 드라이버를 사용하여 Microsoft .NET 애플리케이션에서 Amazon QLDB에 연결할 수 있습니다. 드라이버는 **.NET Standard 2.0**을 대상으로 합니다. 구체적으로, **.NET Core (LTS) 2.1\$1** 및 **.NET Framework 4.5.2\$1**를 지원합니다. 호환성에 대한 자세한 내용은 *Microsoft Docs* 사이트의 [.NET Standard](https://docs.microsoft.com/en-us/dotnet/standard/net-standard)을 참조하세요.

Amazon Ion 유형과 네이티브 C\$1 유형 간에 수동으로 변환할 필요가 없도록 하려면 *Ion 객체 매퍼*를 사용하는 것이 좋습니다.

다음 주제에서는 .NET용 QLDB 드라이버를 시작하는 방법에 대해 설명합니다.

**Topics**
+ [드라이버 리소스](#getting-started.dotnet.resources)
+ [사전 조건](#getting-started.dotnet.prereqs)
+ [설치](#getting-started.dotnet.install)
+ [빠른 시작 자습서](driver-quickstart-dotnet.md)
+ [Cookbook 참조](driver-cookbook-dotnet.md)

## 드라이버 리소스
<a name="getting-started.dotnet.resources"></a>

.NET 드라이버에서 지원하는 기능에 대한 자세한 정보는 다음 리소스를 참조하세요.
+ [API 참조](https://amazon-qldb-docs.s3.amazonaws.com/drivers/dotnet/1.4.1/api/Amazon.QLDB.Driver.html)
+ [드라이버 소스 코드(GitHub)](https://github.com/awslabs/amazon-qldb-driver-dotnet)
+ [샘플 애플리케이션 소스 코드(GitHub)](https://github.com/aws-samples/amazon-qldb-dmv-sample-dotnet)
+ [Amazon Ion Cookbook](http://amzn.github.io/ion-docs/guides/cookbook.html)
+ [Ion 객체 매퍼(GitHub)](https://github.com/amzn/ion-object-mapper-dotnet)

## 사전 조건
<a name="getting-started.dotnet.prereqs"></a>

.NET용 QLDB 드라이버를 시작하기 전에 다음을 수행해야 합니다.

1. 의 AWS 설정 지침을 따릅니다[Amazon QLDB 액세스](accessing.md). 다음 내용이 포함됩니다:

   1. 가입합니다 AWS.

   1. 적절한 QLDB 권한을 가진 사용자를 생성합니다.

   1. 개발을 위한 프로그래밍 방식 액세스 권한을 부여합니다.

1. [Microsoft .NET 다운로드](https://dotnet.microsoft.com/download) 사이트에서 .NET Core SDK 버전 2.1 이상을 다운로드하여 설치합니다.

1. (선택 사항) Visual Studio, Mac용 Visual Studio 또는 Visual Studio Code와 같은 통합 개발 환경(IDE)을 원하는 대로 설치합니다. 이러한 프로그램은 [Microsoft Visual Studio](https://visualstudio.microsoft.com/) 사이트에서 다운로드할 수 있습니다.

1. [AWS SDK for .NET](https://aws.amazon.com/sdk-for-net)를 위한 개발 환경 구성:

   1.  AWS 자격 증명을 설정합니다. 공유 보안 인증 파일을 생성할 것을 권장합니다.

      지침은 *AWS SDK for .NET 개발자 안내서*의 [보안 인증 파일을 사용하여 AWS 보안 인증 구성](https://docs.aws.amazon.com/sdk-for-net/latest/developer-guide/net-dg-config-creds.html#creds-file)을 참조하세요.

   1. 기본 AWS 리전을 설정하세요. 방법을 알아보려면 [AWS 리전 선택](https://docs.aws.amazon.com/sdk-for-net/latest/developer-guide/net-dg-region-selection.html)을 참조하세요.

      사용 가능한 리전의 전체 목록은 *AWS 일반 참조*에서 [Amazon QLDB 엔드포인트 및 할당량](https://docs.aws.amazon.com/general/latest/gr/qldb.html)을 참조하세요.

그런 다음 기본 샘플 애플리케이션을 설정하고 단축 코드 예제를 실행하거나 기존 .NET 프로젝트에 드라이버를 설치할 수 있습니다.
+ 기존 프로젝트에 QLDB 드라이버와 AWS SDK for .NET 를 설치하려면 로 이동합니다[설치](#getting-started.dotnet.install).
+ 프로젝트를 설정하고 원장에 대한 기본 데이터 트랜잭션을 보여주는 단축 코드 예제를 실행하려면 [빠른 시작 자습서](driver-quickstart-dotnet.md)를 참조하세요.

## 설치
<a name="getting-started.dotnet.install"></a>

NuGet 패키지 관리자를 사용하여 .NET용 QLDB 드라이버를 설치합니다. 원하는 대로 Visual Studio 또는 IDE를 사용하여 프로젝트 종속 항목을 추가하는 것이 좋습니다. 드라이버 패키지 이름은 [Amazon.QLDB.Driver](https://www.nuget.org/packages/amazon.qldb.driver)입니다.

예를 들어 Visual Studio의 경우, **도구** 메뉴에서 **NuGet 패키지 관리자 콘솔**을 엽니다. 그런 다음 `PM>` 프롬프트에 다음 명령을 입력합니다.

```
PM> Install-Package Amazon.QLDB.Driver
```

드라이버를 설치하면 AWS SDK for .NET 및 [Amazon Ion](ion.md) 패키지를 비롯한 종속성도 설치됩니다.

### Ion 객체 매퍼 설치
<a name="getting-started.dotnet.install.mapper"></a>

.NET용 QLDB 드라이버 버전 1.3.0에는 Amazon Ion을 사용할 필요 없이 네이티브 C\$1 데이터 유형을 수락하고 반환할 수 있는 지원이 도입되었습니다. 이 기능을 사용하려면 프로젝트에 다음 패키지를 추가하세요.
+ [Amazon.QLDB.Driver.Serialization](https://www.nuget.org/packages/Amazon.QLDB.Driver.Serialization/) - Ion 값을 C\$1 *POCO(Plain Old CLR Object)*에 매핑하거나 그 반대로도 매핑할 수 있는 라이브러리입니다. 이 Ion 객체 매퍼를 사용하면 애플리케이션이 Ion을 사용할 필요 없이 네이티브 C\$1 데이터 유형과 직접 상호 작용할 수 있습니다. 이 라이브러리를 사용하는 방법에 대한 간단한 가이드는 GitHub 리포지토리 `awslabs/amazon-qldb-driver-dotnet`의 [SERIALIZATION.md](https://github.com/awslabs/amazon-qldb-driver-dotnet/blob/master/SERIALIZATION.md) 파일을 참조하세요.

이 패키지를 설치하려면 다음 명령을 입력합니다.

```
PM> Install-Package Amazon.QLDB.Driver.Serialization
```

원장에서 기본 데이터 트랜잭션을 실행하는 방법에 대한 단축 코드 예제는 [Cookbook 참조](driver-cookbook-dotnet.md)를 참조하세요.

# .NET용 Amazon QLDB 드라이버 - 빠른 시작 자습서
<a name="driver-quickstart-dotnet"></a>

**중요**  
지원 종료 알림: 기존 고객은 07/31/2025에 지원이 종료될 때까지 Amazon QLDB를 사용할 수 있습니다. 자세한 내용은 [Amazon QLDB 원장을 Amazon Aurora PostgreSQL로 마이그레이션](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/)을 참조하세요.

이 자습서에서는 .NET용 Amazon QLDB 드라이버를 사용하여 간단한 애플리케이션을 설정하는 방법을 알아봅니다. 이 안내서에는 드라이버 설치 단계 및 기본적인 CRUD(*생성, 읽기, 업데이트 및 삭제*) 작업에 대한 단축 코드 예제가 포함되어 있습니다.

**Topics**
+ [사전 조건](#driver-quickstart-dotnet.prereqs)
+ [1단계: 프로젝트 설정](#driver-quickstart-dotnet.step-1)
+ [2단계: 드라이버 초기화](#driver-quickstart-dotnet.step-2)
+ [3단계: 테이블 및 인덱스 생성](#driver-quickstart-dotnet.step-3)
+ [4단계: 문서 삽입](#driver-quickstart-dotnet.step-4)
+ [5단계: 문서 쿼리](#driver-quickstart-dotnet.step-5)
+ [6단계: 문서 업데이트](#driver-quickstart-dotnet.step-6)
+ [전체 애플리케이션 실행](#driver-quickstart-dotnet.complete)

## 사전 조건
<a name="driver-quickstart-dotnet.prereqs"></a>

시작하기 전에 다음을 수행해야 합니다.

1. 아직 완료하지 않은 경우 .NET 드라이버에 대해 [사전 조건](getting-started.dotnet.md#getting-started.dotnet.prereqs)를 완료합니다. 여기에는 가입 AWS, 개발을 위한 프로그래밍 방식 액세스 권한 부여, .NET Core SDK 설치가 포함됩니다.

1. `quick-start`라는 명칭의 원장을 생성합니다.

   원장 생성 방법을 알아보려면 *콘솔 시작하기*의 [Amazon QLDB 원장의 기본 작업](ledger-management.basics.md) 또는 [1단계: 새 원장 생성](getting-started-step-1.md) 섹션을 참조하세요.

## 1단계: 프로젝트 설정
<a name="driver-quickstart-dotnet.step-1"></a>

먼저 .NET 프로젝트를 설정합니다.

1. 템플릿 애플리케이션을 생성하고 실행하려면 *bash*, *PowerShell* 또는 *명령 프롬프트*와 같은 터미널에 `dotnet` 명령을 입력합니다.

   ```
   $ dotnet new console --output Amazon.QLDB.QuickStartGuide
   $ dotnet run --project Amazon.QLDB.QuickStartGuide
   ```

   이 템플릿은 `Amazon.QLDB.QuickStartGuide`이라는 폴더를 생성합니다. 해당 폴더에 같은 이름의 프로젝트와 `Program.cs`이라는 이름의 파일이 생성됩니다. 프로그램에는 `Hello World!` 출력을 표시하는 코드가 포함되어 있습니다.

1. NuGet 패키지 관리자를 사용하여 .NET용 QLDB 드라이버를 설치합니다. 프로젝트에 종속성을 추가하려면 Visual Studio 또는 선택한 IDE를 사용하는 것이 좋습니다. 드라이버 패키지 이름은 [Amazon.QLDB.Driver](https://www.nuget.org/packages/amazon.qldb.driver)입니다.
   + 예를 들어 Visual Studio의 경우, **도구** 메뉴에서 **NuGet 패키지 관리자 콘솔**을 엽니다. 그런 다음 `PM>` 프롬프트에 다음 명령을 입력합니다.

     ```
     PM> Install-Package Amazon.QLDB.Driver
     ```
   + 또는 터미널에서 다음 명령을 입력할 수 있습니다.

     ```
     $ cd Amazon.QLDB.QuickStartGuide
     $ dotnet add package Amazon.QLDB.Driver
     ```

   드라이버를 설치하면 [AWS SDK for .NET](https://aws.amazon.com/sdk-for-net)및 [Amazon Ion](ion.md) 라이브러리를 비롯한 해당 종속 항목도 설치됩니다.

1. 드라이버의 직렬화 라이브러리를 설치합니다.

   ```
   PM> Install-Package Amazon.QLDB.Driver.Serialization
   ```

1. `Program.cs` 파일을 엽니다.

   그런 다음, 다음 단계의 코드 예를 점진적으로 추가하여 몇 가지 기본 CRUD 작업을 시도해 보세요. 또는 단계별 자습서를 건너뛰고 [전체 애플리케이션](#driver-quickstart-dotnet.complete)을 실행할 수도 있습니다.

**참고**  
**동기식 API와 비동기식 API 선택** - 드라이버는 동기식 및 비동기식 API를 제공합니다. 여러 요청을 차단하지 않고 처리하는 수요가 많은 애플리케이션의 경우, 성능 향상을 위해 비동기식 API를 사용하는 것이 좋습니다. 드라이버는 동기식으로 작성된 기존 코드 베이스의 편의성을 높이기 위해 동기식 API를 제공합니다.  
이 자습서에는 동기식 및 비동기식 코드 예제가 모두 포함되어 있습니다. API에 대한 자세한 내용은 API 설명서의 [IQldbDriver](https://amazon-qldb-docs.s3.amazonaws.com/drivers/dotnet/1.4.1/api/Amazon.QLDB.Driver.IQldbDriver.html) 및 [IAsyncQldbDriver](https://amazon-qldb-docs.s3.amazonaws.com/drivers/dotnet/1.4.1/api/Amazon.QLDB.Driver.IAsyncQldbDriver.html) 인터페이스를 참조하세요.
**Amazon Ion 데이터 처리** - 이 자습서에서는 기본적으로 [Ion 객체 매퍼](https://github.com/amzn/ion-object-mapper-dotnet)를 사용하여 Amazon Ion 데이터를 처리하는 코드 예제를 제공합니다. QLDB는 .NET 드라이버 버전 1.3.0에서 Ion 객체 매퍼를 도입했습니다. 해당하는 경우, 이 자습서에서는 표준 [Ion 라이브러리](https://github.com/amzn/ion-dotnet)를 대안으로 사용하는 코드 예제도 제공합니다. 자세한 내용은 [Amazon Ion 작업](driver-cookbook-dotnet.md#cookbook-dotnet.ion) 섹션을 참조하세요.

## 2단계: 드라이버 초기화
<a name="driver-quickstart-dotnet.step-2"></a>

`quick-start`라는 명칭의 원장에 연결되는 드라이버의 인스턴스를 초기화합니다. 다음 코드를 `Program.cs` 파일에 추가합니다.

------
#### [ Async ]

```
using Amazon.QLDB.Driver;
using Amazon.QLDB.Driver.Generic;
using Amazon.QLDB.Driver.Serialization;

namespace Amazon.QLDB.QuickStartGuide
{
    class Program
    {
        public class Person
        {
            public string FirstName { get; set; }

            public string LastName { get; set; }

            public int Age { get; set; }

            public override string ToString()
            {
                return FirstName + ", " + LastName + ", " + Age.ToString();
            }
        }

        static async Task Main(string[] args)
        {
            Console.WriteLine("Create the async QLDB driver");
            IAsyncQldbDriver driver = AsyncQldbDriver.Builder()
                .WithLedger("quick-start")
                .WithSerializer(new ObjectSerializer())
                .Build();
        }
    }
}
```

------
#### [ Sync ]

```
using Amazon.QLDB.Driver;
using Amazon.QLDB.Driver.Generic;
using Amazon.QLDB.Driver.Serialization;

namespace Amazon.QLDB.QuickStartGuide
{
    class Program
    {
        public class Person
        {
            public string FirstName { get; set; }

            public string LastName { get; set; }

            public int Age { get; set; }

            public override string ToString()
            {
                return FirstName + ", " + LastName + ", " + Age.ToString();
            }
        }

        static void Main(string[] args)
        {
            Console.WriteLine("Create the sync QLDB driver");
            IQldbDriver driver = QldbDriver.Builder()
                .WithLedger("quick-start")
                .WithSerializer(new ObjectSerializer())
                .Build();
        }
    }
}
```

------

### Ion 라이브러리 사용
<a name="driver-quickstart-dotnet.step-2.ion-library"></a>

------
#### [ Async ]

```
using System;
using System.Threading.Tasks;
using Amazon.IonDotnet.Tree;
using Amazon.IonDotnet.Tree.Impl;
using Amazon.QLDB.Driver;
using IAsyncResult = Amazon.QLDB.Driver.IAsyncResult;

namespace Amazon.QLDB.QuickStartGuide
{
    class Program
    {
        static IValueFactory valueFactory = new ValueFactory();

        static async Task Main(string[] args)
        {
            Console.WriteLine("Create the async QLDB driver");
            IAsyncQldbDriver driver = AsyncQldbDriver.Builder()
                .WithLedger("quick-start")
                .Build();
        }
    }
}
```

------
#### [ Sync ]

```
using System;
using Amazon.IonDotnet.Tree;
using Amazon.IonDotnet.Tree.Impl;
using Amazon.QLDB.Driver;

namespace Amazon.QLDB.QuickStartGuide
{
    class Program
    {
        static IValueFactory valueFactory = new ValueFactory();

        static void Main(string[] args)
        {
            Console.WriteLine("Create the sync QLDB Driver");
            IQldbDriver driver = QldbDriver.Builder()
                .WithLedger("quick-start")
                .Build();
        }
    }
}
```

------

## 3단계: 테이블 및 인덱스 생성
<a name="driver-quickstart-dotnet.step-3"></a>

이 자습서의 나머지 부분부터 *6단계*까지는 이전 코드 예제에 다음 코드 예제를 추가해야 합니다.

이 단계에서 다음 코드는 `CREATE TABLE` 및 `CREATE INDEX` 문을 실행하는 방법을 보여줍니다. `Person`라는 이름의 테이블과 해당 테이블의 `firstName` 필드에 대한 인덱스를 생성합니다. [인덱스](ql-reference.create-index.md)는 쿼리 성능을 최적화하고 [OCC(낙관적 동시성 제어)](concurrency.md) 충돌 예외를 제한하는 데 필요합니다.

------
#### [ Async ]

```
Console.WriteLine("Creating the table and index");

// Creates the table and the index in the same transaction.
// Note: Any code within the lambda can potentially execute multiple times due to retries.
// For more information, see: https://docs.aws.amazon.com/qldb/latest/developerguide/driver-retry-policy
await driver.Execute(async txn =>
{
    await txn.Execute("CREATE TABLE Person");
    await txn.Execute("CREATE INDEX ON Person(firstName)");
});
```

------
#### [ Sync ]

```
Console.WriteLine("Creating the tables and index");

// Creates the table and the index in the same transaction.
// Note: Any code within the lambda can potentially execute multiple times due to retries.
// For more information, see: https://docs.aws.amazon.com/qldb/latest/developerguide/driver-retry-policy
driver.Execute(txn =>
{
    txn.Execute("CREATE TABLE Person");
    txn.Execute("CREATE INDEX ON Person(firstName)");
});
```

------

## 4단계: 문서 삽입
<a name="driver-quickstart-dotnet.step-4"></a>

다음 코드 예에서는 `INSERT` 문을 실행하는 방법을 보여줍니다. QLDB는 [PartiQL](ql-reference.md) 쿼리 언어(SQL 호환) 및 [Amazon Ion](ion.md) 데이터 형식(JSON의 상위 집합)을 지원합니다.

`Person` 테이블에 문서를 삽입하는 다음 코드를 추가합니다.

------
#### [ Async ]

```
Console.WriteLine("Inserting a document");

Person myPerson = new Person {
    FirstName = "John",
    LastName = "Doe",
    Age = 32
};

await driver.Execute(async txn =>
{
    IQuery<Person> myQuery = txn.Query<Person>("INSERT INTO Person ?", myPerson);
    await txn.Execute(myQuery);
});
```

------
#### [ Sync ]

```
Console.WriteLine("Inserting a document");

Person myPerson = new Person {
    FirstName = "John",
    LastName = "Doe",
    Age = 32
};

driver.Execute(txn =>
{
    IQuery<Person> myQuery = txn.Query<Person>("INSERT INTO Person ?", myPerson);
    txn.Execute(myQuery);
});
```

------

### Ion 라이브러리 사용
<a name="driver-quickstart-dotnet.step-4.ion-library"></a>

------
#### [ Async ]

```
Console.WriteLine("Inserting a document");

// This is one way of creating Ion values. We can also use an IonLoader.
// For more details, see: https://docs.aws.amazon.com/qldb/latest/developerguide/driver-cookbook-dotnet.html#cookbook-dotnet.ion
IIonValue ionPerson = valueFactory.NewEmptyStruct();
ionPerson.SetField("firstName", valueFactory.NewString("John"));
ionPerson.SetField("lastName", valueFactory.NewString("Doe"));
ionPerson.SetField("age", valueFactory.NewInt(32));

await driver.Execute(async txn =>
{
    await txn.Execute("INSERT INTO Person ?", ionPerson);
});
```

------
#### [ Sync ]

```
Console.WriteLine("Inserting a document");

// This is one way of creating Ion values, we can also use an IonLoader.
// For more details, see: https://docs.aws.amazon.com/qldb/latest/developerguide/driver-cookbook-dotnet.html#cookbook-dotnet.ion
IIonValue ionPerson = valueFactory.NewEmptyStruct();
ionPerson.SetField("firstName", valueFactory.NewString("John"));
ionPerson.SetField("lastName", valueFactory.NewString("Doe"));
ionPerson.SetField("age", valueFactory.NewInt(32));

driver.Execute(txn =>
{
    txn.Execute("INSERT INTO Person ?", ionPerson);
});
```

------

**작은 정보**  
단일 [INSERT](ql-reference.insert.md) 문을 사용하여 여러 문서를 삽입하려면 다음과 같이 [Ion 목록](driver-working-with-ion.md#driver-ion-list) 타입 파라미터를 해당 에 전달할 수 있습니다.  

```
// people is an Ion list
txn.Execute("INSERT INTO Person ?", people);
```
Ion 목록을 전달할 때는 변수 자리 표시자(`?`)를 이중 꺾쇠 괄호(`<<...>>`)로 묶지 마세요. 수동 PartiQL 문에서 이중 꺾쇠 괄호는 *백*으로 알려진 정렬되지 않은 모음을 의미합니다.

## 5단계: 문서 쿼리
<a name="driver-quickstart-dotnet.step-5"></a>

다음 코드 예에서는 `SELECT` 문을 실행하는 방법을 보여줍니다.

`Person` 테이블에서 문서를 쿼리하는 다음 코드를 추가합니다.

------
#### [ Async ]

```
Console.WriteLine("Querying the table");

// The result from driver.Execute() is buffered into memory because once the
// transaction is committed, streaming the result is no longer possible.
IAsyncResult<Person> selectResult = await driver.Execute(async txn =>
{
    IQuery<Person> myQuery = txn.Query<Person>("SELECT * FROM Person WHERE FirstName = ?", "John");
    return await txn.Execute(myQuery);
});

await foreach (Person person in selectResult)
{
    Console.WriteLine(person);
    // John, Doe, 32
}
```

------
#### [ Sync ]

```
Console.WriteLine("Querying the table");

// The result from driver.Execute() is buffered into memory because once the
// transaction is committed, streaming the result is no longer possible.
IResult<Person> selectResult = driver.Execute(txn =>
{
    IQuery<Person> myQuery = txn.Query<Person>("SELECT * FROM Person WHERE FirstName = ?", "John");
    return txn.Execute(myQuery);
});

foreach (Person person in selectResult)
{
    Console.WriteLine(person);
    // John, Doe, 32
}
```

------

### Ion 라이브러리 사용
<a name="driver-quickstart-dotnet.step-5.ion-library"></a>

------
#### [ Async ]

```
Console.WriteLine("Querying the table");

IIonValue ionFirstName = valueFactory.NewString("John");

// The result from driver.Execute() is buffered into memory because once the
// transaction is committed, streaming the result is no longer possible.
IAsyncResult selectResult = await driver.Execute(async txn =>
{
    return await txn.Execute("SELECT * FROM Person WHERE firstName = ?", ionFirstName);
});

await foreach (IIonValue row in selectResult)
{
    Console.WriteLine(row.GetField("firstName").StringValue);
    Console.WriteLine(row.GetField("lastName").StringValue);
    Console.WriteLine(row.GetField("age").IntValue);
}
```

------
#### [ Sync ]

```
Console.WriteLine("Querying the table");

IIonValue ionFirstName = valueFactory.NewString("John");

// The result from driver.Execute() is buffered into memory because once the
// transaction is committed, streaming the result is no longer possible.
IResult selectResult = driver.Execute(txn =>
{
    return txn.Execute("SELECT * FROM Person WHERE firstName = ?", ionFirstName);
});

foreach (IIonValue row in selectResult)
{
    Console.WriteLine(row.GetField("firstName").StringValue);
    Console.WriteLine(row.GetField("lastName").StringValue);
    Console.WriteLine(row.GetField("age").IntValue);
}
```

------

이 예에서는 물음표(`?`)를 변수 자리 표시자로 사용하여 문서 정보를 해당 문에 전달합니다. 자리 표시자를 사용할 때는 `IonValue` 타입의 값을 전달해야 합니다.

## 6단계: 문서 업데이트
<a name="driver-quickstart-dotnet.step-6"></a>

다음 코드 예에서는 `UPDATE` 문을 실행하는 방법을 보여줍니다.

1. `age`를 42로 업데이트하여 `Person` 테이블의 문서를 업데이트하는 다음 코드를 추가합니다.

------
#### [ Async ]

   ```
   Console.WriteLine("Updating the document");
   
   await driver.Execute(async txn =>
   {
       IQuery<Person> myQuery = txn.Query<Person>("UPDATE Person SET Age = ? WHERE FirstName = ?", 42, "John");
       await txn.Execute(myQuery);
   });
   ```

------
#### [ Sync ]

   ```
   Console.WriteLine("Updating the document");
   
   driver.Execute(txn =>
   {
       IQuery<Person> myQuery = txn.Query<Person>("UPDATE Person SET Age = ? WHERE FirstName = ?", 42, "John");
       txn.Execute(myQuery);
   });
   ```

------

1. 문서를 다시 쿼리하여 업데이트된 값을 확인합니다.

------
#### [ Async ]

   ```
   Console.WriteLine("Querying the table for the updated document");
   
   IAsyncResult<Person> updateResult = await driver.Execute(async txn =>
   {
       IQuery<Person> myQuery = txn.Query<Person>("SELECT * FROM Person WHERE FirstName = ?", "John");
       return await txn.Execute(myQuery);
   });
   
   await foreach (Person person in updateResult)
   {
       Console.WriteLine(person);
       // John, Doe, 42
   }
   ```

------
#### [ Sync ]

   ```
   Console.WriteLine("Querying the table for the updated document");
   
   IResult<Person> updateResult = driver.Execute(txn =>
   {
       IQuery<Person> myQuery = txn.Query<Person>("SELECT * FROM Person WHERE FirstName = ?", "John");
       return txn.Execute(myQuery);
   });
   
   foreach (Person person in updateResult)
   {
       Console.WriteLine(person);
       // John, Doe, 42
   }
   ```

------

1. 애플리케이션을 실행하려면 `Amazon.QLDB.QuickStartGuide` 프로젝트 디렉터리의 상위 디렉터리에서 다음 명령을 입력합니다.

   ```
   $ dotnet run --project Amazon.QLDB.QuickStartGuide
   ```

### Ion 라이브러리 사용
<a name="driver-quickstart-dotnet.step-6.ion-library"></a>

1. `age`를 42로 업데이트하여 `Person` 테이블의 문서를 업데이트하는 다음 코드를 추가합니다.

------
#### [ Async ]

   ```
   Console.WriteLine("Updating the document");
   
   IIonValue ionIntAge = valueFactory.NewInt(42);
   IIonValue ionFirstName2 = valueFactory.NewString("John");
   
   await driver.Execute(async txn =>
   {
       await txn.Execute("UPDATE Person SET age = ? WHERE firstName = ?", ionIntAge, ionFirstName2);
   });
   ```

------
#### [ Sync ]

   ```
   Console.WriteLine("Updating a document");
   
   IIonValue ionIntAge = valueFactory.NewInt(42);
   IIonValue ionFirstName2 = valueFactory.NewString("John");
   
   driver.Execute(txn =>
   {
       txn.Execute("UPDATE Person SET age = ? WHERE firstName = ?", ionIntAge, ionFirstName2);
   });
   ```

------

1. 문서를 다시 쿼리하여 업데이트된 값을 확인합니다.

------
#### [ Async ]

   ```
   Console.WriteLine("Querying the table for the updated document");
   
   IIonValue ionFirstName3 = valueFactory.NewString("John");
   
   IAsyncResult updateResult = await driver.Execute(async txn =>
   {
       return await txn.Execute("SELECT * FROM Person WHERE firstName = ?", ionFirstName3 );
   });
   
   await foreach (IIonValue row in updateResult)
   {
       Console.WriteLine(row.GetField("firstName").StringValue);
       Console.WriteLine(row.GetField("lastName").StringValue);
       Console.WriteLine(row.GetField("age").IntValue);
   }
   ```

------
#### [ Sync ]

   ```
   Console.WriteLine("Querying the table for the updated document");
   
   IIonValue ionFirstName3 = valueFactory.NewString("John");
   
   IResult updateResult = driver.Execute(txn =>
   {
       return txn.Execute("SELECT * FROM Person WHERE firstName = ?", ionFirstName3);
   });
   
   foreach (IIonValue row in updateResult)
   {
       Console.WriteLine(row.GetField("firstName").StringValue);
       Console.WriteLine(row.GetField("lastName").StringValue);
       Console.WriteLine(row.GetField("age").IntValue);
   }
   ```

------

1. 애플리케이션을 실행하려면 `Amazon.QLDB.QuickStartGuide` 프로젝트 디렉터리의 상위 디렉터리에서 다음 명령을 입력합니다.

   ```
   $ dotnet run --project Amazon.QLDB.QuickStartGuide
   ```

## 전체 애플리케이션 실행
<a name="driver-quickstart-dotnet.complete"></a>

다음 코드 예는 `Program.cs` 애플리케이션의 전체 버전입니다. 이전 단계를 개별적으로 수행하는 대신 이 코드 예를 처음부터 끝까지 복사하여 실행할 수도 있습니다. 이 애플리케이션은 `quick-start`이라는 명칭의 원장에 대한 몇 가지 기본 CRUD 작업을 보여줍니다.

**참고**  
이 코드를 실행하기 전에 `quick-start` 원장에 `Person`이라는 명칭의 활성 테이블이 아직 없는지 확인하세요.

------
#### [ Async ]

```
using Amazon.QLDB.Driver;
using Amazon.QLDB.Driver.Generic;
using Amazon.QLDB.Driver.Serialization;

namespace Amazon.QLDB.QuickStartGuide
{
    class Program
    {
        public class Person
        {
            public string FirstName { get; set; }

            public string LastName { get; set; }

            public int Age { get; set; }

            public override string ToString()
            {
                return FirstName + ", " + LastName + ", " + Age.ToString();
            }
        }

        static async Task Main(string[] args)
        {
            Console.WriteLine("Create the async QLDB driver");
            IAsyncQldbDriver driver = AsyncQldbDriver.Builder()
                .WithLedger("quick-start")
                .WithSerializer(new ObjectSerializer())
                .Build();

            Console.WriteLine("Creating the table and index");

            // Creates the table and the index in the same transaction.
            // Note: Any code within the lambda can potentially execute multiple times due to retries.
            // For more information, see: https://docs.aws.amazon.com/qldb/latest/developerguide/driver-retry-policy
            await driver.Execute(async txn =>
            {
                await txn.Execute("CREATE TABLE Person");
                await txn.Execute("CREATE INDEX ON Person(firstName)");
            });

            Console.WriteLine("Inserting a document");

            Person myPerson = new Person {
                FirstName = "John",
                LastName = "Doe",
                Age = 32
            };

            await driver.Execute(async txn =>
            {
                IQuery<Person> myQuery = txn.Query<Person>("INSERT INTO Person ?", myPerson);
                await txn.Execute(myQuery);
            });

            Console.WriteLine("Querying the table");

            // The result from driver.Execute() is buffered into memory because once the
            // transaction is committed, streaming the result is no longer possible.
            IAsyncResult<Person> selectResult = await driver.Execute(async txn =>
            {
                IQuery<Person> myQuery = txn.Query<Person>("SELECT * FROM Person WHERE FirstName = ?", "John");
                return await txn.Execute(myQuery);
            });

            await foreach (Person person in selectResult)
            {
                Console.WriteLine(person);
                // John, Doe, 32
            }

            Console.WriteLine("Updating the document");

            await driver.Execute(async txn =>
            {
                IQuery<Person> myQuery = txn.Query<Person>("UPDATE Person SET Age = ? WHERE FirstName = ?", 42, "John");
                await txn.Execute(myQuery);
            });

            Console.WriteLine("Querying the table for the updated document");

            IAsyncResult<Person> updateResult = await driver.Execute(async txn =>
            {
                IQuery<Person> myQuery = txn.Query<Person>("SELECT * FROM Person WHERE FirstName = ?", "John");
                return await txn.Execute(myQuery);
            });

            await foreach (Person person in updateResult)
            {
                Console.WriteLine(person);
                // John, Doe, 42
            }
        }
    }
}
```

------
#### [ Sync ]

```
using Amazon.QLDB.Driver;
using Amazon.QLDB.Driver.Generic;
using Amazon.QLDB.Driver.Serialization;

namespace Amazon.QLDB.QuickStartGuide
{
    class Program
    {
        public class Person
        {
            public string FirstName { get; set; }

            public string LastName { get; set; }

            public int Age { get; set; }

            public override string ToString()
            {
                return FirstName + ", " + LastName + ", " + Age.ToString();
            }
        }

        static void Main(string[] args)
        {
            Console.WriteLine("Create the sync QLDB driver");
            IQldbDriver driver = QldbDriver.Builder()
                .WithLedger("quick-start")
                .WithSerializer(new ObjectSerializer())
                .Build();

            Console.WriteLine("Creating the table and index");

            // Creates the table and the index in the same transaction.
            // Note: Any code within the lambda can potentially execute multiple times due to retries.
            // For more information, see: https://docs.aws.amazon.com/qldb/latest/developerguide/driver-retry-policy
            driver.Execute(txn =>
            {
                txn.Execute("CREATE TABLE Person");
                txn.Execute("CREATE INDEX ON Person(firstName)");
            });

            Console.WriteLine("Inserting a document");

            Person myPerson = new Person {
                FirstName = "John",
                LastName = "Doe",
                Age = 32
            };

            driver.Execute(txn =>
            {
                IQuery<Person> myQuery = txn.Query<Person>("INSERT INTO Person ?", myPerson);
                txn.Execute(myQuery);
            });

            Console.WriteLine("Querying the table");

            // The result from driver.Execute() is buffered into memory because once the
            // transaction is committed, streaming the result is no longer possible.
            IResult<Person> selectResult = driver.Execute(txn =>
            {
                IQuery<Person> myQuery = txn.Query<Person>("SELECT * FROM Person WHERE FirstName = ?", "John");
                return txn.Execute(myQuery);
            });

            foreach (Person person in selectResult)
            {
                Console.WriteLine(person);
                // John, Doe, 32
            }

            Console.WriteLine("Updating the document");

            driver.Execute(txn =>
            {
                IQuery<Person> myQuery = txn.Query<Person>("UPDATE Person SET Age = ? WHERE FirstName = ?", 42, "John");
                txn.Execute(myQuery);
            });

            Console.WriteLine("Querying the table for the updated document");

            IResult<Person> updateResult = driver.Execute(txn =>
            {
                IQuery<Person> myQuery = txn.Query<Person>("SELECT * FROM Person WHERE FirstName = ?", "John");
                return txn.Execute(myQuery);
            });

            foreach (Person person in updateResult)
            {
                Console.WriteLine(person);
                // John, Doe, 42
            }

        }
    }
}
```

------

### Ion 라이브러리 사용
<a name="driver-quickstart-dotnet.complete.ion-library"></a>

------
#### [ Async ]

```
using System;
using System.Threading.Tasks;
using Amazon.IonDotnet.Tree;
using Amazon.IonDotnet.Tree.Impl;
using Amazon.QLDB.Driver;
using IAsyncResult = Amazon.QLDB.Driver.IAsyncResult;

namespace Amazon.QLDB.QuickStartGuide
{
    class Program
    {
        static IValueFactory valueFactory = new ValueFactory();

        static async Task Main(string[] args)
        {
            Console.WriteLine("Create the async QLDB driver");
            IAsyncQldbDriver driver = AsyncQldbDriver.Builder()
                .WithLedger("quick-start")
                .Build();

            Console.WriteLine("Creating the table and index");

            // Creates the table and the index in the same transaction.
            // Note: Any code within the lambda can potentially execute multiple times due to retries.
            // For more information, see: https://docs.aws.amazon.com/qldb/latest/developerguide/driver-retry-policy
            await driver.Execute(async txn =>
            {
                await txn.Execute("CREATE TABLE Person");
                await txn.Execute("CREATE INDEX ON Person(firstName)");
            });

            Console.WriteLine("Inserting a document");

            // This is one way of creating Ion values. We can also use an IonLoader.
            // For more details, see: https://docs.aws.amazon.com/qldb/latest/developerguide/driver-cookbook-dotnet.html#cookbook-dotnet.ion
            IIonValue ionPerson = valueFactory.NewEmptyStruct();
            ionPerson.SetField("firstName", valueFactory.NewString("John"));
            ionPerson.SetField("lastName", valueFactory.NewString("Doe"));
            ionPerson.SetField("age", valueFactory.NewInt(32));

            await driver.Execute(async txn =>
            {
                await txn.Execute("INSERT INTO Person ?", ionPerson);
            });

            Console.WriteLine("Querying the table");

            IIonValue ionFirstName = valueFactory.NewString("John");

            // The result from driver.Execute() is buffered into memory because once the
            // transaction is committed, streaming the result is no longer possible.
            IAsyncResult selectResult = await driver.Execute(async txn =>
            {
                return await txn.Execute("SELECT * FROM Person WHERE firstName = ?", ionFirstName);
            });

            await foreach (IIonValue row in selectResult)
            {
                Console.WriteLine(row.GetField("firstName").StringValue);
                Console.WriteLine(row.GetField("lastName").StringValue);
                Console.WriteLine(row.GetField("age").IntValue);
            }

            Console.WriteLine("Updating the document");

            IIonValue ionIntAge = valueFactory.NewInt(42);
            IIonValue ionFirstName2 = valueFactory.NewString("John");

            await driver.Execute(async txn =>
            {
                await txn.Execute("UPDATE Person SET age = ? WHERE firstName = ?", ionIntAge, ionFirstName2);
            });

            Console.WriteLine("Querying the table for the updated document");

            IIonValue ionFirstName3 = valueFactory.NewString("John");

            IAsyncResult updateResult = await driver.Execute(async txn =>
            {
                return await txn.Execute("SELECT * FROM Person WHERE firstName = ?", ionFirstName3);
            });

            await foreach (IIonValue row in updateResult)
            {
                Console.WriteLine(row.GetField("firstName").StringValue);
                Console.WriteLine(row.GetField("lastName").StringValue);
                Console.WriteLine(row.GetField("age").IntValue);
            }
        }
    }
}
```

------
#### [ Sync ]

```
using System;
using Amazon.IonDotnet.Tree;
using Amazon.IonDotnet.Tree.Impl;
using Amazon.QLDB.Driver;

namespace Amazon.QLDB.QuickStartGuide
{
    class Program
    {
        static IValueFactory valueFactory = new ValueFactory();

        static void Main(string[] args)
        {
            Console.WriteLine("Create the sync QLDB Driver");
            IQldbDriver driver = QldbDriver.Builder()
                .WithLedger("quick-start")
                .Build();

            Console.WriteLine("Creating the tables and index");

            // Creates the table and the index in the same transaction.
            // Note: Any code within the lambda can potentially execute multiple times due to retries.
            // For more information, see: https://docs.aws.amazon.com/qldb/latest/developerguide/driver-retry-policy
            driver.Execute(txn =>
            {
                txn.Execute("CREATE TABLE Person");
                txn.Execute("CREATE INDEX ON Person(firstName)");
            });

            Console.WriteLine("Inserting a document");

            // This is one way of creating Ion values. We can also use an IonLoader.
            // For more details, see: https://docs.aws.amazon.com/qldb/latest/developerguide/driver-cookbook-dotnet.html#cookbook-dotnet.ion
            IIonValue ionPerson = valueFactory.NewEmptyStruct();
            ionPerson.SetField("firstName", valueFactory.NewString("John"));
            ionPerson.SetField("lastName", valueFactory.NewString("Doe"));
            ionPerson.SetField("age", valueFactory.NewInt(32));

            driver.Execute(txn =>
            {
                txn.Execute("INSERT INTO Person ?", ionPerson);
            });

            Console.WriteLine("Querying the table");

            IIonValue ionFirstName = valueFactory.NewString("John");

            // The result from driver.Execute() is buffered into memory because once the
            // transaction is committed, streaming the result is no longer possible.
            IResult selectResult = driver.Execute(txn =>
            {
                return txn.Execute("SELECT * FROM Person WHERE firstName = ?", ionFirstName);
            });

            foreach (IIonValue row in selectResult)
            {
                Console.WriteLine(row.GetField("firstName").StringValue);
                Console.WriteLine(row.GetField("lastName").StringValue);
                Console.WriteLine(row.GetField("age").IntValue);
            }

            Console.WriteLine("Updating a document");

            IIonValue ionIntAge = valueFactory.NewInt(42);
            IIonValue ionFirstName2 = valueFactory.NewString("John");

            driver.Execute(txn =>
            {
                txn.Execute("UPDATE Person SET age = ? WHERE firstName = ?", ionIntAge, ionFirstName2);
            });

            Console.WriteLine("Querying the table for the updated document");

            IIonValue ionFirstName3 = valueFactory.NewString("John");

            IResult updateResult = driver.Execute(txn =>
            {
                return txn.Execute("SELECT * FROM Person WHERE firstName = ?", ionFirstName3);
            });

            foreach (IIonValue row in updateResult)
            {
                Console.WriteLine(row.GetField("firstName").StringValue);
                Console.WriteLine(row.GetField("lastName").StringValue);
                Console.WriteLine(row.GetField("age").IntValue);
            }
        }
    }
}
```

------

전체 애플리케이션을 실행하려면 `Amazon.QLDB.QuickStartGuide` 프로젝트 디렉터리의 상위 디렉터리에서 다음 명령을 입력합니다.

```
$ dotnet run --project Amazon.QLDB.QuickStartGuide
```

# .NET용 Amazon QLDB 드라이버 - Cookbook 참조
<a name="driver-cookbook-dotnet"></a>

**중요**  
지원 종료 알림: 기존 고객은 07/31/2025에 지원이 종료될 때까지 Amazon QLDB를 사용할 수 있습니다. 자세한 내용은 [Amazon QLDB 원장을 Amazon Aurora PostgreSQL로 마이그레이션](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/)을 참조하세요.

이 참조 가이드는 .NET용 Amazon QLDB 드라이버의 일반적인 사용 사례를 보여줍니다. 이 C\$1 코드 예제는 드라이버를 사용하여 기본 CRUD(*생성, 읽기, 업데이트, 삭제*) 작업을 실행하는 방법을 보여줍니다. 또한 Amazon Ion 데이터를 처리하기 위한 코드 예제도 포함되어 있습니다. 또한 이 가이드에서는 트랜잭션에 멱등성을 부여하고 고유성 제약하는 모범 사례를 중점적으로 설명합니다.

**참고**  
이 주제는 기본적으로 [Ion 객체 매퍼](https://github.com/amzn/ion-object-mapper-dotnet)를 사용하여 Amazon Ion 데이터를 처리하는 코드 예제를 제공합니다. QLDB는 .NET 드라이버 버전 1.3.0에서 Ion 객체 매퍼를 도입했습니다. 해당하는 경우, 이 주제에서는 표준 [Ion 라이브러리](https://github.com/amzn/ion-dotnet)를 대안으로 사용하는 코드 예제도 제공합니다. 자세한 내용은 [Amazon Ion 작업](#cookbook-dotnet.ion) 섹션을 참조하세요.

**Contents**
+ [드라이버 가져오기](#cookbook-dotnet.importing)
+ [드라이버 인스턴스화](#cookbook-dotnet.instantiating)
+ [CRUD 작업](#cookbook-dotnet.crud)
  + [테이블 생성](#cookbook-dotnet.crud.creating-tables)
  + [인덱스 생성](#cookbook-dotnet.crud.creating-indexes)
  + [문서 읽기](#cookbook-dotnet.crud.reading)
    + [쿼리 파라미터 사용](#cookbook-dotnet.reading-using-params)
  + [문서 삽입하기](#cookbook-dotnet.crud.inserting)
    + [하나의 명령문에 여러 문서 삽입](#cookbook-dotnet.crud.inserting.multiple)
  + [문서 업데이트](#cookbook-dotnet.crud.updating)
  + [문서 삭제](#cookbook-dotnet.crud.deleting)
  + [하나의 트랜잭션에서 여러 명령문 실행](#cookbook-dotnet.crud.multi-statement)
  + [재시도 로직](#cookbook-dotnet.crud.retry-logic)
  + [고유성 제약 조건 구현](#cookbook-dotnet.crud.uniqueness-constraints)
+ [Amazon Ion 작업](#cookbook-dotnet.ion)
  + [Ion 모듈 가져오기](#cookbook-dotnet.ion.import)
  + [Ion 유형 생성](#cookbook-dotnet.ion.creating-types)
  + [Ion 이진 덤프 가져오기](#cookbook-dotnet.ion.getting-binary)
  + [Ion 텍스트 덤프 가져오기](#cookbook-dotnet.ion.getting-text)

## 드라이버 가져오기
<a name="cookbook-dotnet.importing"></a>

다음 코드 예제에서는 드라이브를 가져옵니다.

```
using Amazon.QLDB.Driver;
using Amazon.QLDB.Driver.Generic;
using Amazon.QLDB.Driver.Serialization;
```

### Ion 라이브러리 사용
<a name="cookbook-dotnet.importing.ion-library"></a>

```
using Amazon.QLDB.Driver;
using Amazon.IonDotnet.Builders;
```

## 드라이버 인스턴스화
<a name="cookbook-dotnet.instantiating"></a>

다음 코드 예제는 기본 설정을 사용하여 지정된 원장 이름에 연결하는 드라이버 인스턴스를 만듭니다.

------
#### [ Async ]

```
IAsyncQldbDriver driver = AsyncQldbDriver.Builder()
    .WithLedger("vehicle-registration")
    // Add Serialization library
    .WithSerializer(new ObjectSerializer())
    .Build();
```

------
#### [ Sync ]

```
IQldbDriver driver = QldbDriver.Builder()
    .WithLedger("vehicle-registration")
    // Add Serialization library
    .WithSerializer(new ObjectSerializer())
    .Build();
```

------

### Ion 라이브러리 사용
<a name="cookbook-dotnet.instantiating.ion-library"></a>

------
#### [ Async ]

```
IAsyncQldbDriver driver = AsyncQldbDriver.Builder().WithLedger("vehicle-registration").Build();
```

------
#### [ Sync ]

```
IQldbDriver driver = QldbDriver.Builder().WithLedger("vehicle-registration").Build();
```

------

## CRUD 작업
<a name="cookbook-dotnet.crud"></a>

QLDB는 트랜잭션의 일부로 CRUD(*생성, 읽기, 업데이트, 삭제*) 작업을 실행합니다.

**주의**  
가장 좋은 방법은 쓰기 트랜잭션이 완전한 멱등성을 부여하는 것입니다.

**트랜잭션에 멱등성 부여하기**

재시도 시 예상치 못한 부작용이 발생하지 않도록 쓰기 트랜잭션에 멱등성을 부여하는 것이 좋습니다. 여러 번 실행하여 매번 동일한 결과를 생성할 수 있는 트랜잭션은 *멱등성*을 가집니다.

이름이 `Person`인 테이블에 문서를 삽입하는 트랜잭션을 예로 들어 보겠습니다. 트랜잭션은 먼저 문서가 테이블에 이미 존재하는지 여부를 확인해야 합니다. 이렇게 확인하지 않으면 테이블에 문서가 중복될 수 있습니다.

QLDB가 서버 측에서 트랜잭션을 성공적으로 커밋했지만 응답을 기다리는 동안 클라이언트 제한 시간이 초과되었다고 가정해 보겠습니다. 트랜잭션이 멱등성을 가지지 않는 경우 재시도 시 동일한 문서가 두 번 이상 삽입될 수 있습니다.

**인덱스를 사용하여 전체 테이블 스캔 방지**

인덱싱된 필드 또는 문서 ID(예: `WHERE indexedField = 123` 또는 `WHERE indexedField IN (456, 789)`)에서 동등 *연산자*를 사용하여 `WHERE` 조건자 절이 포함된 문을 실행하는 것이 좋습니다. 이 인덱싱된 조회가 없으면 QLDB는 테이블 스캔을 수행해야 하며, 이로 인해 트랜잭션 제한 시간이 초과되거나 *OCC(낙관적 동시성 제어)* 충돌이 발생할 수 있습니다.

OCC에 대한 자세한 내용은 [Amazon QLDB 동시성 모델](concurrency.md) 단원을 참조하세요.

**암시적으로 생성된 트랜잭션**

[Amazon.QLDB.Driver.IQldbDriver.Execute](https://amazon-qldb-docs.s3.amazonaws.com/drivers/dotnet/1.4.1/api/Amazon.QLDB.Driver.IQldbDriver.html) 메서드는 [Amazon.QLDB.Driver.TransactionExecutor](https://amazon-qldb-docs.s3.amazonaws.com/drivers/dotnet/1.4.1/api/Amazon.QLDB.Driver.TransactionExecutor.html)의 인스턴스를 수신하는 Lambda 함수를 허용하며, 이 인스턴스를 사용하여 명령문을 실행할 수 있습니다. `TransactionExecutor`의 인스턴스는 암시적으로 생성된 트랜잭션을 래핑합니다.

트랜잭션 실행자의 `Execute` 메서드를 사용하여 Lambda 함수 내에서 명령문을 실행할 수 있습니다. 드라이버는 Lambda 함수가 반환될 때 트랜잭션을 암시적으로 커밋합니다.

다음 섹션에서는 기본 CRUD 작업을 실행하고, 사용자 지정 재시도 로직을 지정하고, 고유성 제약 조건을 구현하는 방법을 보여줍니다.

**Contents**
+ [테이블 생성](#cookbook-dotnet.crud.creating-tables)
+ [인덱스 생성](#cookbook-dotnet.crud.creating-indexes)
+ [문서 읽기](#cookbook-dotnet.crud.reading)
  + [쿼리 파라미터 사용](#cookbook-dotnet.reading-using-params)
+ [문서 삽입하기](#cookbook-dotnet.crud.inserting)
  + [하나의 명령문에 여러 문서 삽입](#cookbook-dotnet.crud.inserting.multiple)
+ [문서 업데이트](#cookbook-dotnet.crud.updating)
+ [문서 삭제](#cookbook-dotnet.crud.deleting)
+ [하나의 트랜잭션에서 여러 명령문 실행](#cookbook-dotnet.crud.multi-statement)
+ [재시도 로직](#cookbook-dotnet.crud.retry-logic)
+ [고유성 제약 조건 구현](#cookbook-dotnet.crud.uniqueness-constraints)

### 테이블 생성
<a name="cookbook-dotnet.crud.creating-tables"></a>

------
#### [ Async ]

```
IAsyncResult<Table> createResult = await driver.Execute(async txn =>
{
    IQuery<Table> query = txn.Query<Table>("CREATE TABLE Person");
    return await txn.Execute(query);
});

await foreach (var result in createResult)
{
    Console.WriteLine("{ tableId: " + result.TableId + " }");
    // The statement returns the created table ID:
    // { tableId: 4o5Uk09OcjC6PpJpLahceE }
}
```

------
#### [ Sync ]

```
IResult<Table> createResult = driver.Execute( txn =>
{
    IQuery<Table> query = txn.Query<Table>("CREATE TABLE Person");
    return txn.Execute(query);
});

foreach (var result in createResult)
{
    Console.WriteLine("{ tableId: " + result.TableId + " }");
    // The statement returns the created table ID:
    // { tableId: 4o5Uk09OcjC6PpJpLahceE }
}
```

------

#### Ion 라이브러리 사용
<a name="cookbook-dotnet.creating-tables.ion-library"></a>

------
#### [ Async ]

```
// The result from driver.Execute() is buffered into memory because once the
// transaction is committed, streaming the result is no longer possible.
IAsyncResult result = await driver.Execute(async txn =>
{
    return await txn.Execute("CREATE TABLE Person");
});

await foreach (IIonValue row in result)
{
    Console.WriteLine(row.ToPrettyString());
    // The statement returns the created table ID:
    // {
    //    tableId: "4o5Uk09OcjC6PpJpLahceE"
    // }
}
```

------
#### [ Sync ]

```
// The result from driver.Execute() is buffered into memory because once the
// transaction is committed, streaming the result is no longer possible.
IResult result = driver.Execute(txn =>
{
    return txn.Execute("CREATE TABLE Person");
});

foreach (IIonValue row in result)
{
    Console.WriteLine(row.ToPrettyString());
    // The statement returns the created table ID:
    // {
    //    tableId: "4o5Uk09OcjC6PpJpLahceE"
    // }
}
```

------

### 인덱스 생성
<a name="cookbook-dotnet.crud.creating-indexes"></a>

------
#### [ Async ]

```
IAsyncResult<Table> createResult = await driver.Execute(async txn =>
{
    IQuery<Table> query = txn.Query<Table>("CREATE INDEX ON Person(firstName)");
    return await txn.Execute(query);
});

await foreach (var result in createResult)
{
    Console.WriteLine("{ tableId: " + result.TableId + " }");
    // The statement returns the updated table ID:
    // { tableId: 4o5Uk09OcjC6PpJpLahceE }
}
```

------
#### [ Sync ]

```
IResult<Table> createResult = driver.Execute(txn =>
{
    IQuery<Table> query = txn.Query<Table>("CREATE INDEX ON Person(firstName)");
    return txn.Execute(query);
});

foreach (var result in createResult)
{
    Console.WriteLine("{ tableId: " + result.TableId + " }");
    // The statement returns the updated table ID:
    // { tableId: 4o5Uk09OcjC6PpJpLahceE }
}
```

------

#### Ion 라이브러리 사용
<a name="cookbook-dotnet.creating-indexes.ion-library"></a>

------
#### [ Async ]

```
IAsyncResult result = await driver.Execute(async txn =>
{
    return await txn.Execute("CREATE INDEX ON Person(GovId)");
});

await foreach (IIonValue row in result)
{
    Console.WriteLine(row.ToPrettyString());
    // The statement returns the updated table ID:
    // {
    //    tableId: "4o5Uk09OcjC6PpJpLahceE"
    // }
}
```

------
#### [ Sync ]

```
IResult result = driver.Execute(txn =>
{
    return txn.Execute("CREATE INDEX ON Person(GovId)");
});

foreach (IIonValue row in result)
{
    Console.WriteLine(row.ToPrettyString());
    // The statement returns the updated table ID:
    // {
    //    tableId: "4o5Uk09OcjC6PpJpLahceE"
    // }
}
```

------

### 문서 읽기
<a name="cookbook-dotnet.crud.reading"></a>

```
// Assumes that Person table has documents as follows:
// { "GovId": "TOYENC486FH", "FirstName" : "Brent" }
// Person class is defined as follows:
// public class Person
// {
//     public string GovId { get; set; }
//     public string FirstName { get; set; }
//  }

IAsyncResult<Person> result = await driver.Execute(async txn =>
{
    return await txn.Execute(txn.Query<Person>("SELECT * FROM Person WHERE GovId = 'TOYENC486FH'"));
});

await foreach (Person person in result)
{
    Console.WriteLine(person.GovId); // Prints TOYENC486FH.
    Console.WriteLine(person.FirstName); // Prints Brent.
}
```

**참고**  
인덱싱된 조회 없이 쿼리를 실행하면 전체 테이블 스캔이 호출됩니다. 이 예제에서는 성능을 최적화하기 위해 `GovId` 필드에 [인덱스](ql-reference.create-index.md)를 사용하는 것이 좋습니다. `GovId`에 인덱스를 사용하지 않으면 쿼리에 지연 시간이 길어지고 OCC 충돌 예외 또는 트랜잭션 시간 초과가 발생할 수도 있습니다.

#### 쿼리 파라미터 사용
<a name="cookbook-dotnet.reading-using-params"></a>

다음 코드 예제에서는 C\$1 형식 쿼리 파라미터를 사용합니다.

```
IAsyncResult<Person> result = await driver.Execute(async txn =>
{
    return await txn.Execute(txn.Query<Person>("SELECT * FROM Person WHERE FirstName = ?", "Brent"));
});

await foreach (Person person in result)
{
    Console.WriteLine(person.GovId); // Prints TOYENC486FH.
    Console.WriteLine(person.FirstName); // Prints Brent.
}
```

다음 코드 예제에서는 여러 C\$1 형식 쿼리 파라미터를 사용합니다.

```
IAsyncResult<Person> result = await driver.Execute(async txn =>
{
    return await txn.Execute(txn.Query<Person>("SELECT * FROM Person WHERE GovId = ? AND FirstName = ?", "TOYENC486FH", "Brent"));
});

await foreach (Person person in result)
{
    Console.WriteLine(person.GovId); // Prints TOYENC486FH.
    Console.WriteLine(person.FirstName); // Prints Brent.
}
```

다음 코드 예제에서는 C\$1 형식 쿼리 파라미터의 배열을 사용합니다.

```
// Assumes that Person table has documents as follows:
// { "GovId": "TOYENC486FH", "FirstName" : "Brent" }
// { "GovId": "ROEE1C1AABH", "FirstName" : "Jim" }
// { "GovId": "YH844DA7LDB", "FirstName" : "Mary" }

string[] ids = {
    "TOYENC486FH",
    "ROEE1C1AABH",
    "YH844DA7LDB"
};

IAsyncResult<Person> result = await driver.Execute(async txn =>
{
    return await txn.Execute(txn.Query<Person>("SELECT * FROM Person WHERE GovId IN (?,?,?)", ids));
});

await foreach (Person person in result)
{
    Console.WriteLine(person.FirstName); // Prints Brent on first iteration.
    // Prints Jim on second iteration.
    // Prints Mary on third iteration.
}
```

다음 코드 예제에서는 C\$1 목록을 값으로 사용합니다.

```
// Assumes that Person table has document as follows:
// { "GovId": "TOYENC486FH",
//   "FirstName" : "Brent",
//   "Vehicles": [
//      { "Make": "Volkswagen",
//        "Model": "Golf"},
//      { "Make": "Honda",
//        "Model": "Civic"}
//   ]
// }
// Person class is defined as follows:
// public class Person
// {
//     public string GovId { get; set; }
//     public string FirstName { get; set; }
//     public List<Vehicle> Vehicles { get; set; }
// }
// Vehicle class is defined as follows:
// public class Vehicle
// {
//     public string Make { get; set; }
//     public string Model { get; set; }
// }

List<Vehicle> vehicles = new List<Vehicle>
{
    new Vehicle
    {
        Make = "Volkswagen",
        Model = "Golf"
    },
    new Vehicle
    {
        Make = "Honda",
        Model = "Civic"
    }
};

IAsyncResult<Person> result = await driver.Execute(async txn =>
{
    return await txn.Execute(txn.Query<Person>("SELECT * FROM Person WHERE Vehicles = ?", vehicles));
});

await foreach (Person person in result)
{
    Console.WriteLine("{");
    Console.WriteLine($"  GovId: {person.GovId},");
    Console.WriteLine($"  FirstName: {person.FirstName},");
    Console.WriteLine("  Vehicles: [");
    foreach (Vehicle vehicle in person.Vehicles)
    {
        Console.WriteLine("  {");
        Console.WriteLine($"    Make: {vehicle.Make},");
        Console.WriteLine($"    Model: {vehicle.Model},");
        Console.WriteLine("  },");
    }
    Console.WriteLine("  ]");
    Console.WriteLine("}");
    // Prints:
    // {
    //     GovId: TOYENC486FH,
    //     FirstName: Brent,
    //     Vehicles: [
    //     {
    //         Make: Volkswagen,
    //         Model: Golf
    //     },
    //     {
    //         Make: Honda,
    //         Model: Civic
    //     },
    //     ]
    // }
}
```

#### Ion 라이브러리 사용
<a name="cookbook-dotnet.crud.reading.ion-library"></a>

------
#### [ Async ]

```
// Assumes that Person table has documents as follows:
// { "GovId": "TOYENC486FH", "FirstName" : "Brent" }

IAsyncResult result = await driver.Execute(async txn =>
{
    return await txn.Execute("SELECT * FROM Person WHERE GovId = 'TOYENC486FH'");
});

await foreach (IIonValue row in result)
{
    Console.WriteLine(row.GetField("GovId").StringValue); // Prints TOYENC486FH.
    Console.WriteLine(row.GetField("FirstName").StringValue); // Prints Brent.
}
```

------
#### [ Sync ]

```
// Assumes that Person table has documents as follows:
// { "GovId": "TOYENC486FH", "FirstName" : "Brent" }

IResult result = driver.Execute(txn =>
{
    return txn.Execute("SELECT * FROM Person WHERE GovId = 'TOYENC486FH'");
});

foreach (IIonValue row in result)
{
    Console.WriteLine(row.GetField("GovId").StringValue); // Prints TOYENC486FH.
    Console.WriteLine(row.GetField("FirstName").StringValue); // Prints Brent.
}
```

------

**참고**  
인덱싱된 조회 없이 쿼리를 실행하면 전체 테이블 스캔이 호출됩니다. 이 예제에서는 성능을 최적화하기 위해 `GovId` 필드에 [인덱스](ql-reference.create-index.md)를 사용하는 것이 좋습니다. `GovId`에 인덱스를 사용하지 않으면 쿼리에 지연 시간이 길어지고 OCC 충돌 예외 또는 트랜잭션 시간 초과가 발생할 수도 있습니다.

다음 코드 예제는 Ion 유형 쿼리 파라미터를 사용합니다.

------
#### [ Async ]

```
IValueFactory valueFactory = new ValueFactory();
IIonValue ionFirstName = valueFactory.NewString("Brent");

IAsyncResult result = await driver.Execute(async txn =>
{
    return await txn.Execute("SELECT * FROM Person WHERE FirstName = ?", ionFirstName);
});

await foreach (IIonValue row in result)
{
    Console.WriteLine(row.GetField("GovId").StringValue); // Prints TOYENC486FH.
    Console.WriteLine(row.GetField("FirstName").StringValue); // Prints Brent.
}
```

------
#### [ Sync ]

```
IValueFactory valueFactory = new ValueFactory();
IIonValue ionFirstName = valueFactory.NewString("Brent");

IResult result = driver.Execute(txn =>
{
    return txn.Execute("SELECT * FROM Person WHERE FirstName = ?", ionFirstName);
});

foreach (IIonValue row in result)
{
    Console.WriteLine(row.GetField("GovId").StringValue); // Prints TOYENC486FH.
    Console.WriteLine(row.GetField("FirstName").StringValue); // Prints Brent.
}
```

------

다음 코드 예제는 여러 쿼리 파라미터를 사용합니다.

------
#### [ Async ]

```
IIonValue ionGovId = valueFactory.NewString("TOYENC486FH");
IIonValue ionFirstName = valueFactory.NewString("Brent");

IAsyncResult result = await driver.Execute(async txn =>
{
    return await txn.Execute("SELECT * FROM Person WHERE GovId = ? AND FirstName = ?", ionGovId, ionFirstName);
});

await foreach (IIonValue row in result)
{
    Console.WriteLine(row.GetField("GovId").StringValue); // Prints TOYENC486FH.
    Console.WriteLine(row.GetField("FirstName").StringValue); // Prints Brent.
}
```

------
#### [ Sync ]

```
IIonValue ionGovId = valueFactory.NewString("TOYENC486FH");
IIonValue ionFirstName = valueFactory.NewString("Brent");

IResult result = driver.Execute(txn =>
{
    return txn.Execute("SELECT * FROM Person WHERE GovId = ? AND FirstName = ?", ionGovId, ionFirstName);
});

foreach (IIonValue row in result)
{
    Console.WriteLine(row.GetField("GovId").StringValue); // Prints TOYENC486FH.
    Console.WriteLine(row.GetField("FirstName").StringValue); // Prints Brent.
}
```

------

다음 코드 예제는 쿼리 파라미터 목록을 사용합니다.

------
#### [ Async ]

```
// Assumes that Person table has documents as follows:
// { "GovId": "TOYENC486FH", "FirstName" : "Brent" }
// { "GovId": "ROEE1C1AABH", "FirstName" : "Jim" }
// { "GovId": "YH844DA7LDB", "FirstName" : "Mary" }

IIonValue[] ionIds = {
    valueFactory.NewString("TOYENC486FH"),
    valueFactory.NewString("ROEE1C1AABH"),
    valueFactory.NewString("YH844DA7LDB")
};

IAsyncResult result = await driver.Execute(async txn =>
{
    return await txn.Execute("SELECT * FROM Person WHERE GovId IN (?,?,?)", ionIds);
});

await foreach (IIonValue row in result)
{
    Console.WriteLine(row.GetField("FirstName").StringValue); // Prints Brent on first iteration.
                                                              // Prints Jim on second iteration.
                                                              // Prints Mary on third iteration.
}
```

------
#### [ Sync ]

```
// Assumes that Person table has documents as follows:
// { "GovId": "TOYENC486FH", "FirstName" : "Brent" }
// { "GovId": "ROEE1C1AABH", "FirstName" : "Jim" }
// { "GovId": "YH844DA7LDB", "FirstName" : "Mary" }

IIonValue[] ionIds = {
    valueFactory.NewString("TOYENC486FH"),
    valueFactory.NewString("ROEE1C1AABH"),
    valueFactory.NewString("YH844DA7LDB")
};

IResult result = driver.Execute(txn =>
{
    return txn.Execute("SELECT * FROM Person WHERE GovId IN (?,?,?)", ionIds);
});

foreach (IIonValue row in result)
{
    Console.WriteLine(row.GetField("FirstName").StringValue); // Prints Brent on first iteration.
                                                              // Prints Jim on second iteration.
                                                              // Prints Mary on third iteration.
}
```

------

다음 코드 예제에서는 Ion 목록을 값으로 사용합니다. 다른 Ion 유형 사용에 더 알아보려면 [Amazon QLDB에서 Amazon Ion 데이터 타입을 사용한 작업](driver-working-with-ion.md) 단원을 참조하세요.

------
#### [ Async ]

```
// Assumes that Person table has document as follows:
// { "GovId": "TOYENC486FH",
//   "FirstName" : "Brent",
//   "Vehicles": [
//      { "Make": "Volkswagen",
//        "Model": "Golf"},
//      { "Make": "Honda",
//        "Model": "Civic"}
//   ]
// }

IIonValue ionVehicle1 = valueFactory.NewEmptyStruct();
ionVehicle1.SetField("Make", valueFactory.NewString("Volkswagen"));
ionVehicle1.SetField("Model", valueFactory.NewString("Golf"));

IIonValue ionVehicle2 = valueFactory.NewEmptyStruct();
ionVehicle2.SetField("Make", valueFactory.NewString("Honda"));
ionVehicle2.SetField("Model", valueFactory.NewString("Civic"));

IIonValue ionVehicles =  valueFactory.NewEmptyList();
ionVehicles.Add(ionVehicle1);
ionVehicles.Add(ionVehicle2);

IAsyncResult result = await driver.Execute(async txn =>
{
    return await txn.Execute("SELECT * FROM Person WHERE Vehicles = ?", ionVehicles);
});

await foreach (IIonValue row in result)
{
    Console.WriteLine(row.ToPrettyString());
    // Prints:
    // {
    //     GovId: "TOYENC486FN",
    //     FirstName: "Brent",
    //     Vehicles: [
    //     {
    //         Make: "Volkswagen",
    //         Model: "Golf"
    //     },
    //     {
    //         Make: "Honda",
    //         Model: "Civic"
    //     }
    //     ]
    // }
}
```

------
#### [ Sync ]

```
// Assumes that Person table has document as follows:
// { "GovId": "TOYENC486FH",
//   "FirstName" : "Brent",
//   "Vehicles": [
//      { "Make": "Volkswagen",
//        "Model": "Golf"},
//      { "Make": "Honda",
//        "Model": "Civic"}
//   ]
// }

IIonValue ionVehicle1 = valueFactory.NewEmptyStruct();
ionVehicle1.SetField("Make", valueFactory.NewString("Volkswagen"));
ionVehicle1.SetField("Model", valueFactory.NewString("Golf"));

IIonValue ionVehicle2 = valueFactory.NewEmptyStruct();
ionVehicle2.SetField("Make", valueFactory.NewString("Honda"));
ionVehicle2.SetField("Model", valueFactory.NewString("Civic"));

IIonValue ionVehicles =  valueFactory.NewEmptyList();
ionVehicles.Add(ionVehicle1);
ionVehicles.Add(ionVehicle2);

IResult result = driver.Execute(txn =>
{
    return txn.Execute("SELECT * FROM Person WHERE Vehicles = ?", ionVehicles);
});

foreach (IIonValue row in result)
{
    Console.WriteLine(row.ToPrettyString());
    // Prints:
    // {
    //     GovId: "TOYENC486FN",
    //     FirstName: "Brent",
    //     Vehicles: [
    //     {
    //         Make: "Volkswagen",
    //         Model: "Golf"
    //     },
    //     {
    //         Make: "Honda",
    //         Model: "Civic"
    //     }
    //     ]
    // }
}
```

------

### 문서 삽입하기
<a name="cookbook-dotnet.crud.inserting"></a>

다음 코드 예제는 Ion 데이터 유형을 삽입합니다.

```
string govId = "TOYENC486FH";

Person person = new Person
{
    GovId = "TOYENC486FH",
    FirstName = "Brent"
};

await driver.Execute(async txn =>
{
    // Check if a document with GovId:TOYENC486FH exists
    // This is critical to make this transaction idempotent
    IAsyncResult<Person> result = await txn.Execute(txn.Query<Person>("SELECT * FROM Person WHERE GovId = ?", govId));

    // Check if there is a record in the cursor.
    int count = await result.CountAsync();
    if (count > 0)
    {
        // Document already exists, no need to insert
        return;
    }

    // Insert the document.
    await txn.Execute(txn.Query<Document>("INSERT INTO Person ?", person));
});
```

#### Ion 라이브러리 사용
<a name="cookbook-dotnet.crud.inserting.ion-library"></a>

------
#### [ Async ]

```
IIonValue ionGovId = valueFactory.NewString("TOYENC486FH");

IIonValue ionPerson = valueFactory.NewEmptyStruct();
ionPerson.SetField("GovId", valueFactory.NewString("TOYENC486FH"));
ionPerson.SetField("FirstName", valueFactory.NewString("Brent"));

await driver.Execute(async txn =>
{
    // Check if a document with GovId:TOYENC486FH exists
    // This is critical to make this transaction idempotent
    IAsyncResult result = await txn.Execute("SELECT * FROM Person WHERE GovId = ?", ionGovId);

    // Check if there is a record in the cursor.
    int count = await result.CountAsync();
    if (count > 0)
    {
        // Document already exists, no need to insert
        return;
    }

    // Insert the document.
    await txn.Execute("INSERT INTO Person ?", ionPerson);
});
```

------
#### [ Sync ]

```
IIonValue ionGovId = valueFactory.NewString("TOYENC486FH");

IIonValue ionPerson = valueFactory.NewEmptyStruct();
ionPerson.SetField("GovId", valueFactory.NewString("TOYENC486FH"));
ionPerson.SetField("FirstName", valueFactory.NewString("Brent"));

driver.Execute(txn =>
{
    // Check if a document with GovId:TOYENC486FH exists
    // This is critical to make this transaction idempotent
    IResult result = txn.Execute("SELECT * FROM Person WHERE GovId = ?", ionGovId);

    // Check if there is a record in the cursor.
    int count = result.Count();
    if (count > 0)
    {
        // Document already exists, no need to insert
        return;
    }

    // Insert the document.
    txn.Execute("INSERT INTO Person ?", ionPerson);
});
```

------

이 트랜잭션은 문서를 `Person` 테이블에 삽입합니다. 삽입하기 전에 먼저 문서가 테이블에 이미 있는지 확인합니다. **이 검사를 통해 트랜잭션은 본질적으로 멱등성을 가지게 됩니다.** 이 트랜잭션을 여러 번 실행하더라도 의도하지 않은 부작용이 발생하지는 않습니다.

**참고**  
이 예제에서는 성능을 최적화하기 위해 `GovId` 필드에 인덱스를 사용하는 것이 좋습니다. `GovId`에 인덱스를 설정하지 않으면 명령문의 지연 시간이 길어지고 OCC 충돌 예외 또는 트랜잭션 시간 초과가 발생할 수도 있습니다.

#### 하나의 명령문에 여러 문서 삽입
<a name="cookbook-dotnet.crud.inserting.multiple"></a>

단일 [INSERT](ql-reference.insert.md) 문을 사용하여 여러 문서를 삽입하려면 다음과 같이 문에 C\$1 `List` 파라미터를 전달할 수 있습니다.

```
Person person1 = new Person
{
    FirstName = "Brent",
    GovId = "TOYENC486FH"
};

Person person2 = new Person
{
    FirstName = "Jim",
    GovId = "ROEE1C1AABH"
};

List<Person> people = new List<Person>();
people.Add(person1);
people.Add(person2);

IAsyncResult<Document> result = await driver.Execute(async txn =>
{
    return await txn.Execute(txn.Query<Document>("INSERT INTO Person ?", people));
});

await foreach (Document row in result)
{
    Console.WriteLine("{ documentId: " + row.DocumentId + " }");
    // The statement returns the created documents' ID:
    // { documentId: 6BFt5eJQDFLBW2aR8LPw42 }
    // { documentId: K5Zrcb6N3gmIEHgGhwoyKF }
}
```

##### Ion 라이브러리 사용
<a name="cookbook-dotnet.crud.inserting.multiple.ion-library"></a>

단일 [INSERT](ql-reference.insert.md) 문을 사용하여 여러 문서를 삽입하려면 다음과 같이 [Ion 목록](driver-working-with-ion.md#driver-ion-list) 타입의 파라미터를 해당 문에 전달할 수 있습니다.

------
#### [ Async ]

```
IIonValue ionPerson1 = valueFactory.NewEmptyStruct();
ionPerson1.SetField("FirstName", valueFactory.NewString("Brent"));
ionPerson1.SetField("GovId", valueFactory.NewString("TOYENC486FH"));

IIonValue ionPerson2 = valueFactory.NewEmptyStruct();
ionPerson2.SetField("FirstName", valueFactory.NewString("Jim"));
ionPerson2.SetField("GovId", valueFactory.NewString("ROEE1C1AABH"));

IIonValue ionPeople = valueFactory.NewEmptyList();
ionPeople.Add(ionPerson1);
ionPeople.Add(ionPerson2);

IAsyncResult result = await driver.Execute(async txn =>
{
    return await txn.Execute("INSERT INTO Person ?", ionPeople);
});

await foreach (IIonValue row in result)
{
    Console.WriteLine(row.ToPrettyString());
    // The statement returns the created documents' ID:
    // {
    //     documentId: "6BFt5eJQDFLBW2aR8LPw42"
    // }
    //
    // {
    //     documentId: "K5Zrcb6N3gmIEHgGhwoyKF"
    // }
}
```

------
#### [ Sync ]

```
IIonValue ionPerson1 = valueFactory.NewEmptyStruct();
ionPerson1.SetField("FirstName", valueFactory.NewString("Brent"));
ionPerson1.SetField("GovId", valueFactory.NewString("TOYENC486FH"));

IIonValue ionPerson2 = valueFactory.NewEmptyStruct();
ionPerson2.SetField("FirstName", valueFactory.NewString("Jim"));
ionPerson2.SetField("GovId", valueFactory.NewString("ROEE1C1AABH"));

IIonValue ionPeople = valueFactory.NewEmptyList();
ionPeople.Add(ionPerson1);
ionPeople.Add(ionPerson2);

IResult result = driver.Execute(txn =>
{
    return txn.Execute("INSERT INTO Person ?", ionPeople);
});

foreach (IIonValue row in result)
{
    Console.WriteLine(row.ToPrettyString());
    // The statement returns the created documents' ID:
    // {
    //     documentId: "6BFt5eJQDFLBW2aR8LPw42"
    // }
    //
    // {
    //     documentId: "K5Zrcb6N3gmIEHgGhwoyKF"
    // }
}
```

------

Ion 목록을 전달할 때는 변수 자리 표시자(`?`)를 이중 꺾쇠 괄호(`<<...>>`)로 묶지 마세요. 수동 PartiQL 문에서 이중 꺾쇠 괄호는 *백*으로 알려진 정렬되지 않은 모음을 의미합니다.

### 문서 업데이트
<a name="cookbook-dotnet.crud.updating"></a>

```
string govId = "TOYENC486FH";
string firstName = "John";

IAsyncResult<Document> result = await driver.Execute(async txn =>
{
    return await txn.Execute(txn.Query<Document>("UPDATE Person SET FirstName = ? WHERE GovId = ?", firstName , govId));
});

await foreach (Document row in result)
{
    Console.WriteLine("{ documentId: " + row.DocumentId + " }");
    // The statement returns the updated document ID:
    // { documentId: Djg30Zoltqy5M4BFsA2jSJ }
}
```

#### Ion 라이브러리 사용
<a name="cookbook-dotnet.crud.updating.ion-library"></a>

------
#### [ Async ]

```
IIonValue ionGovId = valueFactory.NewString("TOYENC486FH");
IIonValue ionFirstName = valueFactory.NewString("John");

IAsyncResult result = await driver.Execute(async txn =>
{
    return await txn.Execute("UPDATE Person SET FirstName = ? WHERE GovId = ?", ionFirstName , ionGovId);
});

await foreach (IIonValue row in result)
{
    Console.WriteLine(row.ToPrettyString());
    // The statement returns the updated document ID:
    // {
    //     documentId: "Djg30Zoltqy5M4BFsA2jSJ"
    // }
}
```

------
#### [ Sync ]

```
IIonValue ionGovId = valueFactory.NewString("TOYENC486FH");
IIonValue ionFirstName = valueFactory.NewString("John");

IResult result = driver.Execute(txn =>
{
    return txn.Execute("UPDATE Person SET FirstName = ? WHERE GovId = ?", ionFirstName , ionGovId);
});

foreach (IIonValue row in result)
{
    Console.WriteLine(row.ToPrettyString());
    // The statement returns the updated document ID:
    // {
    //     documentId: "Djg30Zoltqy5M4BFsA2jSJ"
    // }
}
```

------

**참고**  
이 예제에서는 성능을 최적화하기 위해 `GovId` 필드에 인덱스를 사용하는 것이 좋습니다. `GovId`에 인덱스를 설정하지 않으면 명령문의 지연 시간이 길어지고 OCC 충돌 예외 또는 트랜잭션 시간 초과가 발생할 수도 있습니다.

### 문서 삭제
<a name="cookbook-dotnet.crud.deleting"></a>

```
string govId = "TOYENC486FH";

IAsyncResult<Document> result = await driver.Execute(async txn =>
{
    return await txn.Execute(txn.Query<Document>("DELETE FROM Person WHERE GovId = ?", govId));
});

await foreach (Document row in result)
{
    Console.WriteLine("{ documentId: " + row.DocumentId + " }");
    // The statement returns the updated document ID:
    // { documentId: Djg30Zoltqy5M4BFsA2jSJ }
}
```

#### Ion 라이브러리 사용
<a name="cookbook-dotnet.crud.deleting.ion-library"></a>

------
#### [ Async ]

```
IIonValue ionGovId = valueFactory.NewString("TOYENC486FH");

IAsyncResult result = await driver.Execute(async txn =>
{
    return await txn.Execute("DELETE FROM Person WHERE GovId = ?", ionGovId);
});

await foreach (IIonValue row in result)
{
    Console.WriteLine(row.ToPrettyString());
    // The statement returns the deleted document ID:
    // {
    //     documentId: "Djg30Zoltqy5M4BFsA2jSJ"
    // }
}
```

------
#### [ Sync ]

```
IIonValue ionGovId = valueFactory.NewString("TOYENC486FH");

IResult result = driver.Execute(txn =>
{
    return txn.Execute("DELETE FROM Person WHERE GovId = ?", ionGovId);
});

foreach (IIonValue row in result)
{
    Console.WriteLine(row.ToPrettyString());
    // The statement returns the deleted document ID:
    // {
    //     documentId: "Djg30Zoltqy5M4BFsA2jSJ"
    // }
}
```

------

**참고**  
이 예제에서는 성능을 최적화하기 위해 `GovId` 필드에 인덱스를 사용하는 것이 좋습니다. `GovId`에 인덱스를 설정하지 않으면 명령문의 지연 시간이 길어지고 OCC 충돌 예외 또는 트랜잭션 시간 초과가 발생할 수도 있습니다.

### 하나의 트랜잭션에서 여러 명령문 실행
<a name="cookbook-dotnet.crud.multi-statement"></a>

```
// This code snippet is intentionally trivial. In reality you wouldn't do this because you'd
// set your UPDATE to filter on vin and insured, and check if you updated something or not.
public static async Task<bool> InsureVehicle(IAsyncQldbDriver driver, string vin)
{
    return await driver.Execute(async txn =>
    {
        // Check if the vehicle is insured.
        Amazon.QLDB.Driver.Generic.IAsyncResult<Vehicle> result = await txn.Execute(
            txn.Query<Vehicle>("SELECT insured FROM Vehicles WHERE vin = ? AND insured = FALSE", vin));

        if (await result.CountAsync() > 0)
        {
            // If the vehicle is not insured, insure it.
            await txn.Execute(
                txn.Query<Document>("UPDATE Vehicles SET insured = TRUE WHERE vin = ?", vin));
            return true;
        }
        return false;
    });
}
```

#### Ion 라이브러리 사용
<a name="cookbook-dotnet.crud.multi-statement.ion-library"></a>

------
#### [ Async ]

```
// This code snippet is intentionally trivial. In reality you wouldn't do this because you'd
// set your UPDATE to filter on vin and insured, and check if you updated something or not.
public static async Task<bool> InsureVehicle(IAsyncQldbDriver driver, string vin)
{
    ValueFactory valueFactory = new ValueFactory();
    IIonValue ionVin = valueFactory.NewString(vin);

    return await driver.Execute(async txn =>
    {
        // Check if the vehicle is insured.
        Amazon.QLDB.Driver.IAsyncResult result = await txn.Execute(
            "SELECT insured FROM Vehicles WHERE vin = ? AND insured = FALSE", ionVin);

        if (await result.CountAsync() > 0)
        {
            // If the vehicle is not insured, insure it.
            await txn.Execute(
                "UPDATE Vehicles SET insured = TRUE WHERE vin = ?", ionVin);
            return true;
        }
        return false;
    });
}
```

------

### 재시도 로직
<a name="cookbook-dotnet.crud.retry-logic"></a>

드라이버의 내장 재시도 로직에 대한 자세한 내용은 [Amazon QLDB의 드라이버를 사용한 재시도 정책에 대한 이해](driver-retry-policy.md) 섹션을 참조하세요.

### 고유성 제약 조건 구현
<a name="cookbook-dotnet.crud.uniqueness-constraints"></a>

QLDB는 고유 인덱스를 지원하지 않지만 애플리케이션에서 이 동작을 구현할 수 있습니다.

`Person` 테이블의 `GovId` 필드에 고유성 제약 조건을 구현하려고 한다고 가정해 보겠습니다. 이렇게 하면 다음 작업을 수행하는 트랜잭션을 작성합니다.

1. 테이블에 지정된 `GovId`가 있는 기존 문서가 없는지 확인합니다.

1. 어설션이 통과하면 문서를 삽입합니다.

경쟁 트랜잭션이 어설션을 동시에 통과하면 트랜잭션 중 하나만 성공적으로 커밋됩니다. 다른 트랜잭션은 OCC 충돌 예외가 발생하여 실패합니다.

다음 코드 예제는 이 고유성 제약 조건 구현 방법을 보여줍니다.

```
string govId = "TOYENC486FH";

Person person = new Person
{
    GovId = "TOYENC486FH",
    FirstName = "Brent"
};

await driver.Execute(async txn =>
{
    // Check if a document with GovId:TOYENC486FH exists
    // This is critical to make this transaction idempotent
    IAsyncResult<Person> result = await txn.Execute(txn.Query<Person>("SELECT * FROM Person WHERE GovId = ?", govId));

    // Check if there is a record in the cursor.
    int count = await result.CountAsync();
    if (count > 0)
    {
        // Document already exists, no need to insert
        return;
    }

    // Insert the document.
    await txn.Execute(txn.Query<Document>("INSERT INTO Person ?", person));
});
```

#### Ion 라이브러리 사용
<a name="cookbook-dotnet.crud.uniqueness.ion-library"></a>

------
#### [ Async ]

```
IIonValue ionGovId = valueFactory.NewString("TOYENC486FH");

IIonValue ionPerson = valueFactory.NewEmptyStruct();
ionPerson.SetField("GovId", valueFactory.NewString("TOYENC486FH"));
ionPerson.SetField("FirstName", valueFactory.NewString("Brent"));

await driver.Execute(async txn =>
{
    // Check if a document with GovId:TOYENC486FH exists
    // This is critical to make this transaction idempotent
    IAsyncResult result = await txn.Execute("SELECT * FROM Person WHERE GovId = ?", ionGovId);

    // Check if there is a record in the cursor.
    int count = await result.CountAsync();
    if (count > 0)
    {
        // Document already exists, no need to insert
        return;
    }

    // Insert the document.
    await txn.Execute("INSERT INTO Person ?", ionPerson);
});
```

------
#### [ Sync ]

```
IIonValue ionGovId = valueFactory.NewString("TOYENC486FH");

IIonValue ionPerson = valueFactory.NewEmptyStruct();
ionPerson.SetField("GovId", valueFactory.NewString("TOYENC486FH"));
ionPerson.SetField("FirstName", valueFactory.NewString("Brent"));

driver.Execute(txn =>
{
    // Check if a document with GovId:TOYENC486FH exists
    // This is critical to make this transaction idempotent
    IResult result = txn.Execute("SELECT * FROM Person WHERE GovId = ?", ionGovId);

    // Check if there is a record in the cursor.
    int count = result.Count();
    if (count > 0)
    {
        // Document already exists, no need to insert
        return;
    }

    // Insert the document.
    txn.Execute("INSERT INTO Person ?", ionPerson);
});
```

------

**참고**  
이 예제에서는 성능을 최적화하기 위해 `GovId` 필드에 인덱스를 사용하는 것이 좋습니다. `GovId`에 인덱스를 설정하지 않으면 명령문의 지연 시간이 길어지고 OCC 충돌 예외 또는 트랜잭션 시간 초과가 발생할 수도 있습니다.

## Amazon Ion 작업
<a name="cookbook-dotnet.ion"></a>

QLDB에서 Amazon Ion 데이터를 처리하는 방법은 여러 가지가 있습니다. [Ion 라이브러리](https://github.com/amzn/ion-dotnet)를 사용하여 Ion 값을 생성하고 수정할 수 있습니다. 또는 [Ion 객체 매퍼](https://github.com/amzn/ion-object-mapper-dotnet)를 사용하여 C\$1 *POCO(Plain Old CLR Object)*를 Ion 값에 매핑하거나 그 반대로 매핑할 수 있습니다. .NET용 QLDB 드라이버 버전 1.3.0에서는 Ion 객체 매퍼에 대한 지원이 도입되었습니다.

다음 섹션에서는 두 기술을 모두 사용하여 Ion 데이터를 처리하는 코드 예제를 제공합니다.

**Contents**
+ [Ion 모듈 가져오기](#cookbook-dotnet.ion.import)
+ [Ion 유형 생성](#cookbook-dotnet.ion.creating-types)
+ [Ion 이진 덤프 가져오기](#cookbook-dotnet.ion.getting-binary)
+ [Ion 텍스트 덤프 가져오기](#cookbook-dotnet.ion.getting-text)

### Ion 모듈 가져오기
<a name="cookbook-dotnet.ion.import"></a>

```
using Amazon.IonObjectMapper;
```

#### Ion 라이브러리 사용
<a name="cookbook-dotnet.ion.import.ion-library"></a>

```
using Amazon.IonDotnet.Builders;
```

### Ion 유형 생성
<a name="cookbook-dotnet.ion.creating-types"></a>

다음 코드 예제는 Ion 객체 매퍼를 사용하여 C\$1 객체에서 Ion 값을 만드는 방법을 보여줍니다.

```
// Assumes that Person class is defined as follows:
// public class Person
// {
//     public string FirstName { get; set; }
//     public int Age { get; set; }
// }

// Initialize the Ion Object Mapper
IonSerializer ionSerializer = new IonSerializer();

// The C# object to be serialized
Person person = new Person
{
    FirstName = "John",
    Age = 13
};

// Serialize the C# object into stream using the Ion Object Mapper
Stream stream = ionSerializer.Serialize(person);

// Load will take in stream and return a datagram; a top level container of Ion values.
IIonValue ionDatagram = IonLoader.Default.Load(stream);

// To get the Ion value within the datagram, we call GetElementAt(0).
IIonValue ionPerson = ionDatagram.GetElementAt(0);

Console.WriteLine(ionPerson.GetField("firstName").StringValue);
Console.WriteLine(ionPerson.GetField("age").IntValue);
```

#### Ion 라이브러리 사용
<a name="cookbook-dotnet.ion.creating-types.ion-library"></a>

다음 코드 예제는 Ion 라이브러리를 사용하여 Ion 값을 생성하는 두 가지 방법을 보여줍니다.

**`ValueFactory` 사용**

```
using Amazon.IonDotnet.Tree;
using Amazon.IonDotnet.Tree.Impl;

IValueFactory valueFactory = new ValueFactory();

IIonValue ionPerson = valueFactory.NewEmptyStruct();
ionPerson.SetField("firstName", valueFactory.NewString("John"));
ionPerson.SetField("age", valueFactory.NewInt(13));

Console.WriteLine(ionPerson.GetField("firstName").StringValue);
Console.WriteLine(ionPerson.GetField("age").IntValue);
```

**`IonLoader` 사용**

```
using Amazon.IonDotnet.Builders;
using Amazon.IonDotnet.Tree;

// Load will take in Ion text and return a datagram; a top level container of Ion values.
IIonValue ionDatagram = IonLoader.Default.Load("{firstName: \"John\", age: 13}");

// To get the Ion value within the datagram, we call GetElementAt(0).
IIonValue ionPerson = ionDatagram.GetElementAt(0);

Console.WriteLine(ionPerson.GetField("firstName").StringValue);
Console.WriteLine(ionPerson.GetField("age").IntValue);
```

### Ion 이진 덤프 가져오기
<a name="cookbook-dotnet.ion.getting-binary"></a>

```
// Initialize the Ion Object Mapper with Ion binary serialization format
IonSerializer ionSerializer = new IonSerializer(new IonSerializationOptions
{
    Format = IonSerializationFormat.BINARY
});

// The C# object to be serialized
Person person = new Person
{
    FirstName = "John",
    Age = 13
};

MemoryStream stream = (MemoryStream) ionSerializer.Serialize(person);
Console.WriteLine(BitConverter.ToString(stream.ToArray()));
```

#### Ion 라이브러리 사용
<a name="cookbook-dotnet.ion.getting-binary.ion-library"></a>

```
// ionObject is an Ion struct
MemoryStream stream = new MemoryStream();
using (var writer = IonBinaryWriterBuilder.Build(stream))
{
    ionObject.WriteTo(writer);
    writer.Finish();
}

Console.WriteLine(BitConverter.ToString(stream.ToArray()));
```

### Ion 텍스트 덤프 가져오기
<a name="cookbook-dotnet.ion.getting-text"></a>

```
// Initialize the Ion Object Mapper
IonSerializer ionSerializer = new IonSerializer(new IonSerializationOptions
{
    Format = IonSerializationFormat.TEXT
});

// The C# object to be serialized
Person person = new Person
{
    FirstName = "John",
    Age = 13
};

MemoryStream stream = (MemoryStream) ionSerializer.Serialize(person);
Console.WriteLine(System.Text.Encoding.UTF8.GetString(stream.ToArray()));
```

#### Ion 라이브러리 사용
<a name="cookbook-dotnet.ion.getting-text.ion-library"></a>

```
// ionObject is an Ion struct
StringWriter sw = new StringWriter();
using (var writer = IonTextWriterBuilder.Build(sw))
{
    ionObject.WriteTo(writer);
    writer.Finish();
}

Console.WriteLine(sw.ToString());
```

Ion 작업에 대한 자세한 정보는 GitHub의 [Amazon Ion 설명서](http://amzn.github.io/ion-docs/)를 참조하세요. QLDB에서 Ion을 사용하는 방법에 대한 추가 코드 예제는 [Amazon QLDB에서 Amazon Ion 데이터 타입을 사용한 작업](driver-working-with-ion.md) 섹션을 참조하세요.