LINQPad Premium ではじめる AWS SDK for .NET

# 概要

AWS S3 の API を使うプログラムを LINQPad で作成し、AWS SDK for .NET の使い方を把握する。

# 対象読者

- AWSを使うアプリケーション開発をこれから始めようとしている
- C# の構文や .NET の API についてある程度の知識を持っている
- LINQPad Premium を持っている ※

※LINQPad には無料の Free Edition もあるが、この記事では有償版である Premium Edition を前提に書く。まだ持ってない？買おう！

# なんでLINQPad？

[LINQPad](https://www.linqpad.net/) は Visual Studio よりも一億万倍くらいお手軽に .NET プログラムを作成できる開発・実行環境。小さいプログラムの作成に特化していて、プログラミングやAPIの学習にとても向いている。

LINQPad を使って学習すると次のような良いことがある。

- コードが一瞬で実行できるので、結果がすぐわかる
- 入力補完が効くので、APIの名前などを正確に覚えてなくても書ける
- 変数のダンプが見やすいので、API仕様の理解やデバッグが捗る

また、実際の開発現場でも下記のようなときに役立つ。

- テストデータを作って S3 や DynamoDB に突っ込む
- AWSコンソールを開かずにEC2インスタンスの状態を表示する
- 不慣れなAPIを使うアプリケーションのサンプル実装

# AWS SDK for .NET を使う準備

AWS SDK for .NET を使うには、まず AWS の認証情報を開発端末に登録する必要がある。

手順：

1. LINQPad を起動し、NuGet で AWSSDK.Core をインストール
2. AWS コンソールで開発用IAMユーザの AccessKey を発行
3. 発行した認証情報を開発端末に登録するコードを書いて実行
4. LINQPad を再起動

## NuGet で AWSSDK.Core をインストール

まず AWSSDK を使用するための NuGet パッケージをインストールする。

手順：LINQPadを起動後、エディタで右クリック、NuGet Package Manager

![1-linqpad-nuget.png](https://qiita-image-store.s3.amazonaws.com/0/118451/f3b30797-ce41-04dd-5dbb-877cc2707de9.png)


Search online に ASSDK と入力、リストに表示された AWSSDK - Core Runtime を Add To Queryする

![1-linqpad-nuget2.png](https://qiita-image-store.s3.amazonaws.com/0/118451/f651ded1-e45e-4d3d-5ecf-d2f817ff2c48.png)

これで LINQPad の準備は完了。

## AccessKey/SecretKeyを発行

開発用の認証情報を発行する。手順：

AWSコンソールにログイン
⇒ IAM
⇒ Users
⇒ 開発用のユーザを選択(なかったら作る)
⇒ Security credentials
⇒ Access keys
⇒ Create access key

![0-key.png](https://qiita-image-store.s3.amazonaws.com/0/118451/b1c3d255-f75a-b794-d419-44b4fc4e9e6c.png)

ここに表示された Key は次で使う。

※Secret access key は絶対に他の人に見せてはいけない。認証情報の登録がすんだら速やかに破棄すること。万が一漏洩した場合は、この画面でキーを削除して無効化できる。

## 発行したSecretKeyを開発端末に登録するコードを実行

LINQPad に戻って、Language を `C# Program` にして認証情報を登録するコードを書く。

![1-linqpad-lang.png](https://qiita-image-store.s3.amazonaws.com/0/118451/3775be5a-e79a-94d1-4137-958bc498c68f.png)

```csharp
void Main()
{
    var options = new Amazon.Runtime.CredentialManagement.CredentialProfileOptions
    {
        AccessKey = "accesskey", // ここに AWSコンソールで発行したKeyを入れる
        SecretKey = "secretkey", // コードを実行したらすぐに消してね
    };
    var profile = new Amazon.Runtime.CredentialManagement.CredentialProfile("default", options);
    profile.Region = Amazon.RegionEndpoint.APNortheast1; // 使うリージョンに合わせて変えて
    var netSDKFile = new Amazon.Runtime.CredentialManagement.NetSDKCredentialsFile();
    netSDKFile.RegisterProfile(profile);
}
```

5, 6行目に先に発行したKeyを設定し実行すると開発端末に認証情報が登録され、以後特に指定しない限りはこの認証情報が使われるようになる。

コードを実行するにはF5キーまたはエディタ左上の &#x25b6; をクリック。

実行が完了したら LINQPad を再起動する。（再起動しないとAWSSDKが認証情報を読み込んでくれないっぽい）

これで AWS SDK for .NET を使う準備が整った。

# 初めてのS3 AWS SDK for .NET 編 

ここではS3に次の操作を実行するプログラムを作成する。

- バケットを作成
- バケット一覧を取得
- オブジェクトを作成
- オブジェクト一覧を取得
- オブジェクトの削除
- バケットの削除

プログラム作成を通して、AWS SDK for .NET のお作法的なことを把握してもらえるとよい。

## コードを書き始める前に知っておいてほしいこと

非同期メソッドを使おう！

.NET Framework 版の AWSSDK には同期版・非同期版それぞれの API が提供されているが、.NET Core 版には非同期版しかない。なので特に理由がなければ非同期版APIを使っておくのがよい。非同期で書いておくと LINQPad で書いたコードを後で Lambda に移植したりするのも楽だし。

.NET Framework 版の ListBucket には同期・非同期両方のメソッドがある

![3-s312.png](https://qiita-image-store.s3.amazonaws.com/0/118451/05f375fb-8222-98ca-93f6-6975ab9f3d9b.png)

.NET Core版 の ListBucket には非同期のメソッドしかない

![3-s313.png](https://qiita-image-store.s3.amazonaws.com/0/118451/28e177f4-9427-d1cb-9027-c1dc87564982.png)

### LINQPad で非同期メソッドを使うための手順

1. Language を `C# Program` に設定 ※
2. `void Main()` を `async Task Main()` に変える
3. Taskにカーソルをあてて using System.Threading.Tasks する

![2-async.png](https://qiita-image-store.s3.amazonaws.com/0/118451/4a1ed59c-bf9e-b2fd-f7f1-067d0c0c864f.png)


※毎回 Language を `C# Program` に変えるのがめんどくさいときは、デフォルト設定を変えられる。

手順：メニュー Edit ⇒ Preferences ⇒ Query ⇒ Default Query Language を `C# Program`

![2-async2.png](https://qiita-image-store.s3.amazonaws.com/0/118451/10b89ab0-3520-44e2-7905-28cad21dcd65.png)

これで準備完了。

## AWSSDK.S3 をインストール

S3 を使うには NuGet パッケージ `AWSSDK.S3` をインストールする。

手順：エディタで右クリック、NuGet Package Manager

![3-s3.png](https://qiita-image-store.s3.amazonaws.com/0/118451/d3e0c9fb-c62f-0e92-fdbb-831dc034367f.png)


Search online に ASSDK.S3 と入力、リストに表示された AWSSDK - Simple Storage Service を Add To Query する

![3-s32.png](https://qiita-image-store.s3.amazonaws.com/0/118451/c707ba46-8b77-fee5-b87c-8f59bb7941dd.png)


なお、S3以外のサービスも同様に AWSSDK.{サービス名} という NuGet パッケージで提供されている。EC2 なら `AWSSDK.EC2`、SQS なら `AWSSDK.SQS` など。

## サービスクライアントを作成

S3 の API を呼ぶには Amazon.S3.AmazonS3Client クラスを使う。

```csharp
async Task Main()
{
    var s3 = new Amazon.S3.AmazonS3Client();
}
```

### 入力補完を活用する

LINQPad は入力の候補を提示・補完してくれるので、クラスの名前を覚えてなくても `Amazon.` と入力すればだいたいなんとかなる。

![3-s33.png](https://qiita-image-store.s3.amazonaws.com/0/118451/879d1261-54c0-adfb-78c6-e82be4a33b07.png)


S3 の API も同様に、`s3.` と入力すれば一覧表示される。

![3-s34.png](https://qiita-image-store.s3.amazonaws.com/0/118451/8be7b102-80d2-f651-9eb0-a25ff747a552.png)

![3-s35.png](https://qiita-image-store.s3.amazonaws.com/0/118451/bea5df99-d57d-e10f-1814-85acf55c32bf.png)

ちなみにこの入力候補は、`.` や `(` を打った時のほか、`Ctrl+Space` で表示させることもできる。

## ListBuckets

バケット一覧を表示してみる。

```csharp
async Task Main()
{
    var s3 = new Amazon.S3.AmazonS3Client();
    
    // list buckets
    var listBucketResponse = await s3.ListBucketsAsync();
    listBucketResponse.Dump();
}
```

レスポンスを確認したかったらとりあえず Dump() しておくとよい。下記のように出力できる。

![3-s36.png](https://qiita-image-store.s3.amazonaws.com/0/118451/14a50c5d-c9fa-e3c1-0ef5-06b06462fd19.png)

Dumpされた内容から、バケットに関する情報は Buckets プロパティに入ってきそうだなーと推測できる。(まだバケット無いけど)

## PutBucket

バケットを作ってみる。

```csharp
async Task Main()
{
    var s3 = new Amazon.S3.AmazonS3Client();
    
    // put bucket (create new bucket)
    var putBucketResponse = await s3.PutBucketAsync("my-favorit-baketto");
    
    // list buckets
    var listBucketResponse = await s3.ListBucketsAsync();
    listBucketResponse.Dump(); // バケット一覧を表示
}
```

作成したバケットの名前と作成時刻が表示された。

![3-s310.png](https://qiita-image-store.s3.amazonaws.com/0/118451/ac3cc5e5-33c0-99a6-500c-b32cb1228e94.png)

ここから次のようなことがわかる。(ドキュメントを読まなくても！)

- ListBucketResponse.Buckets にバケット情報 S3Bucket がリストで入る
- S3Bucket には BucketName と CreationDate がある
- バケット名を取得したかったら `string bucketName = listBucketResponse.Buckets[0].BucketName;` のように書けばよい

AWS コンソール上でもちゃんと作成されてることが確認できる。

![3-s37.png](https://qiita-image-store.s3.amazonaws.com/0/118451/294b4683-f29b-117d-b1b8-a72df0b0d031.png)


## PutObject

バケットの中にオブジェクト(ファイル)を作ってみる。

```csharp
async Task Main()
{
    var s3 = new Amazon.S3.AmazonS3Client();

    PutObjectResponse putobjres = await s3.PutObjectAsync(new PutObjectRequest()
    {
         BucketName = "my-favorit-baketto",
         Key = "test",
         ContentBody = "i am a pen."
    });
}
```

PutObjectにはS3のバケット名やキー名、コンテンツをパラメータとして渡す必要がある。このパラメータに PutObjectRequest というクラスを使っている。また、APIのレスポンスは PutObjectResponse というクラスのオブジェクトで帰ってくる。

PutObjectRequest や PutObjectRequest クラスは別の名前空間にあるので using が必要な点に注意。

![3-s39.png](https://qiita-image-store.s3.amazonaws.com/0/118451/3a967f3d-a4c3-916b-aaa8-a1a455c29ffb.png)

AWSコンソール上でもこのコードで作成したオブジェクトを確認。

![3-s38.png](https://qiita-image-store.s3.amazonaws.com/0/118451/8d991e89-8c3a-2e8c-2eba-eb74dc0d20c7.png)

### AWSSDKのクラス構造

上記のコードで使ったクラスは下記の構造になっている。

- Amazon.S3
    - AmazonS3Client
- Amazon.S3.Model
    - PutObjectRequest
    - PutObjectResponse
    - その他のRequest
    - その他のResponse

これはS3に限らず、他のAWSサービスのAPIでも同様なので覚えておこう。

あらかじめ名前空間 `Amazon.{サービス名}` と `Amazon.{サービス名}.Model` を using しておくのもよい。

## ListObject

バケットの中のオブジェクト一覧を取得する。

```csharp
async Task Main()
{
    var s3 = new Amazon.S3.AmazonS3Client();

    var listObjRes = await s3.ListObjectsAsync("my-favorit-baketto");
    listObjRes.Dump();
    
    // ListObjects のパラメータには ListObjectsRequest クラスを使うこともできるが、
    // 上記のようにクラスを作成せずに使える場合もある。
}
```

PutObject で作成した test が表示された。

![3-s311.png](https://qiita-image-store.s3.amazonaws.com/0/118451/69c9b60b-672d-60df-f04e-34e6e389dd50.png)

キー名を取得したかったら `string key = listObjRes.S3Objects[0].Key;` のようにすれば良い。

## GetObject

オブジェクトの中身を取得する。これはひと手間必要。

GetObjectResponse を Dump() するとわかるが、GetObjectResponse にはコンテンツを直接取得するためのプロパティやメソッドがない。その代わりに ResponseStream が用意されているのでここから読みとる必要がある。

```csharp
async Task Main()
{
    var s3 = new Amazon.S3.AmazonS3Client();

    var getObjectResponse = await s3.GetObjectAsync("my-favorit-baketto", "test");

    // StreamReader を使って ResponseStream から文字列を読む
    using (var sr = new StreamReader(getObjectResponse.ResponseStream))
    {
        var content = await sr.ReadToEndAsync();
        Console.WriteLine(content);
    }

    // 次のようにファイルに保存することもできる。
    // getObjectResponse.WriteResponseStreamToFile(@"C:\Temp\test");
    // 
    // ※StreamReader と WriteResponseStreamToFile を同時に使うことはできない。
}
```

## DeleteObject / DeleteBucket

上で作ったオブジェクトやバケットはいらないので削除する。

```csharp
async Task Main()
{
    var s3 = new Amazon.S3.AmazonS3Client();

    var deleteObjRes = await s3.DeleteObjectAsync(new DeleteObjectRequest()
    {
        BucketName = "my-favorit-baketto",
        Key = "test",
    });
    
    var deleteBucketRes = await s3.DeleteBucketAsync("my-favorit-baketto");
}
```

と、ここまでに使用したAPIの名前は {List/Get/Put/Delete}+{Bucket/Object} という規則になっている。これは他のAWSサービスのAPIでも同様の規則となっているので覚えておくとよい。

# まとめ

S3 を操作するコードを LINQPad で作成した。また、作成を通して AWS SDK for .NET の使う上で押さえておきたいポイントも書いた。これらのポイントは S3 に限らず、ほかのAWSサービスでも共通。

1. APIを使うまでの流れ：NuGetパッケージ入れる ⇒ クライアント作る ⇒ API呼ぶ
1. NuGetパッケージは AWSSDK.{サービス名} で提供されている
2. APIの使用に必要なクラスは主に3種類
    - Amazon.{サービス名} 名前空間
        - Amazon{サービス名}Client
    - Amazon.{サービス名}.Model 名前空間
        - {API名}Requset
        - {API名}Response
3. APIの名前は次のような動詞＋対象名になっていることが多い。正確に覚えてなくても入力補完を頼れば使える。
    - List
    - Get
    - Put
    - Delete
    - Describe 
4. .NET Core 版 AWSSDK には非同期 API しか用意されていない
5. 困ったら Dump() してみる