Azure聊天中间件的依赖注入?

ema*_*aia 9 c# .net-core asp.net-core azure-bot-service asp.net-core-2.1

我正在使用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. ....
Run Code Online (Sandbox Code Playgroud)

是否有不同的方法来配置允许依赖注入的机器人?

如果MyCustomMiddleware需要HttpClient在其构造函数中输入,我必须在此处创建一个新实例,因此我没有得到DI和我刚刚设置的配置的好处.

Nko*_*osi 1

虽然我不喜欢服务定位器模式,但机器人配置的当前设计对依赖注入不太友好。

利用机器人中间件的设置方式但必须在启动期间提供新实例的性质,我想出了以下解决方法。

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

它将IServiceCollection作为显式依赖项,并推迟服务提供者的创建以及工厂委托中实际中间件的最终解析。

然后可以使用它来实现

middleware.Add(new BotMiddlewareAdapter<MyCustomMiddleware>(services));
Run Code Online (Sandbox Code Playgroud)

当适配器被调用时,它将在初始调用时延迟解析预期的中间件,然后调用它。

事实上,您可以更进一步,将其转换为扩展方法

public static class BotBuilderMiddlewareExtension {
    public static void Add<TMiddleware>(this IList<IMiddleware> middleware, IServiceCollection services) 
        where TMiddleware : IMiddleware {
        middleware.Add(new BotMiddlewareAdapter<TMiddleware>(services));
    }
}
Run Code Online (Sandbox Code Playgroud)

这简化了设置

middleware.Add<MyCustomMiddleware>(services);
Run Code Online (Sandbox Code Playgroud)

  • 这看起来不对劲。任何要求您调用 BuildServiceProvider 的解决方案“总是”都是可疑的。这将创建一个新的宇宙,最终您将得到多个单例实例,不会共享任何内容。 (2认同)