You have to keep in mind that when using Adaptive Dialogs (that is, the core of Composer) Dialogs are singletons and, when using Composer, they're not instantiated from dependency injection (DI).
Also, since dialogs are singletons, you can't (well, you could but you shouldn't) use services like constructor injected DbContexts and similar (when working with the SDK, that is, coding).
The easiest way to solve this is by using HTTP requests using the HttpRequest
action. This is the way that's built into the whole adaptive dialogs ecosystem to achieve this kind of functionality.
If you really insist on doing it with DI into the dialogs, you'd have to solve DI from the TurnContext
and you'd have to set it up in the adapter. However, that's a bit convoluted an requires you to use a custom runtime.
UPDATE Added the way to implement DI with adaptive dialogs.
1 - Register the service class in the turn state in the adapter
public class AdapterWithErrorHandler : BotFrameworkHttpAdapter
{
public AdapterWithErrorHandler(
IConfiguration configuration,
ILogger<BotFrameworkHttpAdapter> logger,
//...
QnAClient qnaClient)
: base(configuration, logger)
{
// Add QnAClient to TurnState so we can use it while in the turn
Use(new RegisterClassMiddleware<QnAClient>(qnaClient));
//...
}
}
In the code above QnAClient
is an typed HttpClient created with IHttpClientFactory
so it's a safe to use singleton.
2 - Get the service from the TurnState
wherever you need it
public async Task SetPropertiesAsync(DialogContext context, ...)
{
var qnaClient = context.Context.TurnState.Get<QnAClient>();
//...
}
BTW, this is a nice way to get an HttpClient
properly managed by IHttpClientFactory
when you register it like this in ConfigureServices
:
services.AddHttpClient<QnAClient>()
.AddTransientHttpErrorPolicy(p => p.WaitAndRetryAsync(new[] { 1, 2, 3, 5, 8, 13 }.Select(t => TimeSpan.FromSeconds(t))))
.AddTransientHttpErrorPolicy(p => p.CircuitBreakerAsync(6, TimeSpan.FromSeconds(30)));
In this case with retry policies from Polly.