BEACHSIDE BLOG

Azure と GitHub と C# が好きなエンジニアの個人メモ ( ・ㅂ・)و ̑̑

AWS Lambda を .Net Core と ( .NET Standard と) C# で実装する(その2)

前回から引き続き、 AWS Lambda を C# で、.NET Core 1.0、.NET Standard 1.6 を使って実装する際のメモです。 Autofac 使ってDIをする話がメインです。

(2017/9月時点での話=.NET Core 1.0しかサポートしてない時点です)

Overview

 1 開発環境の準備 (その1)
 2 .NET Core 1.0 対応の .NET Standard 1.6 のクラスライブラリの作成(その1)
 3 簡易なクラスライブラリー実装(その1)
 4 Console プロジェクトの作成 (←今回ココから)
 5 Autofac の実装
 6 AWS Labmda プロジェクトの作成
 7 AWS Labmda の環境変数を読み込む
 8 AWS API Gateway から Lambda - プロキシ統合 の使用とか
 9 AWS Lambda から AWS Lambda の呼び出し
 10 AWS Lambda から CloudWatch Events の呼び出し

4 Console プロジェクトの作成

デバッグでの動作確認用に、.NET Core のコンソールアプリを作ってしまいます。ソリューションエクスプローラーで、今回のソリューションを右クリック > 追加 > 新しいプロジェクト をクリックします。

左ペインで「.NET Core」を選択して、「コンソールアプリ(.NET Core)」を選択しましょう(.NET Standardではなですよ)。プロジェクトの名前は、「LambdaDemo.ConsoleApp」にしました。

f:id:beachside:20170928202941p:plain


プロジェクトのターゲットフレームワークを変更します。ソリューションエクスプローラーで今作った「LambdaDemo.ConsoleApp」を右クリック > プロパティ をクリックします。

表示された画面の左ペインの アプリケーション で、ターゲットフレームワークを「.NET Core 1.0」にします。

f:id:beachside:20170928202952p:plain


そのままの画面で、左ペインの ビルド をクリックし、一番下にある 詳細設定 をクリックします。

f:id:beachside:20170928203001p:plain


言語バージョンを「C#の最新のマイナーバージョン(最新)」に変更します。

f:id:beachside:20170928203038p:plain


文字化け対策

日本語はデフォルトだと文字化けするので拡張を入れておきましょう。

VS2017 の上部メニュー ツール > NuGet パッケージ マネージャー > ソリューションの Nuget パッケージ の管理 をクリックします。

(右上のクイック起動から「nu」と入力して起動したほうが早いですが...)

参照をクリックして、検索で「System.Text.Encoding.CodePages」を入力すると、そのパッケージが出てきます。ConsoleAppのプロジェクトのみにインストールします。

f:id:beachside:20170928203219p:plain


このプロジェクトのエントリーポイント、Program.cs のMain メソッドの最初に、以下のコードを書いておきましょう。

 Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);

async な Main メソッドへ変更

LambdaDemo.ConsoleApp」プロジェクトの Program.cs を開きましょう。言語バージョンを上げたので、Main メソッドを async 化できます。C#7.1 からの使える async Task Main Method です(あまり本編の内容とは関係ないのですがね...)。

元々のシグネチャ(以下)を、

static void Main(string[] args)

下のように変更しておきましょう。あー幸せ。

static async Task Main(string[] args)

(Taskを使ってるので、using System.Threading.Tasks; も必要になります。)

(独り言ですが...結局以前やってたような MainAsyncメソッド作って Main メソッドから...GetAwaiter().GetResult() する手間とさ程変わりませんが...それでもこの機能は好きです)

スタートアップ プロジェクトに設定

ソリューションエクスプローラーで「LambdaDemo.ConsoleApp」プロジェクトを右クリック > スタートアップ プロジェクトに設定 をクリックします。これでデバッグ時はデフォルトでこのプロジェクトがデバッグされるようになりました。

5 Autofac の実装

そもそもIoCだったりDIとは?みたいなことは、ひとことでさらっと言って理解できるものではないと思うので省略。Nuitsさんの ここら辺とかいい勉強になると思います。

AWS Lambda 観点でいうと、環境変数を利用することができるので、最初からその想定でものを作っておいた方がよい(後で変更とか死ぬほどめんどい:個人的主観)。

環境変数の取得は、テストプロジェクトや動作確認用のConsole アプリではjsonファイルから呼び出すようにしておき、Lambda にデプロイしたときには、環境変数を取得するよう実装していきます。

Autofac 用のプロジェクト作成

ソリューションエクスプローラーで、今回のソリューションを右クリック > 追加 > 新しいプロジェクト をクリックします。

f:id:beachside:20170928203245p:plain


.NET Standard のクラスライブラリを作ります。プロジェクトの名前は、「LambdaDemo.IoC」にしました。

f:id:beachside:20170928203257p:plain


.NET Standard1.6.0対応をします。やり方は前回同様で、 「ターゲットフレームワークの変更」と「csprojの編集」の2点です。

.NET Core 1.0 対応の .NET Standard 1.6 のクラスライブラリの作成

これを忘れると動かないのでご注意を。

NuGet で拡張をインストール

VS2017 の上部メニュー ツール > NuGet パッケージ マネージャー > ソリューションの Nuget パッケージ の管理 をクリックします。

(右上のクイック起動から「nu」と入力して起動したほうが早いですが...)

f:id:beachside:20170928203308p:plain


Autofac をインストールします。参照 をクリックし、検索で「autofac」と入れます。Autofacの最新版(今回だとv4.6.1)を、先程作った「LambdaDemo.IoC」プロジェクトだけにインストールします。

f:id:beachside:20170928203317p:plain


次は、環境変数の読み込みに必要な拡張を3つインストールします。

検索で「Microsoft.Extensions.Configuration」と入力すると出てきます。注意点として、.NET Standard1.6.0対応のバージョンは 3つとも v1.0.2 です(それぞれのライブラリの依存関係を見ると確認できます)。

3つとも、最新版ではなくバージョンを v1.0.2 にしてインストールしましょう。

f:id:beachside:20170928203329p:plain


プロジェクトの参照を追加

ソリューションエクスプローラーで「LambdaDemo.IoC」プロジェクトを右クリック > 追加 > 参照 をクリックします。

f:id:beachside:20170928203355p:plain


左ペインでプロジェクトをクリックし、「LambdaDemo.DemoService」を追加しましょう。

f:id:beachside:20170928203529p:plain


Container クラスの作成

LambdaDemo.IoC」プロジェクトを右クリックし、追加 >クラス をクリックします。クラス名は、「DemoContainer」としました。名前、安易につけると色んなライブラリと被って(壊れはしないけど)リーダビリティが落ちる場合があるので、意外に気を付けてます(今回はテキトーですが)

実装は、まずこんな感じで....

Autofac の基本的なところしか書いてないのざっくりとしか説明しませんが、DemoContainerクラスのstaticなコンストラクターで必要な情報を登録して、外からは34行目の Resolve<T> メソッドを呼び出せばOKって流れです。 環境変数絡みのところは、19行目あたりは別の回で説明します。

コンテナーへの登録

48行目あたりの BuildContainer メソッドを以下のように書き換えて、前回作成した SampleClass を登録しましょう。

13行目~15行目を追加しています。SampleClass を実装されているインターフェース(ISampleClass)として登録、インスタンスのスコープは、InstancePerLifetimeScope に設定という感じです。

スコープは、とりあえず InstancePerLifetimeScope にしていますが、必要に応じて適切なスコープをせっていしましょう。詳しくは本家のドキュメントに。

Controlling Scope and Lifetime — Autofac 4.0 documentation

ConsoleApp で動作確認

「LambdaDemo.ConsoleApp」プロジェクトの Program.cs を開いて以下のようにコーディングします。

2通りの呼び出し方をサンプルとして書いてますが、プロダクションでは用途に応じでって感じですね。

F5を押してデバッグ実行してみると、正しく動いてることが確認できます。

f:id:beachside:20170928203619p:plain

6 AWS Labmda プロジェクトの作成

いい加減、Lambda で動かしてしましょうか...という頃合いです。

プロジェクトの作成

ソリューションエクスプローラーで、今回のソリューションを右クリック > 追加 > 新しいプロジェクト をクリックします。

左ペインで「AWS Lambda」をクリック > 「AWS Lambda Project(.NET Core)」を選択、プロジェクトの名前は、「LambdaDemo.Lambda」にしました。

f:id:beachside:20170928203632p:plain

AWS の「AWS Tookkit for Visual Studio 2017」が入ってない可能性があります。ここら辺を確認してみましょう。


AWS の「Select Blueprint」が表示されます。 「Empty Function」を選んで、Finish ボタンをクリックします。

f:id:beachside:20170928203644p:plain


プロジェクトの参照追加

ソリューションエクスプローラーで「LambdaDemo.Lambda」プロジェクトを右クリック > 追加 > 参照 をクリックします。 追加するのは、「LambdaDemo.IoC」プロジェクトだけでOKです。

FunctionHandler メソッドのコーディング

AWS Lambda で入力パラメーターを受け取る場合、Lambda側でJSONをデシリアライズしてプログラムに渡してくれるってのがありますし、API Gateway 経由の場合、プロキシ統合の機能もあるので、それに応じてパラメーターの受け取り方を考える必要があります。

今回はシンプルに以下のJSONが入力される想定で実装します。

{
  "key1": "value1"
}

LambdaDemo.Lambda」プロジェクトの「Function.cs」を開いて以下のように実装します。

まず、FunctionHandler メソッドは、async にします。今回は戻り値を返さないので、Task を定義しています。

ちなみに、async のサポートは、本家のドキュメントで書かれています。

また、入力のパラメーター用に、Param クラスを21行目で乱暴に定義してます。

AWS Lambda からこのプログラムに入力パラメーターが入ってくるとき、LambdaがJSONをデシリアライズしてメソッドの引数 param にセットしてくれます。シリアライザーは、SDKの内部でJson.netを使ってるので、json のキャメルケースを .NET のクラスのパスカルケースにデシリアライズしてくれます。

15行目~16行目がビジネスロジックを呼び出すところとなりますが、これは ConsoleApp のプロジェクトでの呼び方と全く同じです。

デプロイ!の前に...

Lambda を作成するにあたっては、AWSのアカウント作ったりロール作ったりがあります。そこはまだのかたは本家ドキュメントを参考に...

ご利用開始にあたって - AWS Lambda

 

さらに、VS2017の AWS Explorer でアカウントを設定しておきましょう。

AWS Toolkit for Visual Studio をセットアップする - AWS Toolkit for Visual Studio

 

デプロイ!

まず、「LambdaDemo.Lambda」プロジェクトを右クリックしてビルドしておきましょう。

AWS Toolkit の動きが怪しくて、保存済みのファイルだとビルドしてデプロイされ、保存してないとビルドされないって動作がありますよね?)

LambdaDemo.Lambda」プロジェクトを右クリック > publish to AWS Lambda... をクリックします。

f:id:beachside:20170928203657p:plain


デプロイのための設定画面が表示されます。アカウントを選択し(前述の作業をしていれば表示されます)、Regionも作りたいリージョンを選びましょう(私は東京を選んでます)。 FunctionNameは、既存の Lambda を選ぶこともできますし、新規に入力すると、新しく Lambda を作成してくれます。今回は、「lambdaDemo1」という 関数名で作ります。その他の項目も確認して、Next をクリックします。

f:id:beachside:20170928203709p:plain


後は、Role Nameを指定します。この画面で、Memoryの割り当てやTimeout時間、環境変数を設定することもできます。

f:id:beachside:20170928203718p:plain


Upload をクリックするとデプロイが開始します。

デプロイ時、わけわからないエラーがでることが(私の場合、仕事の時に)多々ありましたが、プログラムはおかしくない前提で話すと、なんどかデプロイ連打すれば正常にデプロイできました。

Web から動作確認

デプロイが完了すると、VS2017からでもテスト実行できる画面が表示されて便利です。

が、今回は、AWS のマネジメントコンソールから動作確認してみましょう。今作った関数「lambdaDemo1」を開きましょう。関数名「lambdaDemo1」の画面を適当に開いて、 アクション > テストイベントの設定 をクリックします。

f:id:beachside:20170928203729p:plain


Hello World」のテンプレートがデフォルトで表示されます。想定したJSONのフォーマットの key1 があって丁度良いので、このまま 保存してテスト をクリックしましょう。

f:id:beachside:20170928203740p:plain


正常に動作すると、こんな画面になります。ログをクリックしてみましょう。

f:id:beachside:20170928204108p:plain


ログストリームの一覧が出てきますので、今実行したらしきログを開いてみると....

f:id:beachside:20170928204054p:plain


無事に動作してます。

今回はここまでで、次回に続きますー♪