Bot框架发生了一些令人悲伤而不是微笑的事情。
采用以下示例:
public async Task StartAsync(IDialogContext context)
{
_userName = context.FindUserName();
_packageId = context.FindPackageId();
if(!string.IsNullOrEmpty(_packageId))
{
context.Call(new ConfirmPackageIdResuse(_packageId), AfterConfirmIdReuseAsync);
}
else
{
context.Call(new AskForPackageIdDialog(), DisplayPackageStatusAsync);
}
await Task.CompletedTask;
}
此启动任务位于基础LuisDialog中从多个方法调用的类ObtainPackageIdDialog:IDialog 中。 packageId 用于跟踪包裹,更改地址,更改交货时间等。
流程如下:
现在,如果上下文中存在packageId(存储在 ConversationData 中),那么机器人会询问用户是否要使用它( ConfirmPackageIdReuse )或者如果他想输入新的包ID( AskForPackageIdDialog )。
后者看起来像这样:
[Serializable]
public class AskForPackageIdDialog : IDialog<string>
{
public async Task StartAsync(IDialogContext context)
{
await context.PostAsync("Type in the package id:");
context.Wait(MessageReceivedAsync);
}
public async Task MessageReceivedAsync(IDialogContext context, IAwaitable<IMessageActivity> result)
{
var candidate = (await result).Text;
var validator = WebApiApplication.IoCResolver.GetInstance<IPackageValidator>();
if(validator.IsValidId(candidate))
{
context.SetPackageId(candidate);
context.Done(candidate);
}
else
{
context.Wait(MessageReceivedAsync);
}
}
}
AskForPackageIdDialog 中的 MessageReceivedAsync()立即执行,无需等待。没有例外。
由于 MessageReceivedAsync()立即执行,因此结果中没有有效的packageId,因此 else 语句会启动。问题是,它不是在 AskForPackageIdDialog 中更长,但是在对话框堆栈中调用它的类中。
从 AskForPackageIdDialog 的声明中可以看出,它是 IDialog&lt; string&gt; ,因此下面的对话框将通过以下方法恢复:
private async Task DisplayPackageStatusAsync(IDialogContext context, IAwaitable<string> result)
然而,在这里,它崩溃,声称它收到 IAwaitable&lt; MessageActivity&gt; ,即使关闭对话框类型IDialog。
我已经一遍又一遍地阅读了关于机器人框架的文档,我无法理解为什么这对我失败了。关于我可能做错的任何意见将不胜感激。
观察
它看起来像底层LUISDialog可能是责备在这里,我可以告诉它它确实填写activity.text,这是在堆栈上出现2个对话框。该属性是get / set,我已经尝试通过将其显式设置为string.Empty来清除它,但它仍然使用活动文本到达2个堆栈,使得Intent命中。
我有一个理论, context.Call()似乎是从我的LUIS对话框中执行 context.Forward(),因为activity.Text具有原始的用户文字。然后通过立即调用MessageReceivedAsync抛出我的其他对话框。如果是这种情况,如何在进行呼叫之前正确重置context.Activity?
基于以上和我的理论,这个可以解释为什么我从 context.Call&lt; string&gt;()和 context.Call得到混合回复()即可。有人可以向我确认是这样的吗?在开发期间,我看到了很多关于错误类型的 IAwaitable 的例外,没有明显的解释。
我在StackOverflow上发现了一个问题,表明僵尸框架现在自动使用上次使用的 context.Wait 方法,以避免过多的样板,但我相信这可能是我的根源现在悲伤,肯定会引起很多困惑。
**请求评论:**
[Serializable]
public class LuisRoot : LuisDialog<object>
{
public LuisRoot(ISettingsReader settings)
: base(new LuisService(new LuisModelAttribute(settings["luis.modelid"], settings["luis.subscriptionkey"])))
{
}
[LuisIntent("None")]
public async Task None(IDialogContext context, LuisResult result)
{
await context.PostAsync("Oh, I'm sorry, but I have no idea what you just said!");
context.Wait(MessageReceived);
}
[LuisIntent("Track package")]
public async Task TrackPackageAsync(IDialogContext context, LuisResult result)
{
await Task.CompletedTask;
context.ConversationData.SetValue("LuisResult", result);
context.Call(new GetPackageId(), AfterPackageIdForTrackingStatus);
}
private async Task AfterPackageIdForTrackingStatus(IDialogContext context, IAwaitable<string> result)
{
var packageId = await result;
if (!string.IsNullOrEmpty(packageId))
{
context.Call<object>(new DisplayPackageStatus(packageId), AfterDisplayPackageStatus);
}
else
{
await context.PostAsync("I'm sorry, but I cannot help you without a valid tracking Id.");
}
}
**最后,MessagesController.Post()**
public async Task<HttpResponseMessage> Post([FromBody]Activity activity)
{
if (activity.Type == ActivityTypes.Message)
{
await Conversation.SendAsync(activity, () => new Dialogs.RootDialog());
}
else
{
HandleSystemMessage(activity);
}
var response = Request.CreateResponse(HttpStatusCode.OK);
return response;
}