我通过为其创建一个扩展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。
答案 0 :(得分:3)
在Startup.cs中
我将从您的Startup.cs
文件开始,因为这是第一个问题所在,然后我建议您采用其他设计。
使用以下代码块可以有效地完成工作:
services.AddSingleton<GreetingDialog>();
services.AddTransient<IBot, AuthBot<GreetingDialog>>();
services.AddSingleton<GetPersonInfoDialog>();
services.AddTransient<IBot, AuthBot<GetPersonInfoDialog>>();
GreetingDialog
的单个实例(本质上是静态的)。IBot
接口以返回类型为AuthBot
的新GreetingDialog
每次都请求一个IBot
。GetPersonInfoDialog
的单个实例(本质上是静态的)。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
调用更新为使用“问候”。
替代设计
由于我不相信您希望BeginDialogAsync
或GreetingDialog
成为您的实际起点,因此建议您添加另一个名为GetPersonInfoDialog
的对话框,该对话框继承自{{1} }类(Microsoft.Bot.Builder.Solutions.Dialogs NuGet包)。基于here架构(虚拟助手模板),您可以将MainDialog
和RouterDialog
生成为MainDialog
。
假设您的GreetingDialog
只是向用户发送卡片或一些文本以欢迎他们的单一阶段,则可以完全由GetPersonInfoDialog
方法代替,该方法发送您的卡片/消息。然后,通过GreetingDialog
方法example here处理用户到您的OnStartAsync
。
您需要对现有项目进行更改以使其连接起来(假设您保留GetPersonInfoDialog
):
RouteAsync
中为GreetingDialog
,Startup.cs
和GreetingDialog
添加临时注册。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虚拟助手模板,将登录代码移植到此模板应该足够容易。