我正在使用Azure Bot服务和QnAMaker开发新的聊天机器人。我们正在使用BotBuilder中间件(包括自定义中间件)来定制机器人行为。
其中一个中间件将调用Azure函数,我想将新的HttpClientFactory
功能与自定义中间件一起使用 - 但这需要依赖注入。
如何在BotBuilder中间件中使用依赖注入,就像使用常规.NET Core中间件一样?
当您查看 Startup.cs 中的机器人配置时,您可以看到它需要您 新 的所有内容机器人依赖:
services.AddHttpClient<MyFunctionClient>(client =>
{
client.BaseAddress = new Uri(mySettings.GetValue<string>("myFunctionUrl"));
client.DefaultRequestHeaders.Add("x-functions-key", mySettings.GetValue<string>("myFunctionKey"));
});
services.AddBot<QnAMakerBot>(options =>
{
options.CredentialProvider = new ConfigurationCredentialProvider(Configuration);
options.ConnectorClientRetryPolicy = new RetryPolicy(
new BotFrameworkHttpStatusCodeErrorDetectionStrategy(),
3,
TimeSpan.FromSeconds(2),
TimeSpan.FromSeconds(20),
TimeSpan.FromSeconds(1));
var middleware = options.Middleware;
middleware.Add(new ConversationState<ChatLog>(new MemoryStorage()));
middleware.Add(new MyCustomMiddleware()); // <- I want to inject a typed HttpClient here
//... etc. ....
是否有不同的方法来配置允许依赖注入的机器人?
如果MyCustomMiddleware
在其构造函数中需要输入HttpClient
,我必须在此处创建一个新实例,因此我无法获得DI和我刚刚设置的配置的好处起来。
答案 0 :(得分:1)
虽然我不是服务定位器模式的粉丝,但是bot配置的当前设计并不是非常依赖注入的。
使用bot中间件的设置方式,但在启动时必须提供一个新实例,我想出了以下工作。
public class BotMiddlewareAdapter<TMiddleware> : IMiddleware
where TMiddleware : IMiddleware {
private readonly Lazy<TMiddleware> middleware;
public BotMiddlewareAdapter(IServiceCollection services) {
middleware = new Lazy<TMiddleware>(() =>
services.BuildServiceProvider().GetRequiredService<TMiddleware>());
}
public Task OnTurn(ITurnContext context, MiddlewareSet.NextDelegate next) {
return middleware.Value.OnTurn(context, next);
}
}
它将IServiceCollection
作为显式依赖关系,并推迟创建服务提供者并最终解决工厂代理中的实际中间件。
然后可以使用
实现middleware.Add(new BotMiddlewareAdapter<MyCustomMiddleware>(services));
调用适配器时,它将在初始调用时延迟解析预期的中间件,然后调用它。
事实上,您可以进一步采取这一步骤并将其转换为扩展方法
public static class BotBuilderMiddlewareExtension {
public static void Add<TMiddleware>(this IList<IMiddleware> middleware, IServiceCollection services)
where TMiddleware : IMiddleware {
middleware.Add(new BotMiddlewareAdapter<TMiddleware>(services));
}
}
这简化了设置
middleware.Add<MyCustomMiddleware>(services);