Bot Framework V4 依赖注入不起作用

Bot Framework V4 dependence injection not working

我正在使用 .Net Core 2.1 构建版本 4 的 Bot,如示例 here,但我没有收到来自 Bot Framework Emulator 的任何响应或来自 Visual Studio 的代码错误。当我从 url 开始时,它会在下图中显示此错误。 我认为依赖注入有问题。 我做错了什么?

启动时:

public class Startup
    {
        public Startup(IConfiguration configuration, IHostingEnvironment env)
        {
            Configuration = configuration;
            var builder = new ConfigurationBuilder()
            .SetBasePath(env.ContentRootPath)
            .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
            .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
            .AddEnvironmentVariables();
            configuration = builder.Build();

        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

            // Create the Bot Framework Adapter with error handling enabled.
            services.AddSingleton<IBotFrameworkHttpAdapter, AdapterWithErrorHandler>();

            // Create the bot as a transient. In this case the ASP Controller is expecting an IBot.
           // services.AddTransient<IBot, MyBot>();

            // Create the credential provider to be used with the Bot Framework Adapter.
            services.AddSingleton<ICredentialProvider, ConfigurationCredentialProvider>();

            // Create the Bot Framework Adapter.

            // storage
            services.AddSingleton<IStorage, MemoryStorage>();

            // Create the User state. (Used in this bot's Dialog implementation.)
            services.AddSingleton<UserState>();

            services.AddSingleton<RootDialog>();
            services.AddTransient<IBot, DialogBot<RootDialog>>();

            // Create the Conversation state. (Used by the Dialog system itself.)
            services.AddSingleton<ConversationState>();

            services.AddSingleton(Configuration);

        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseHsts();
            }

            app.UseDefaultFiles();
            app.UseStaticFiles();
            app.UseWebSockets();
            app.UseMvc();
            app.UseBotFramework();

        }
    }

在 DialogBot.cs 中:

 public class DialogBot<T>: ActivityHandler where T : Dialog
    {
        protected readonly BotState ConversationState;
        protected readonly Dialog Dialog;
        protected readonly ILogger Logger;
        protected readonly BotState UserState;

        public DialogBot(ConversationState conversationState, UserState userState, T dialog, ILogger<DialogBot<T>> logger)
        {
            ConversationState = conversationState;
            UserState = userState;
            Dialog = dialog;
            Logger = logger;
        }

        public override async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default(CancellationToken))
        {
            await base.OnTurnAsync(turnContext, cancellationToken);

            // Save any state changes that might have occured during the turn.
            await ConversationState.SaveChangesAsync(turnContext, false, cancellationToken);
            await UserState.SaveChangesAsync(turnContext, false, cancellationToken);
        }

        protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
        {
            Logger.LogInformation("Running dialog with Message Activity.");

            // Run the Dialog with the new message Activity.
            await Dialog.RunAsync(turnContext, ConversationState.CreateProperty<DialogState>(nameof(DialogState)), cancellationToken);
        }
        protected override async Task OnMembersAddedAsync(IList<ChannelAccount> membersAdded, ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
        {
            foreach (var member in membersAdded)
            {
                if (member.Id != turnContext.Activity.Recipient.Id)
                {
                    await turnContext.SendActivityAsync(MessageFactory.Text($"Welcome!"), cancellationToken);
                }
            }
        }

在 RootDialog.cs 中:

[Serializable]
    public class RootDialog : ComponentDialog
    {
        protected readonly ILogger _logger;
        protected readonly Dialog _dialog;
        protected readonly BotState _conversationState;
        protected readonly AdapterWithErrorHandler _adapterWithErrorHandler;
        protected readonly Encryption _encryption;
        private readonly IConfiguration _iConfiguration;


        public RootDialog(ConversationState conversationState, Dialog dialog,ILogger<RootDialog> Logger, AdapterWithErrorHandler AdapterWithErrorHandler, Encryption Encryption, IConfiguration configuration)
        {
            _conversationState = conversationState;
            _dialog = dialog;
            _logger = Logger;
            _adapterWithErrorHandler = AdapterWithErrorHandler;
            _encryption = Encryption;
            _iConfiguration = configuration;
        }

        const string HeroCard = "Hero Card";
        const string ThumbnailCard = "Thumbnail Card";

        private async Task<DialogTurnResult> StartDialogAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
        {
            // Start the child dialog. This will run the top slot dialog than will complete when all the properties are gathered.
            return await stepContext.BeginDialogAsync("slot-dialog", null, cancellationToken);
        }
}

模拟器:

移除

Dialog dialog,

来自你的 RootDialog

它没有任何作用,容器不知道如何解析它以注入目标 class。

RootDialog 将重构为

[Serializable]
public class RootDialog : ComponentDialog {
    protected readonly ILogger _logger;        
    protected readonly BotState _conversationState;
    protected readonly AdapterWithErrorHandler _adapterWithErrorHandler;
    protected readonly Encryption _encryption;
    private readonly IConfiguration _iConfiguration;

    public RootDialog(ConversationState conversationState, ILogger<RootDialog> logger, AdapterWithErrorHandler adapterWithErrorHandler, Encryption encryption, IConfiguration configuration) {
        _conversationState = conversationState;            
        _logger = logger;
        _adapterWithErrorHandler = adapterWithErrorHandler;
        _encryption = encryption;
        _iConfiguration = configuration;
    }

    const string HeroCard = "Hero Card";
    const string ThumbnailCard = "Thumbnail Card";

    private async Task<DialogTurnResult> StartDialogAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
    {
        // Start the child dialog. This will run the top slot dialog than will complete when all the properties are gathered.
        return await stepContext.BeginDialogAsync("slot-dialog", null, cancellationToken);
    }
}

确保所有要注入目标 class 的显式依赖项都已注册到服务集合并扩展到 DI 容器。