January 2018

Volume 33 Number 1

データ ポイント - Cosmos DB とやり取りする Azure Functions の作成

Julie Lerman

Julie Lerman前回は、CookieBinge というシンプルなユニバーサル Windows プラットフォーム (UWP) アプリ をビルドする方法を紹介しました。前回の主眼は、デバイス限定の Windows 10 アプリで Entity Framework Core 2.0 (EF Core) を使用する方法を模索する理由を説明することでした。SQLite などの移植可能なデータベースのプロバイダーが追加されたことで、これが実現します。

現時点の CookieBinge アプリは、ゲームをプレイするデバイスのローカルにゲーム スコアを保存しています。その場合、EF Core 2.0 を使用して、データを SQLite データベースに永続化します。前回、このゲームを進化させる次の段階として、ユーザーがインターネットで他のゲーム プレイヤーとデータを共有できるようにするとお知らせしました。この目標を実現するため、Azure の便利な 2 つの機能を利用する予定です。それは Azure Cosmos DB と Azure Functions です。

Azure Cosmos DB の概要

Azure Cosmos DB は、当初 Azure DocumentDB としてリリースされたテクノロジの次世代 DB です。Azure DocumentDB については、何度もこのコラムで取り上げています。2015 年 6 月にコラム「Microsoft Azure DocumentDB の概要」(msdn.com/magazine/mt147238) を書いた後、さらに 2 回のコラムで、Node.js サーバー API を使用し、Azure DocumentDB を Aurelia Web のバックエンドとして使用しました。

DocumentDB は、いくつかのすばらしい機能が追加され、グローバル分散データベースに進化しています。DocumentDB は、グローバル分散が容易なことに加えて、一貫性モデルが再調整されており、「厳密」や「最終的」以外の選択肢も用意されています。この 2 つの両極端の間の選択肢として、「有界整合性制約」、「セッション」、または「一貫性のあるプレフィックス」も選択できるようになっています。データ保存をサポートする重要な機能は他にも多数あります。また、DocumentDB では、MongoDB API 経由でアクセスできるドキュメント、グラフ データベース、テーブル (キー/値のペア)、列データ ストレージなど、他のさまざまなデータ モデルがサポートされるようになりました。そして、これらのモデルはすべて、Cosmos DB の下にまとめられています。実際、Azure DocumentDB を使用していたら、自動的に Azure Cosmos DB に切り替わり、既存のドキュメント データベースで Cosmos DB の新機能すべてのメリットを利用するようになります。Cosmos DB の詳細については、まず cosmosdb.com をご覧ください。

Azure Functions の概要

CookieBinge のスコアの保存には Cosmos DB を使用する予定です。しかし、コードすべてを自分で Document DB API を使用して作成するのではなく、もう 1 つの比較的新しい Azure の機能である Azure Functions を利用します。Azure Functions は、マイクロソフトの「サーバーレス コンピューティング」サービスです。「サーバーレス」という表現には常に懐疑的になります。というのも、自分のサーバーではないだけで、どこかのサーバーでコンピューティング処理していることに変わりはないからです。しかし、Azure Functions を利用する機会を得たことで、サーバーレスという概念についに敬意を抱くようになりました。Azure Functions を利用すると、デプロイ、API のサポート、他の機能 (データ保存、電子メール送信など) への接続といった分野横断的な懸念事項が自動的に処理されるため、開発者はアプリで実行する実際のロジックに専念できます。このメリットについては実際に利用するまで理解していませんでした。この後説明するように CookieBinge アプリにこの機能を準備すると、皆さんにも同じようにご理解いただけると思います。

最初の Azure Functions をビルドする準備

Azure Functions をビルドする方法は 3 つあります。1 つは Visual Studio 2017 のツールを使用する方法、2 つ目は Azure Portal で直接ビルドする方法、もう 1 つは Visual Studio Code と Azure コマンドライン インターフェイス (CLI) を組み合わせて使用する方法です。今回は、Portal を使用する方法から学習を始めることにしました。Portal には、必要な手順すべてのチュートリアルが用意されています。また、Visual Studio 2017 ツールのサポートを利用しないので、不確定要素について少し真剣に検討せざるをえないのも Portal のメリットです。Portal を使用したことで、理解を深めることができたと思います。もちろん、Visual Studio 2017 で同じ作業をする方法についても、すばらしいリソースが多数用意されています。しかし、Portal の利用をお勧めするもう 1 つの理由は Web ベースで利用できるクロスプラットフォーム ソリューションであることです。ただし、関数コードやアセットはソース管理から Azure にデプロイできますが、Portal で直接作成したものについては、PC にダウンロードして (簡単な作業です)、そこからリポジトリにプッシュする必要があります。

この手順に従って作業を進める場合、Azure サブスクリプションをまだお持ちでなければ、無料のサブスクリプションを入手できます。このサブスクリプションは、短期間の試用版ではありません。いくつかの Azure 製品は 1 年間無料で利用できます。また、数十の Azure 製品は常時無料で利用できます。azure.com/free にアクセスすると、無料の Azure アカウントをセットアップできます。今回は、Visual Studio サブスクリプションの一部として取得したアカウントを利用しています。このアカウントには、Azure を試すことができるクレジットが毎月提供されます。

関数を作成する前に、目標を決める必要があります。アプリに実装する機能は次のとおりです。

  • ユーザーのスコアを Web 上に保存する。スコアと一緒にユーザーの一部の情報と日付を保存するだけでなく、ゲームをプレイしたデバイスの種類も保存する。
  • ユーザーが、プレイに使用するすべてのデバイスから自分の最高スコアを取得できるようにする。
  • ユーザーが、すべてのユーザーの最高スコアを取得できるようにする。

ここでは、アカウントの作成や認証といった問題については取り上げません。しかし、実際問題としてはもちろん、アカウントの作成と認証を行う必要があります。今回の目的は、Azure Functions について説明すること、ひいては、UWP アプリからの対話について説明することです。

Azure Portal で Azure Functions を作成する

Azure Functions は、Function App にグループ化されたサービスで、設定を定義して一連の関数で共有できます。そのため、まずは新しい Function App を作成します。Azure Portal で、[新規] をクリックして、"Function App" でフィルター処理すると、このオプションが簡単に見つかります。検索結果の一覧で [Function App] をクリックし、[作成] ページに移動します。ここで、アプリの名前など、いくつかメタデータを入力するよう求められます。ここでは、アプリの名前を「cookiebinge.azurewebsites.net」とします。これは単なるデモなので、他は [作成] ページの既定値を受け入れます。この新しい Function App に後で簡単にアクセスできるよう、[ダッシュボードにピン留めする] オプションをオンにしてから、[作成] をクリックします。約 30 秒で、新しい Function App のデプロイが完了します。

次に、この Function App にいくつか関数を追加します。先ほど述べた目標一覧を実現するための関数をビルドしていきます。Azure Functions サービスには、HTTP 要求イベント、Cosmos DB データベースへの変更イベント、BLOB やキューでのイベントなど、応答できる一連のイベントが豊富に事前定義されています。これらの関数を UWP アプリから呼び出す必要があるため、HTTP 経由の要求に関数が応答するようにする必要があります。Portal には多数のテンプレートが用意されており、さまざまな言語に対応しています (Bash、Batch、C#、F#、JavaScript、PHP、PowerShell、Python、TypeScript)。今回は、C# を使用します。

Function App 内で初めての関数を作成する場合は、[関数] ヘッダーの隣のプラス記号をクリックします。事前定義された関数を作成するボタンが表示されますが、そのボタンの下にスクロールすると、カスタム関数を作成するリンクがあります。このオプションを選択すると、図 1 に示すようなテンプレート オプションがスクロール可能なグリッドに表示されます。一覧の先頭に表示される [HTTP Trigger – C#] を選択します。

新しいカスタム Azure Functions を作成するためのテンプレート一覧の一部
図 1 新しいカスタム Azure Functions を作成するためのテンプレート一覧の一部

関数に名前を付けて、[作成] をクリックします。今回は、「StoreScores」という名前にします。

Portal には、関数の構造がわかるように、いくらか既定のコードを含んだ関数が作成されます。関数は、run.csx というファイルに作成されます (図 2 参照)。サポート ファイルに追加のロジックを含めることもできますが、初めての作業で行うには必要以上に高度な作業です。

新しい HTTPTrigger の既定の関数ロジック
図 2 新しい HTTPTrigger の既定の関数ロジック

この例に含まれるメソッドは、Run という名前の 1 つだけです。Azure では、この関数への HTTP 要求への応答として、このメソッドを呼び出します。メソッドには、要求をキャプチャするパラメーターと、情報をログに中継するパラメーターが 1 つあります。

このサンプルでは、関数が名前を表す受信データを探していること、および関数が柔軟にクエリ パラメーター内と要求本文内を検索できることがわかります。名前が見つからない場合、関数はわかりやすいエラーメッセージを含む Http­ResponseMessage を返します。見つかった場合は、応答で "Hello [name]" を返します。

Cosmos DB を操作するよう関数をカスタマイズする

この関数の目的は、受信データを Cosmos DB データベースに保存することです。ここからがマジックです。保存タスクのために、接続やコマンドなどのコードを作成する必要はありません。Azure Functions には、他の多くの Azure 製品と簡単に統合できる機能があります。Cosmos DB も統合対象の 1 つです。

関数一覧には、新しい関数と、その下に 3 つの項目が表示されています。その項目のうちの 1 つが [統合] です。[統合] を選択すると、図 3 に部分的に示したフォームが表示されます。トリガーが HTTP 要求であり、出力として HTTP を通じて何かを返すことが示されているのがわかります。成功か失敗のメッセージを返す必要があるので、この HTTP 出力はそのままにしておきます。しかし、Cosmos DB コレクションを出力先とする出力は追加します。

関数の統合ポイントを定義する
図 3 関数の統合ポイントを定義する

これを行うには、[新しい出力] をクリックします。クリックすると、アイコンの一覧が表示されます。[Azure Cosmos DB] というアイコンまで下へスクロールして、そのアイコンを選択します。その後は、ページをさらに下へスクロールして、[選択] を表示します。何をすればよいかはおわかりですね (そのボタンをクリックします!)。

統合の設定画面には、既定値が入力されています。ドキュメント パラメーター名は、run.csx で使用するパラメーターを表します。これは、既定の名前の「outputDocument」のままにします。その次に表示されているのは、Cosmos DB データベースの名前とそのデータベース内のコレクションの名前、それから、データベースが存在する Cosmos DB アカウントへの接続です。自動的にデータベースを作成するためのチェックボックスも表示されます。これまでいくつか Cosmos DB アカウントを作成しているので、そのうちの 1 つを使用します。ただし、Binges というコレクションを含む CookieBinge という新しいデータベースの作成は、関数を通じて行います。図 4 に、入力済みのこのフォームを示します (出力定義を保存する直前の状態です)。データベースとコレクションを作成するチェックボックスをオンにしたので、それらが自動的に作成されますが、作成タイミングはこの出力を保存したときではありません。関数がデータをデータベースに初めて保存するときにデータベースが存在していなければ、その場でデータベースが作成されます。

関数の出力として Cosmos DB を定義する
図 4 関数の出力として Cosmos DB を定義する

Run.csx をカスタマイズする

次は、関数コードを再定義します。新しいバージョンの関数では、次の BingeRequest クラスに対応する JSON オブジェクトが渡されることを想定しています。このクラスは、run.csx ファイルの Run メソッドの下に追加しました。

public class BingeRequest{
  public string userId {get;set;}
  public string userName {get;set;}
  public string deviceName {get;set;}
  public DateTime dateTime {get;set;}
  public int score{get;set;}
  public bool worthit {get;set;}
}

しかし、これは保存するデータの構造とは異なります。もう 1 つのプロパティ (データがデータベースに記録された日付と時刻) をキャプチャしたいためです。これを実現するために、BingeDocument というもう 1 つのクラスを使用します。このクラスは、BingeRequest を継承するもので、そのプロパティをすべて継承しており、さらに logged というプロパティが加えられています。コンストラクターは、入力された BingeRequest を受け取り、logged の値を設定した後、BingeRequest の値を独自のプロパティに転送します。

public class BingeDocument:BingeRequest
  {     
    public BingeDocument(BingeRequest binge){
    logged=System.DateTime.Now;
    userId=binge.userId;
    userName=binge.userName;
    deviceName=binge.deviceName;
    dateTime=binge.dateTime;
    score=binge.score;
    }
    public DateTime logged{get;set;}
  }

これらの型の準備ができたら、Run メソッドで利用できます。図 5 に、先述の BingeRequest クラスと BingeDocument クラスのプレースホルダーを含む run.csx の変更後のリストを示します。

新しい Run メソッドを詳しく見てみましょう。このシグネチャは元のシグネチャと同様に要求と TraceWriter を受け取りますが、さらに outputDocument という非同期出力パラメーターも受け取ります。この出力パラメーターの結果は、定義した Cosmos DB 出力にプッシュされます。この名前は、図 4 の出力構成の出力パラメーター名に対応しているのがわかります。TraceWriter は、コード ウィンドウの下のログ ウィンドウにメッセージを出力します。このメッセージは最終的には削除しますが、メッセージを利用して、かつてのように IDE なしでデバッグできます。でも、誤解しないでください。作業している言語の解析にはコード ウィンドウがとても役立ちます。保存の際には、デバッグ ウィンドウにもコンパイル エラーが非常に詳細に出力されます。また、左かっこを入力したら右かっこを挿入するという処理も自動で行われます。実のところ、膨大な数のエディター機能が搭載されています。たとえば、エディター ウィンドウを右クリックすると、利用可能なエディター機能が多数表示されます。

図 5 スコアをキャプチャして出力先の Cosmos DB に保存する新しい run.csx ファイル

using System.Net;
public static async Task<HttpResponseMessage> Run(HttpRequestMessage req,
  TraceWriter log,    IAsyncCollector<object> outputDocument)
{
  BingeRequest bingeData =  await req.Content.ReadAsAsync<BingeRequest>();
  log.Verbose("Incoming userId:" + bingeData.userId);
  var doc=new BingeDocument(bingeData,log);
  log.Verbose("Outgoing userId:" + doc.userId);
  await outputDocument.AddAsync(doc);
  if (doc.userId !=" " ){
    return req.CreateResponse(HttpStatusCode.OK,$"{doc.userId} was created" );
  }
  else {
    return req.CreateResponse(HttpStatusCode.BadRequest,
      $"The request was incorrectly formatted." );
  }
}
public class BingeRequest{ . . . }
public class BingeDocument { . . . }

Run メソッドのコードの 1 行目は、要求を非同期に読み取り、結果を BingeRequest オブジェクトに変換します。

次に、要求から作成したばかりのオブジェクトを渡して、新しい BingeDocument のインスタンスを作成します。これで、完全に設定された BingeDocument オブジェクトが作成されます。logged プロパティも設定されています。

その後、TraceWriter を使用して、要求に含まれるいくつかのデータがログに表示されるようにします。このようにすると、BingeDocument が本当に要求オブジェクトからデータを取得したかどうかをデバッグ中に確認できます。

最後に、作成した BingeDocument を非同期 outputDocument に非同期に追加します。

outputDocument オブジェクトの結果は、関数によって Cosmos DB に送信される内容です。これは、JSON として DocumentDB に保存されます。関数では、バックグラウンドでこれを再度変換します。統合設定を使用してすべてを関連付けたので、この処理を行うコードを記述する必要はありません。

いろいろ説明しましたが、最終的に、HttpResponse を通じてメッセージを返して、関数の成功または失敗を伝えます。

関数をコンパイルしてテストする

関数を保存すると、関数がコンパイルされます。これからコード内の重要な箇所を無作為に削除して、コンパイラの動作をお見せします。結果は、コード ウィンドウの下のログ ウィンドウに表示されます。図 6 に、コンパイラ出力を示します。13 行目の左かっこを削除したことで生じたエラーがはっきりと示されています。IDE の IntelliSense などのコーディング ツールの助けなしで作成しなければならなかったコードの問題を解決するときは、デバッガーがなくても、このエラーを確認すると役立つことがわかります。また、それと同時に、日常的にコーディング ツールにどれほど依存しているかも知ることができます。

エラーを含むコンパイラ情報が表示されたログ ウィンドウ
図 6 エラーを含むコンパイラ情報が表示されたログ ウィンドウ

コードを修正してコンパイル エラーがなくなったら、ログには "再読み込み中" のメッセージの後に "コンパイルに成功しました" と表示されます。

それでは、関数をテストしましょう。関数は、コーディングしていたのと同じウィンドウでテストできます。コード エディターの右側に、2 つのタブ付きウィンドウがあります。一方のタブには、関数に関連するファイルのリストが表示されます。既定で表示されるファイルは、現在説明している run.csx ファイルと、UI で定義した設定をすべて含む function.json ファイルの 2 つだけです。  もう一方のタブで、テストを実行します。この組み込みのテスト UI は、小型の Postman や Fiddler アプリケーションに似ています。テストする関数が既にわかっているので、わずかな手間で HTTP 要求を作成できます。開発者が行う必要があるのは、受信要求を表す JSON オブジェクトを挿入することだけです。テスト UI では既定で HTTP Post を送信するため、このテスト用に HTTP メソッドを変更する必要もありません。次の JSON を要求本文テキスト ボックスに入力します。スキーマは重要ですが、好きな値を何でも使用できます。

{
  "userId": "54321",
  "userName": "Julie",
  "deviceName" : "XBox",
  "dateTime": "2017-10-25 15:26:00",
  "score" : "5",
  "worthit" : "true",
  "logged": ""
}

次に、[テスト] ウィンドウの [実行] をクリックします。テストでは、関数が呼び出されて、要求本文が渡され、HTTP 結果が出力ウィンドウに表示されます。図 7 には、“54321 was created” (54321 が作成されました) という出力が表示されています。また、[ログ] ウィンドウには、関数のログ出力が表示されています。

関数でテストを実行した後のテスト ウィンドウ
図 7 関数でテストを実行した後のテスト ウィンドウ

Cosmos DB データベースの新しいデータを確認する

この時点では、この最初のテストが成功した結果として、CookieBinge Cosmos DB データベースが作成され、そのデータベースの中に、このドキュメントの保存先である Binge コレクションが作成されたことを確認できません。今回のまとめに入る前に、データベースとコレクションが作成されたことを確認する方法について説明しましょう。

それにはまず、Portal でこのデータベースを作成した Cosmos DB アカウントを開きます。データベース名は datapointscosmosdb です。そのため、[すべてのリソース] に移動して、フィルターにデータ ポイントを入力してデータベースを探します。アカウントを開くと、アカウントに含まれるすべてのコレクションとデータベースを確認できます。今回使用したアカウントには CookieBinge データベースに Binges コレクションがあるだけです (図 8 参照)。これが、関数によって作成されたデータベースとコレクションです。

datapointscosmosdb アカウントに表示された Binges コレクション
図 8 datapointscosmosdb アカウントに表示された Binges コレクション

Binges をクリックして、このコレクションのデータ エクスプローラーを開きます。テストを 2 回実行したので、このコレクションには 2 つのドキュメントが保存されています (図 9 参照)。このドキュメントの最初の 7 つのプロパティは、今回定義したプロパティです。残りのプロパティは、インデックス付け、検索、分割などのタスクを処理するために Cosmos DB と関連 API で使用するメタデータです。

Cosmos DB に保存されたドキュメントを Portal で確認する
図 9 Cosmos DB に保存されたドキュメントを Portal で確認する

今後の見通し

図 7 をもう一度見てみると、コード ウィンドウの上に [関数の URL の取得] リンクがあるのがわかります。この URL は、データをクラウドに送信するために CookieBinge アプリで使用します。

今回は、関数を作成して、Cosmos DB に接続する方法を説明しました。次回のコラムでは、さらに 2 つの関数を作成して、データのさまざまなビューを取得する方法を取り上げます。この連載の最終回では、CookieBinge アプリから関数を呼び出して、関数の結果を表示する方法を説明します。


Julie Lerman は、バーモント ヒルズ在住の Microsoft Regional Director、Microsoft MVP、ソフトウェア チームの指導者、およびコンサルタントです。世界中のユーザー グループやカンファレンスで、データ アクセスなどのトピックについてプレゼンテーションを行っています。彼女のブログは thedatafarm.com/blog (英語) で、彼女は O'Reilly Media から出版されている『Programming Entity Framework』(2010 年) および『Code First』版 (2011 年)、『DbContext』版 (2012 年) を執筆しています。彼女の Twitter (@julielerman、英語) をフォローして、juliel.me/PS-Videos (英語) で彼女の Pluralsight コースをご覧ください。

この記事のレビューに協力してくれた技術スタッフの Jeff Hollan に心より感謝いたします。


この記事について MSDN マガジン フォーラムで議論する