しばやん雑記

Azure Serverless とメイドさんが大好きなフリーランスのプログラマーのブログ

Azure Functions v3 で .NET Core 3.1 が利用可能になった

Ignite 2019 のタイミングで Azure Functions v3 のプレビューが正式に公開されましたが、Azure Functions SDK 側の問題で .NET Standard 2.1 ターゲットのライブラリ*1が使えなかったので弄って来ませんでした。

その後 Runtime も一時的に非公開になりましたが、最近全て解決したので試します。現在 v3 はプレビューとプレリリースの 2 種類が公開されています。

f:id:shiba-yan:20191206215355p:plain

プレビューとプレリリースの差が分かりにくいですが、今のところはプレリリースの方が新しい Core Tools とテンプレートとなっています。それぞれの違いは大体以下の通りです。

  • v3-preview
    • .NET Core 3.0 (netcoreapp3.0)
    • Functions SDK v1.0.30-beta2
    • Core Tools v3.0.4
  • v3-prerelease
    • .NET Core 3.1 (netcoreapp3.1 も使える)
    • Functions SDK v3.0.1
    • Core Tools v3.1.0

次の更新ぐらいで v3-preview にも v3-prerelease の内容が降ってくると思うので、v3 のプレビュー中は v3-prerelease を使っておいた方が良さそうな気配です。

Azure Functions v3 の GA 時には .NET Core 3.1 がターゲットになるので、実際にデプロイしたいケースを除いて v3-prerelease 版が出た今となっては .NET Core 3.0 向けを弄る必要はないです。

Timelines
A public preview will be available in October 2019
General Availability, running on .NET Core 3.1 (LTS release), scheduled for Q1 2020

Azure Functions 3.0 · Issue #200 · Azure/app-service-announcements · GitHub

今のところ v3 に必要な .NET Core 3.1 が App Service にデプロイされていないのと、Functions Runtime が古いままなので v3-prerelease をデプロイ可能になるには少し時間がかかります。

App Service の .NET Core 3.1 対応は 12/13 頃に完了する予定らしいです。リージョンやスケールユニットによって多少は前後しそうですが、LTS 版が早めに展開されるのは喜ばしいですね。

前置きはこのくらいにして、実際に Entity Framework Core 3.1 を使う Azure Functions のコードを書いてみることにします。v3-prerelease を使っていきます。

Azure Functions v3 のプレビュー / プレリリースは環境変数を設定しないと出てこないので、以下の記事を参照して設定を追加する必要があります。ユーザー環境変数に追加して VS の再起動で OK です。

Functions SDK はこれまでの 1.0.x から一気に上がって 3.0.1 になりました。SDK はプレリリース版になっていないですが、依存関係は netcoreapp3.0 になっているので v3 専用です。

v3-prerelease を選んでプロジェクトを作成し、Microsoft.EntityFrameworkCore.SqlServer と DI 用の Microsoft.Azure.Functions.Extensions をインストールします。最近の Azure Functions ではインスタンスのライフサイクル管理のために DI を使うのが一般的です。

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>netcoreapp3.1</TargetFramework>
    <AzureFunctionsVersion>v3-prerelease</AzureFunctionsVersion>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.Azure.Functions.Extensions" Version="1.0.0" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="3.1.0" />
    <PackageReference Include="Microsoft.NET.Sdk.Functions" Version="3.0.1" />
  </ItemGroup>
  <ItemGroup>
    <None Update="host.json">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
    <None Update="local.settings.json">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
      <CopyToPublishDirectory>Never</CopyToPublishDirectory>
    </None>
  </ItemGroup>
</Project>

デフォルトでは netcoreapp3.0 で作成されますが、手動で netcoreapp3.1 に変更しました。

DI 周りと Entity Framework Core を使うためのコードは以下のように用意しました。エンティティ周りは適当に用意して、接続文字列は SQL Database Serverless を作成して設定しました。

public class Startup : FunctionsStartup
{
    public override void Configure(IFunctionsHostBuilder builder)
    {
        builder.Services.AddDbContextPool<AppDbContext>(options =>
            options.UseSqlServer(Environment.GetEnvironmentVariable("DefaultSqlConnection")));
    }
}

public class AppDbContext : DbContext
{
    public AppDbContext(DbContextOptions<AppDbContext> options)
        : base(options)
    {
    }

    public DbSet<Blog> Blogs { get; set; }
}

public class Blog
{
    public int Id { get; set; }

    [Required]
    public string Title { get; set; }

    [Required]
    public string Body { get; set; }

    [Required]
    public DateTime CreatedAt { get; set; }
}

あまり見ないであろう AddDbContextPool を使っているのは何となくです。作成してある SQL Database Serverless 側にテーブルを作成して、適当なデータを追加しておきました。

f:id:shiba-yan:20191206232039p:plain

Function 本体はコンストラクタで AppDbContext を受け取って、関数の本体で利用しているだけです。単純に DB に入っているものを JSON として返すだけの簡単なコードです。

public class Function1
{
    public Function1(AppDbContext appDbContext)
    {
        _appDbContext = appDbContext;
    }

    private readonly AppDbContext _appDbContext;

    [FunctionName("Function1")]
    public async Task<IActionResult> Run(
        [HttpTrigger(AuthorizationLevel.Function, "get")] HttpRequest req,
        ILogger log)
    {
        log.LogInformation($".NET Core Version: {Environment.Version}");

        var list = await _appDbContext.Blogs.ToListAsync();

        return new OkObjectResult(list);
    }
}

Visual Studio からデバッグ実行すると、.NET Core 3.1 で動作していることがログから確認できます。

f:id:shiba-yan:20191206235736p:plain

Entity Framework Core 周りでもエラーが出ることなく、DB からデータを取得して応答が返ってくることが確認できました。サクッと .NET Core 3.1 / .NET Standard 2.1 のライブラリが使えたので内容が地味です。

f:id:shiba-yan:20191206234255p:plain

自分は使う予定が今のところないですが .NET Core に対応した Entity Framework 6 も .NET Standard 2.1 がターゲットなので、Azure Functions v3 で使えるようになっているはずです。

少し前に v2 と v3 で Cold start 周りの検証をしたところ、v3 の方が応答時間のばらつきが少なかったのでパフォーマンス面でも v3 のリリースを割と期待しています。*2

*1:Entity Framework Core 3.0 のこと

*2:https://twitter.com/shibayan/status/1195190127917592576