如何添加多个ComponentDialogs?

时间:2019-06-21 23:29:52

标签: botframework azure-bot-service

我通过为其创建一个扩展ComponentDialog的类来创建了一个新的ComponentDialog,如下所示:

public class GetPersonInfoDialog : ComponentDialog
{
        protected readonly ILogger Logger;

        public GetPersonInfoDialog(IConfiguration configuration, ILogger<GetPersonInfoDialog> logger)
            : base("get-person-info", configuration["ConnectionName"])
        { }
        // code ommitted
    }
}

然后我将其添加到Startup.cs中:

public void ConfigureServices(IServiceCollection services)
    {

        // ...

        services.AddSingleton<GreetingDialog>();
        services.AddTransient<IBot, AuthBot<GreetingDialog>>();

        // My new component dialog:
        services.AddSingleton<GetPersonInfoDialog>();
        services.AddTransient<IBot, AuthBot<GetPersonInfoDialog>>();
    }
}

但是我注意到的是只有最后一个对话框被使用了。 GreetingDialog现在不再起作用,说:

  

DialogContext.BeginDialogAsync():未找到ID为“ greeting”的对话框。该对话框必须包含在当前或父DialogSet中。例如,如果子类化ComponentDialog,则可以在构造函数中调用AddDialog()。

但是,我确实注意到,GetPersonInfo对话框确实开始了,而Greeting对话框不再了。就像我只能使用其中一个。我注意到只有最后添加的“瞬变”被使用,好像它会覆盖以前的瞬变一样。

如何在Startup.cs文件中添加多个组件对话框?还是我什至正确地解决了这个问题?我没有找到任何文档解释如何拥有多个ComponentDialogs。

1 个答案:

答案 0 :(得分:3)

在Startup.cs中

我将从您的Startup.cs文件开始,因为这是第一个问题所在,然后我建议您采用其他设计。

使用以下代码块可以有效地完成工作:

services.AddSingleton<GreetingDialog>();
services.AddTransient<IBot, AuthBot<GreetingDialog>>();

services.AddSingleton<GetPersonInfoDialog>();
services.AddTransient<IBot, AuthBot<GetPersonInfoDialog>>();
  1. 为您的机器人注册GreetingDialog的单个实例(本质上是静态的)。
  2. 注册IBot接口以返回类型为AuthBot的新GreetingDialog 每次都请求一个IBot
  3. 为您的机器人注册GetPersonInfoDialog的单个实例(本质上是静态的)。
  4. 再次注册IBot接口,以在每次请求AuthBot时返回类型为GetPersonInfoDialog的新IBot(这将覆盖步骤2中的注册)。

您可以阅读有关服务寿命here的更多信息。

所以您真正想要的是如下所示:

public void ConfigureServices(IServiceCollection services)
{
    // Other code

    // Register dialogs
    services.AddTransient<GreetingDialog>();
    services.AddTransient<GetPersonInfoDialog>();

    // Some more code

    // Configure bot
    services.AddTransient<IBot, DialogBot<GreetingDialog>>();
}

错误消息

  

DialogContext.BeginDialogAsync():ID为“ greeting”的对话框   找不到。该对话框必须包含在当前对话框或父对话框中   对话框集。例如,如果将ComponentDialog子类化,则可以调用   构造函数中的AddDialog()。

此错误消息是由于您的GetPersonInfoDialog不知道您的GreetingDialog而引起的(应该不知道)。我相信这是一个运行时错误,因为我记得自己也遇到过类似的问题。由于您尚未为GetPersonInfoDialog类提供完整的实现,因此我不得不假定您在其中的某个位置尝试执行以下操作:

dialogContext.BeginDialogAsync("greeting");

or

dialogContext.BeginDialogAsync(nameof(GreetingDialog));

根据the documentation,第一个参数是要启动的对话框的 ID ,该ID也用于从对话框堆栈中检索对话框。为了从另一个对话框中调用一个对话框,您需要将其添加到父对话框的DialogSet中。可接受的方法是在父对话框的构造函数中添加一个调用,如下所示:

public ParentDialog(....)
    : base(nameof(ParentDialog)
{
    // Some code

    // Important part
    AddDialog(new ChildDialog(nameof(ChildDialog)));
}

这使用Microsoft.Bot.Builder.Dialogs NuGet包提供的AddDialog方法,并通过ComponentDialog类公开。

然后,当您要显示ChildDialog时,您将呼叫:

dialogContext.BeginDialogAsync(nameof(ChildDialog));

根据您的情况,您可以将ParentDialog替换为GetPersonInfoDialog,将ChildDialog替换为GreetingDialog。由于您的GreetingDialog仅可能使用一次(它不是一个可以多次调用但使用不同参数的实用程序对话框,在这种情况下,您将希望提供特定的ID而不是使用{{1} })可以将类名的字符串表示形式用作DialogId,可以在nameof(GreetingDialog)调用中使用“ greeting”,但是还必须将AddDialog调用更新为使用“问候”。


替代设计

由于我不相信您希望BeginDialogAsyncGreetingDialog成为您的实际起点,因此建议您添加另一个名为GetPersonInfoDialog的对话框,该对话框继承自{{1} }类(Microsoft.Bot.Builder.Solutions.Dialogs NuGet包)。基于here架构(虚拟助手模板),您可以将MainDialogRouterDialog生成为MainDialog

假设您的GreetingDialog只是向用户发送卡片或一些文本以欢迎他们的单一阶段,则可以完全由GetPersonInfoDialog方法代替,该方法发送您的卡片/消息。然后,通过GreetingDialog方法example here处理用户到您的OnStartAsync

您需要对现有项目进行更改以使其连接起来(假设您保留GetPersonInfoDialog):

  • RouteAsync中为GreetingDialogStartup.csGreetingDialog添加临时注册。
  • 添加用于将GetPersonInfoDialog映射到MainDialog的瞬态注册
  • IBot的构造函数中添加调用以添加子对话框AuthBot<MainDialog>MainDialog
  • GreetingDialog的{​​{1}}或GetPersonInfoDialog中启动您的OnBeginDialog
  • OnStartAsync的{​​{1}}中处理显示MainDialog之前的所有条件。
  • 我可能错过了一些其他步骤。

有用的链接:


修改

要在OAuth sample中实现您想要的目标,可以执行以下操作:

LogoutDialog.cs 中进行更改:

GreetingDialog

RouteAsync

MainDialog.cs 中添加:

MainDialog

使用GetPersonInfoDialog方法在private async Task<DialogTurnResult> InterruptAsync(DialogContext innerDc, CancellationToken cancellationToken = default(CancellationToken)) 的构造函数中注册日历,电子邮件等对话框。

我会建议您使用Virtual Assistant Template因为它使用LUIS来确定用户的意图(检查电子邮件,检查日历等),然后进行路由相应地,相关代码在this method中。使用LUIS来确定意图的优势在于,可以将多种询问同一事物的方法绑定到同一意图,因此您不必依赖用户显式键入“检查日历”,而可以将“显示我的日历” ”,“下周一我有什么空缺”,“我今天下午有空吗”,“检查明天是否有约会”等。事实上,Microsoft已经为电子邮件和日历构建了Skills虚拟助手模板,将登录代码移植到此模板应该足够容易。