如何添加多个ComponentDialogs? [英] How do I add multiple ComponentDialogs?

查看:126
本文介绍了如何添加多个ComponentDialogs?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I made a new ComponentDialog by creating a class for it that extends ComponentDialog like so:

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中:

Then I added it in 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现在不再起作用,说:

But what I noticed is that only the last Dialog gets used. GreetingDialog now no longer works, saying:

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

DialogContext.BeginDialogAsync(): A dialog with an id of 'greeting' wasn't found. The dialog must be included in the current or parent DialogSet. For example, if subclassing a ComponentDialog you can call AddDialog() within your constructor.

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

I did notice, however, that the GetPersonInfo dialog does indeed begin and the Greeting dialog doesn't anymore. It's like I can only use one or the other. I noticed only the last added "transient" gets used, as if it overrides the previous transients.

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

How do I add multiple component dialogs in my Startup.cs file? Or am I even going about this correctly? I did not find any documentation explaining how to have multiple ComponentDialogs.

推荐答案

在Startup.cs中

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

I will start with your Startup.cs file since that is where the first issue is, then I will suggest an alternative design.

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

What you are effectively doing with the following block of code is:

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接口,以在每次请求IBot时返回类型为GetPersonInfoDialog的新AuthBot(这将覆盖步骤2中的注册).
  1. Registering a single instance of the GreetingDialog for your bot (essentially a static).
  2. Registering the IBot interface to return a new AuthBot of type GreetingDialog everytime an IBot is requested.
  3. Registering a single instance of the GetPersonInfoDialog for your bot (essentially a static).
  4. Registering the IBot interface (again) to return a new AuthBot of type GetPersonInfoDialog everytime an IBot is requested (which will overwrite the registration in step 2).

您可以阅读有关服务寿命的更多信息此处.

You can read a lot more about Service lifetimes here.

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

So what you actually want is more like the below:

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().

DialogContext.BeginDialogAsync(): A dialog with an id of 'greeting' wasn't found. The dialog must be included in the current or parent DialogSet. For example, if subclassing a ComponentDialog you can call AddDialog() within your constructor.

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

This error message is caused because your GetPersonInfoDialog doesn't know about your GreetingDialog (and it shouldn't). I believe this is a runtime error because I remember running into a similar issue myself. Since you haven't provided the full implementation for your GetPersonInfoDialog class I have to assume that somewhere inside there you are trying to do something like the following:

dialogContext.BeginDialogAsync("greeting");

or

dialogContext.BeginDialogAsync(nameof(GreetingDialog));

按照

as per the documentation the first parameter is the ID of the dialog to start, this ID is also used to retrieve the dialog from the dialog stack. In order to call one dialog from inside another, you will need to add it to the parent dialog's DialogSet. The accepted way to do this is to add a call inside the constructor for the parent dialog like so:

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

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

这使用

This uses the AddDialog method provided by the Microsoft.Bot.Builder.Dialogs NuGet package and exposed through the ComponentDialog class.

然后,当您要显示ChildDialog时,您将调用:

Then when you want to display the ChildDialog you would call:

dialogContext.BeginDialogAsync(nameof(ChildDialog));

根据您的情况,可以将ParentDialog替换为GetPersonInfoDialog,将ChildDialog替换为GreetingDialog.由于您的GreetingDialog仅可能使用一次(这不是一个可以多次调用但使用不同参数的实用程序对话框-在这种情况下,您希望提供特定的ID而不是使用nameof(GreetingDialog)),因此可以将类名的字符串表示形式用作DialogId,可以在AddDialog调用内使用"greeting",但也必须更新BeginDialogAsync调用以也使用"greeting".

In your case you can replace ParentDialog with GetPersonInfoDialog and ChildDialog with GreetingDialog. Since your GreetingDialog is only likely to be used once (it is not a utility dialog that could be called multiple times but with different arguments - in this case you would want to provide a specific ID rather than using nameof(GreetingDialog)) it is fine to go with the string representation of the class name as the DialogId, you can using "greeting" inside the the AddDialog call but you would also have to update the BeginDialogAsync call to also use "greeting".

替代设计

由于我不相信您希望将GreetingDialogGetPersonInfoDialog用作您的实际起点,因此建议您添加另一个名为MainDialog的对话框,该对话框继承自RouterDialog类(Microsoft.Bot.Builder .Solutions.Dialogs NuGet包).基于体系结构(虚拟助手模板)

Since I don't believe you want either GreetingDialog or GetPersonInfoDialog to be your actual starting points, I would suggest adding another dialog called MainDialog which inherits from the RouterDialog class (Microsoft.Bot.Builder.Solutions.Dialogs NuGet package). Based on the architecture (Virtual Assistant Template) here you would have your MainDialog spawn off your GreetingDialog and GetPersonInfoDialog.

假设您的GreetingDialog只是一个发送卡片或文本给用户欢迎的单一阶段,它可以完全由发送您的卡片/消息的OnStartAsync方法代替.然后将通过RouteAsync方法

Assuming that your GreetingDialog is only a single stage where it sends a card or some text to the user to welcome them, it could be completely replaced by the OnStartAsync method which sends your card/message. Getting your user to your GetPersonInfoDialog would then be handled by the RouteAsync method example here.

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

The changes you would need to make to your existing project to get this wired up are (assuming that you keep the GreetingDialog):

  • Startup.cs中为GreetingDialogGetPersonInfoDialogMainDialog添加临时注册.
  • 添加用于将IBot映射到AuthBot<MainDialog>
  • 的瞬态注册
  • MainDialog的构造函数中添加调用以添加子对话框GreetingDialogGetPersonInfoDialog.
  • MainDialogOnBeginDialogOnStartAsync中启动GreetingDialog.
  • MainDialogRouteAsync中,处理显示GetPersonInfoDialog之前的所有情况.
  • 我可能还错过了一些其他步骤.
  • Add Transient registrations in Startup.cs for GreetingDialog, GetPersonInfoDialog and MainDialog.
  • Add a Transient registration for mapping IBot to AuthBot<MainDialog>
  • Add calls inside the constructor of MainDialog to add the child dialogs GreetingDialog and GetPersonInfoDialog.
  • In the OnBeginDialog or OnStartAsync of MainDialog start your GreetingDialog.
  • In the RouteAsync of MainDialog handle any conditions around showing the GetPersonInfoDialog before displaying it.
  • There may be some additional steps that I have missed.

有用的链接:

  • Implementing sequential conversation flow.
  • Reusing dialogs.
  • Virtual Assistant template outline
  • Virtual Assistant template architecture.

修改

要在内实现所需的目标OAuth示例,您可以执行以下操作:

To achieve what you want within the OAuth sample you could do the following:

LogoutDialog.cs 中进行更改:

private async Task<DialogTurnResult> InterruptAsync(DialogContext innerDc, CancellationToken cancellationToken = default(CancellationToken))

protected virtual async Task<DialogTurnResult> InterruptAsync(DialogContext innerDc, CancellationToken cancellationToken = default(CancellationToken))

MainDialog.cs 中添加:

protected override async Task<DialogTurnResult> InterruptAsync(DialogContext innerDc, CancellationToken cancellationToken = default(CancellationToken))
{
    if (innerDc.Context.Activity.Type == ActivityTypes.Message)
    {
        var text = innerDc.Context.Activity.Text.ToLowerInvariant();

        if (text == "check email")
        {
            //
            return innerDc.BeginDialogAsync(/*TODO*/);
        }
        else if (text == "check calender")
        {
            //
            return innerDc.BeginDialogAsync(/*TODO*/);
        }
        // etc


        return await base.InterruptAsync(innerDc, cancellationToken);
    }

    return null;
}

使用AddDialog方法在MainDialog的构造函数中注册日历,电子邮件等对话框.

along with registering your Calendar, Email, etc dialogs in the constructor for MainDialog using the AddDialog method.

我会严重建议您使用虚拟助手模板.,因为它使用 LUIS 确定用户的意图(检查电子邮件,检查日历等),然后进行相应的路由,相关代码位于

I would seriously advise you to look at using the Virtual Assistant Template. As it uses LUIS to determing the user's intent (check email, check calendar etc), then route them accordingly, the relevant code is in this method. Using LUIS to determine intents has the advantage of being able to tie multiple ways of asking the same thing to the same intent, so you're not relying on your users to explicitly type "check calendar", you can have "show me my calendar", "what is my availability for next Monday", "am I free this afternoon", "check if I have any appointments tomorrow" etc. In fact Microsoft has already built Skills for email, and calendar which work with the Virtual Assistant Template, it should be easy enough to port the login code over to this template.

这篇关于如何添加多个ComponentDialogs?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆