Alternative Architecture DOJO

オルターブースのクラウドネイティブ特化型ブログです。

Azure Functions の Azure SQL バインディングを試した(出力編)

MLBお兄さんこと松村です。
この記事はオルターブース Advent Calendar 2021の5日目の記事です。
adventar.org

前回の記事に引き続き、今回も Azure Functions の Azure SQL バインディングを試した内容になります。前回の記事はこちらから。
aadojo.alterbooth.com

前回は入力バインディングを試したので、今回は出力バインディングを試してみます。
Azure SQL の出力バインディングは、データの保存を行うことができる機能になっています。
docs.microsoft.com

Azure SQL の出力バインディングは他の出力バインディングと同様に、引数にオブジェクトをセットすることで関数終了時にデータの保存が行われます。
単一オブジェクトの引数なら 1件のデータの保存となります。
ICollector<T> または IAsyncCollector<T> の引数なら複数件のデータの保存となります。

バインド定義

入力バインディングと同様に Microsoft.Azure.WebJobs.SqlAttribute の属性付きの引数を用意します。

1件のレコードを保存する場合

public IActionResult Run(
    [HttpTrigger(AuthorizationLevel.Function, "get", Route = null)] HttpRequest req,
    [Sql("dbo.ToDo", ConnectionStringSetting = "SqlConnectionString")] out ToDoItem newItem)

複数件のレコードを保存する場合(同期)

public IActionResult Run(
    [HttpTrigger(AuthorizationLevel.Function, "get", Route = null)] HttpRequest req,
    [Sql("dbo.ToDo", ConnectionStringSetting = "SqlConnectionString")] ICollector<ToDoItem> newItems)

複数件のレコードを保存する場合(非同期)

public async Task<IActionResult> Run(
    [HttpTrigger(AuthorizationLevel.Function, "get", Route = null)] HttpRequest req,
    [Sql("dbo.ToDo", ConnectionStringSetting = "SqlConnectionString")] IAsyncCollector<ToDoItem> newItems)

なお、既知の問題がいくつかあるようです。 Azure SQL バインディング自体がプレビューということもあり、用法用量を守って使いましょう。
https://docs.microsoft.com/ja-jp/azure/azure-functions/functions-bindings-azure-sql#known-issues


書き方は単純なのでサンプルコードを真似て書けばいいですが、1つ気になることがあります。

レコードの更新はできるのか?

2つのパターンを試して確認してみます。

  1. 既存の主キーをもつレコードを出力バインドで保存する
  2. 入力バインドで取得したレコードを出力バインドで保存する

1. 既存の主キーをもつレコードを出力バインドで保存する

まず既存のレコードを確認します。(主キーは Id 列です)

同じ主キーの値でレコードを出力バインドで保存します。

[FunctionName("WriteOneRecord")]
public static IActionResult Run(
    [HttpTrigger(AuthorizationLevel.Function, "get", Route = "addtodo")] HttpRequest req,
    [Sql("dbo.ToDo", ConnectionStringSetting = "SqlConnectionString")] out ToDoItem newItem)
{
    newItem = new ToDoItem
    {
        Id = 1,
        Priority = 200,
        Description = "new-description1"
    };

    return new CreatedResult($"/api/addtodo", newItem);
}

この関数を実行すると、主キーが該当する既存レコードが更新されました。つまり SQL の UPDATE クエリが実行されたことになります。

2. 入力バインドで取得したレコードを出力バインドで保存する

前回の記事で紹介した入力バインドで取得したレコードを変更し、出力バインドで保存してみます。

[FunctionName("UpdateOneRecord")]
public static IActionResult Run(
    [HttpTrigger(AuthorizationLevel.Function, "get", Route = "updatetodo/{id}")] HttpRequest req,
    [Sql("select * from dbo.ToDo where Id = @Id",
        CommandType = System.Data.CommandType.Text,
        Parameters = "@Id={id}",
        ConnectionStringSetting = "SqlConnectionString")] IEnumerable<ToDoItem> items,
    [Sql("dbo.ToDo", ConnectionStringSetting = "SqlConnectionString")] out ToDoItem newItem)
{
    var item = items.First();
    newItem = item;
    newItem.Priority = 300;
    newItem.Description = "update-description1";

    return new NoContentResult();
}

この関数を実行すると SELECT クエリで取得したレコードを更新できることが分かります。

  • 出力バインドで保存するレコードが Azure SQL に存在しない場合は INSERT が実行される
  • 出力バインドで保存するレコードが Azure SQL に存在する場合は UPDATE が実行される

つまり UPSERT の挙動になるようです。


2回に分けて Azure Functions の Azure SQL バインディングを試してみました。プレビューとはいえ基本的なことはできることが分かりました。
Azure Functions で O/R マッパー等を使わなければならない、という場面が少しでも減るといいなと思います。GA に期待!

www.alterbooth.com

cloudpointer.tech

www.alterbooth.com