目次
はじめに
- 次の環境を使用して動作確認しています。
ハードウェア CPU: AMD Ryzen 5 3400G, MEM: 16GB, SSD: 130GB OS Windows 10(64ビット) IDE Microsoft Visual Studio Community 2019(16.8.5) + C#(8.0)
(.NET5を使用するためにVisual Studio 16.8.0以降か、別途.NET5 SDKのインストールが必要です。) - 完全なソースコードはこちらで公開しています。
- 次の資料を参考にしています。
.NET5への移行概要
- Azure Functionsの実行方法(モデル)が.NET isolated processに変更されました。
- 以前のAzure Functionsでは、Azure Functionsランタイムとアプリを同一プロセスとして実行するイン・プロセス・モデルが採用されていました。このモデルでは、ランタイム(SDK)が提供する豊富な機能をアプリから無駄なく使用できます。一方で、アプリとランタイムで同じ.NET環境を使用する必要があるので、アプリとランタイムで使用するライブラリの競合、アプリのプロセスを自由にカスタマイズできない、等の問題(制約)が発生します。Azure Functions開発チームでは、新しい.NETバージョンに対応するのがいつも大変のようです。
- そのため、.NET5からはランタイムとアプリを別プロセスとして実行するアウト・オブ・プロセス・モデル(.NETの用語としては.NET isolated process)が採用されました。
(.NET5は長期サポートではなく、この挑戦が成功しなくても影響が少ないと判断したようです。) - .NET5以降のロードマップが公開されています。
将来的にはアウト・オブ・プロセス・モデルに統一していくようです。.NET6ではイン・プロセスモデル/アウト・オブ・プロセスモデルのどちらも選択できますが、将来性を考えるならアウト・オブ・プロセス・モデルの使用をお薦めします。 - その他詳細はこちらをご覧ください。TECHCOMMUNITY.MICROSOFT.COM
Azure Functions now supports .NET 5. Learn about the new is…
- .NET Core 3.1から.NET5に移行する手順の概要を次に示します。
前述の実行モデルの変更に伴う改修が主となり、プロジェクトの形式変更、使用パッケージの.NET isolated process版への置き換えが必要になります。
なお、HTTP以外のService BusやBlobトリガーの場合は、移行前後での改修は不要のようです。No. 概要 説明 1 プロジェクト
定義の変更使用するフレームワークを.NET5、出力の種類をコンソールアプリケーション(Exe)に変更にします。 2 パッケージの変更 Azure Functionsランタイムやトリガー用のパッケージを.NET isolated process用のパッケージ(Microsoft.Azure.Functions.Worker.*)に置き換えます。 3 起動方法の変更 ASP.NET Coreアプリ等のようにホストビルダを使用してプロセスを初期化・実行します。(既存プロジェクトでスタートアップクラスを実装している場合は、DIサービス登録等の処理をこちらに移植する必要があります。) 4 メソッド定義の変更 実行対象のメソッドに付与していたFunctionName属性をFunction属性に変更します。メソッド引数にILoggerを指定している場合は、FunctionContextから取得するよう変更します。 5 HTTPトリガー
使用時の変更HTTPトリガーを使用する場合、
引数・戻り値の型をHttpRequestData, HttpResponseDataに変更します。6 ローカル実行用の
ホスト定義の変更local.settings.jsonのワーカーランタイム(FUNCTIONS_WORKER_RUNTIME)を.NET isolated process(“dotnet-isolated”)に変更します。
.NET5への移行詳細
プロジェクト定義の変更
- プロジェクトのプロパティを開きます。
「対象のフレームワーク」を「.NET 5.0」、「出力の種類」を「クラスライブラリ」から「コンソールアプリケーション」に変更します。 - プロジェクトファイル(.csproj)を直接編集する場合、次のようにTargetFramework/OutputType要素を編集します。12345678<Project Sdk="Microsoft.NET.Sdk"><PropertyGroup><TargetFramework>net5.0</TargetFramework> <!-- 変更箇所 --><AzureFunctionsVersion>v3</AzureFunctionsVersion><OutputType>Exe</OutputType> <!-- 変更箇所 --></PropertyGroup><ItemGroup>...
パッケージの変更
- 既存のイン・プロセス・モデル用のパッケージを、アウト・オブ・プロセス・モデル用のものに置き換える必要があります。
- NuGetパッケージマネージャを起動してパッケージを置き換えます。
NuGetパッケージマネージャの起動方法は、[ツール]-[NuGetパッケージマネージャー]-[ソリューションのNuGetパッケージの管理]、またはプロジェクトの[依存関係]-[パッケージ]を右クリックして[NuGetパッケージの管理]を選択します。 - プロジェクトファイル(.csproj)を直接編集する方法もありますが、使用パッケージの最新安定バージョンが分からないので、前述のNuGetパッケージマネージャを使用する方法をお薦めします。
(次の例ではHTTPトリガーを使用しています。)123456789<Project Sdk="Microsoft.NET.Sdk">...<ItemGroup><PackageReference Include="Microsoft.Azure.Functions.Worker" Version="1.6.0" /><PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http" Version="3.0.13" /><PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" Version="1.3.0" />...</ItemGroup>...
起動方法の変更
- イン・プロセス・モデルの場合、Azure Functionsプロセスの起動をランタイムが行うため、プロセス初期化処理は不要でした。プロセス初期化時に必要となるDIサービスの登録は、ランタイムがスタートアップクラスを実行することで実現します。
アウト・オブ・プロセス・モデルの場合、ホストビルダ(IHostBuilder)を使用してアプリ自身でプロセスの初期化やDIサービス登録処理の呼び出しを実装する必要があります。 - アプリの実行開始場所(エントリポイント)となるメソッドMain()を定義し、ホストビルダでConfigureFunctionsWorkerDefaults()を実行します。
構成情報を追加する場合はConfigureAppConfiguration()、依存関係を注入(DI)する場合はConfigureServices()を使用します。12345678910111213141516171819202122232425class Program{public static Task Main(){var host = new HostBuilder().ConfigureFunctionsWorkerDefaults() // 必須// 構成情報を追加する場合.ConfigureAppConfiguration((context, config) =>{var env = context.HostingEnvironment;config.SetBasePath(Directory.GetCurrentDirectory()).AddJsonFile("appsettings.json", optional: true).AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);})// 依存関係を注入する場合.ConfigureServices((context, services) =>{var configuration = context.Configuration;services.AddHttpClient(); // 例としてHttpClientをDI}).Build();return host.RunAsync();}}
メソッド定義の変更
- 関数のメソッドに付与されたFunctionName属性をFunction属性に変更します。
なお、FunctionName属性の定義(FunctionNameAttribute)は、Microsoft.Azure.Functions.Worker.Extensions.Abstractionsパッケージにあります。HTTPやServiceBusトリガーを使用すると、その依存関係として自動的に参照が追加されます。12345public static class Function1{[FunctionName("Function1")]public static async Task<...> Run(...12345public static class Function1{[Function("Function1")]public static async Task<...> Run(... - メソッドの引数でILoggerを引き渡している場合、FunctionContext経由で取得するよう変更します。1234567public static class Function1{[FunctionName("Function1")]public static async Task<...> Run(..., ILogger log){log.LogInformation("C# HTTP trigger function processed a request.");...1234567public static class Function1{[Function("Function1")]public static async Task<...> Run(..., FunctionContext context){var log = context.GetLogger(nameof(Function1));log.LogInformation("C# HTTP trigger function processed a request.");
HTTPトリガー使用時の変更
- メソッドの引数型HttpRequestをHttpRequestData、戻り値型IActionResultをHttpResponseDataに変更します。1234567891011public static class Function1{[FunctionName("Function1")]public static async Task<IActionResult> Run([HttpTrigger(...)] HttpRequest req, ...){...string responseMessage = "...";return new OkObjectResult(responseMessage);}}12345678910111213public static class Function1{[Function("Function1")]public static async Task<HttpResponseData> Run([HttpTrigger(...)] HttpRequestData req, ...){...string responseMessage = "...";var response = req.CreateResponse(System.Net.HttpStatusCode.OK);response.Body.Write(Encoding.Default.GetBytes(responseMessage));return response;}}
ローカル実行用のホスト定義の変更
- local.settings.jsonのFUNCTIONS_WORKER_RUNTIMEを、アウト・オブ・プロセス・モデルである”dotnet-isolated”に変更します。1234567{"IsEncrypted": false,"Values": {"AzureWebJobsStorage": "UseDevelopmentStorage=true","FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated"}}
トラブルシューティング
ホストが開始していない
- 事象
ローカルで実行時に次のエラー・例外が発生して中断する。- Azure Functions Core Toolsのコンソールでは次のエラーが表示される。
Microsoft.Azure.WebJobs.Script: Did not find functions with language [dotnet].
- アプリのコンソールでは、Microsoft.Azure.WebJobs.Hostで次の例外が発生する。
System.InvalidOperationException("The host has not yet started.")
- Azure Functions Core Toolsのコンソールでは次のエラーが表示される。
- 原因
local.settings.jsonのFUNCTIONS_WORKER_RUNTIMEが”dotnet-isolated”以外になっていることが原因。(当該値に変更する必要がある。)123456789101112131415161718{"IsEncrypted": false,"Values": {"AzureWebJobsStorage": "UseDevelopmentStorage=true",//"FUNCTIONS_WORKER_RUNTIME": "dotnet""FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated"}}</pre]</li></ul><h3>アセンブリをロードできない</h3><ul><li>事象Azure Functionsアプリを起動すると、次の例外が発生して実行が中断される。<pre class="ndw-screen" crayon="false">System.IO.FileNotFoundException: Could not load file or assembly 'xxx' - 原因
- プロジェクトをビルドする際、ランタイムと共有するアセンブリ(パッケージ)は最適化のために単一バージョンのみ保持(それ以外は削除)されます。同一パッケージで異なるバージョンのパッケージを使用している場合、ビルド時にいずれかのバージョンのものが削除されるため、この例外が発生します。
- Microsoft.NET.Sdk.Functions 3.0.12より前では、プロジェクトのプロパティで”_FunctionsSkipCleanOutput”を宣言することで、この動作を抑制できます。12345678<Project Sdk="Microsoft.NET.Sdk"><PropertyGroup><TargetFramework>net5.0</TargetFramework><AzureFunctionsVersion>v3</AzureFunctionsVersion><OutputType>Exe</OutputType><_FunctionsSkipCleanOutput>true</_FunctionsSkipCleanOutput></PropertyGroup>...
- Microsoft.NET.Sdk.Functions 3.0.12以降では、保持対象とするパッケージをFunctionsPreservedDependencies要素で指定できます。
Azure Functions Tools for Visual Studio 2022 を使用して、Azure Fun…
- その他詳細はこちらが参考になると思います。
gRPC公開ポイントの不正
- 事象
Azure Functionsアプリを起動すると、次の例外が発生して実行が中断される。Unhandled exception. System.InvalidOperationException: The gRPC channel URI 'http://:0' could not be parsed.
- 原因
- gRPCの公開ポイントは構成情報(IConfiguration)から取得します。何らかの理由で公開ポイントを取得できない場合、この事象が発生します。
- 既定ではAzure Functionsホストビルダ実行時のConfigureFunctionsWorkerDefaults()で、構成情報が初期化されます。作成された構成情報に対して、意図しない操作を行い、構成情報のクリアや上書きをしている可能性があります。
- 構成情報(IConfiguration)や構成情報ビルダ(IConfigurationBuilder)に対する操作を追跡することで、原因個所を特定できると思います。
- なお、次のように構成情報ビルダのソースをクリアすると同事象を再現できます。12hostBuilder.ConfigureAppConfiguration(config => config.Sources.Clear());