この記事は Microsoft Azure Advent Calendar 2019 の 17日目の記事…ではありません(笑)
アドカレ記事は別途書いてます。今日中には上げます!
[追記] 書きました↓
Durable Functions を使ってコードのみでアンケート回答に便利な LINE Bot を簡単につくる - Qiita
で、この記事は、アドカレ記事を書いていたときに遭遇したエラーについてのメモです。
エラーの内容
前提としては、
という状況。
以下のように Startup
を書いたら…
using System; using LineDC.Messaging; using Microsoft.Extensions.DependencyInjection; using Microsoft.Azure.Functions.Extensions.DependencyInjection; [assembly: FunctionsStartup(typeof(Sample.LineBot.Startup))] namespace Sample.LineBot { public class Startup : FunctionsStartup { public override void Configure(IFunctionsHostBuilder builder) { builder.Services.AddSingleton<IDurableWebhookApplication, EnqBotApp>(_ => new EnqBotApp(LineMessagingClient.Create( Environment.GetEnvironmentVariable("ChannelAccessToken")), Environment.GetEnvironmentVariable("ChannelSecret"))); } } }
なぜかこんなエラーが。
2019-12-17T03:21:41.080 [Error] Executed 'HttpStart' (Failed, Id=5cf2c546-9406-4870-89cc-bae17e4e4e5d) System.InvalidOperationException : Unable to resolve service for type 'Sample.LineBot.EnqBotApp' while attempting to activate 'Sample.LineBot.DurableEnqFunctions'. at Microsoft.Extensions.DependencyInjection.ActivatorUtilities.GetService(IServiceProvider sp,Type type,Type requiredBy,Boolean isDefaultParameterRequired) at lambda_method(Closure ,IServiceProvider ,Object[] ) at Microsoft.Azure.WebJobs.Host.Executors.DefaultJobActivator.CreateInstance[T](IServiceProvider serviceProvider) at C:\projects\azure-webjobs-sdk-rqm4t\src\Microsoft.Azure.WebJobs.Host\Executors\DefaultJobActivator.cs : 37 at Microsoft.Azure.WebJobs.Host.Executors.DefaultJobActivator.CreateInstance[T](IFunctionInstanceEx functionInstance) at C:\projects\azure-webjobs-sdk-rqm4t\src\Microsoft.Azure.WebJobs.Host\Executors\DefaultJobActivator.cs : 32 at Microsoft.Azure.WebJobs.Host.Executors.ActivatorInstanceFactory`1.<>c__DisplayClass1_1.<.ctor>b__0(IFunctionInstanceEx i) at C:\projects\azure-webjobs-sdk-rqm4t\src\Microsoft.Azure.WebJobs.Host\Executors\ActivatorInstanceFactory.cs : 20 at Microsoft.Azure.WebJobs.Host.Executors.ActivatorInstanceFactory`1.Create(IFunctionInstanceEx functionInstance) at C:\projects\azure-webjobs-sdk-rqm4t\src\Microsoft.Azure.WebJobs.Host\Executors\ActivatorInstanceFactory.cs : 26 at Microsoft.Azure.WebJobs.Host.Executors.FunctionInvoker`2.CreateInstance(IFunctionInstanceEx functionInstance) at C:\projects\azure-webjobs-sdk-rqm4t\src\Microsoft.Azure.WebJobs.Host\Executors\FunctionInvoker.cs : 44 at Microsoft.Azure.WebJobs.Host.Executors.FunctionExecutor.ParameterHelper.Initialize() at C:\projects\azure-webjobs-sdk-rqm4t\src\Microsoft.Azure.WebJobs.Host\Executors\FunctionExecutor.cs : 846 at async Microsoft.Azure.WebJobs.Host.Executors.FunctionExecutor.TryExecuteAsyncCore(IFunctionInstanceEx functionInstance,CancellationToken cancellationToken) at C:\projects\azure-webjobs-sdk-rqm4t\src\Microsoft.Azure.WebJobs.Host\Executors\FunctionExecutor.cs : 116
メッセージで調べるといろいろ出てくるのですが、根本的な原因・解決策はすぐにはわからず。
解決策
とりあえずいろいろやってみましたが、以下のように記述を変えたらエラーが出なくなることがわかりました。
using System; using LineDC.Messaging; using Microsoft.Extensions.DependencyInjection; using Microsoft.Azure.Functions.Extensions.DependencyInjection; [assembly: FunctionsStartup(typeof(Sample.LineBot.Startup))] namespace Sample.LineBot { public class Startup : FunctionsStartup { public override void Configure(IFunctionsHostBuilder builder) { var client = LineMessagingClient.Create(Environment.GetEnvironmentVariable("ChannelAccessToken")); builder.Services .AddSingleton<ILineMessagingClient>(_ => client) .AddSingleton<EnqBotApp>(_ => new EnqBotApp(client, Environment.GetEnvironmentVariable("ChannelSecret"))); } } }
このエラーが出たら DI のしかたを見直してみるといいのかも。
※ 追記 ※
WebhookApplication クラスのスコープがまずかったので修正。
using System; using LineDC.Messaging; using Microsoft.Extensions.DependencyInjection; using Microsoft.Azure.Functions.Extensions.DependencyInjection; [assembly: FunctionsStartup(typeof(Sample.LineBot.Startup))] namespace Sample.LineBot { public class Startup : FunctionsStartup { public override void Configure(IFunctionsHostBuilder builder) { builder.Services .AddSingleton<ILineMessagingClient>(_ => LineMessagingClient.Create(Environment.GetEnvironmentVariable("ChannelAccessToken"))) .AddTransient<EnqBotApp>(s => new EnqBotApp(s.GetService<ILineMessagingClient>(), Environment.GetEnvironmentVariable("ChannelSecret"))); } } }
今回またかずきさんに DI に関するご指摘をいただきました(技術書典7 に続き3か月ぶり2回目)。
s.GetService<T>()
で DI コンテナに登録しているサービスをとれるとのことも教えていただいた。知らなかった。
DI まわりはちゃんと勉強して毎回ちゃんとスコープに気を付けないとあぶないですね。