如何添加多个ComponentDialogs? [英] How do I add multiple 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>>();
- 为您的机器人注册
GreetingDialog
的单个实例(本质上是静态的). - 注册
IBot
接口以每次返回新的AuthBot
类型为GreetingDialog
的IBot
. - 为您的机器人注册
GetPersonInfoDialog
的单个实例(本质上是静态的). - 再次注册
IBot
接口,以在每次请求IBot
时返回类型为GetPersonInfoDialog
的新AuthBot
(这将覆盖步骤2中的注册).
- Registering a single instance of the
GreetingDialog
for your bot (essentially a static). - Registering the
IBot
interface to return a newAuthBot
of typeGreetingDialog
everytime anIBot
is requested. - Registering a single instance of the
GetPersonInfoDialog
for your bot (essentially a static). - Registering the
IBot
interface (again) to return a newAuthBot
of typeGetPersonInfoDialog
everytime anIBot
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".
替代设计
由于我不相信您希望将GreetingDialog
或GetPersonInfoDialog
用作您的实际起点,因此建议您添加另一个名为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
中为GreetingDialog
,GetPersonInfoDialog
和MainDialog
添加临时注册. - 添加用于将
IBot
映射到AuthBot<MainDialog>
的瞬态注册
- 在
MainDialog
的构造函数中添加调用以添加子对话框GreetingDialog
和GetPersonInfoDialog
. - 在
MainDialog
的OnBeginDialog
或OnStartAsync
中启动GreetingDialog
. - 在
MainDialog
的RouteAsync
中,处理显示GetPersonInfoDialog
之前的所有情况. - 我可能还错过了一些其他步骤.
- Add Transient registrations in
Startup.cs
forGreetingDialog
,GetPersonInfoDialog
andMainDialog
. - Add a Transient registration for mapping
IBot
toAuthBot<MainDialog>
- Add calls inside the constructor of
MainDialog
to add the child dialogsGreetingDialog
andGetPersonInfoDialog
. - In the
OnBeginDialog
orOnStartAsync
ofMainDialog
start yourGreetingDialog
. - In the
RouteAsync
ofMainDialog
handle any conditions around showing theGetPersonInfoDialog
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屋!