【问题标题】:Botframework V4: ID of active dialog not changing even when other dialogs are activeBotframework V4:即使其他对话框处于活动状态,活动对话框的 ID 也不会改变
【发布时间】:2019-04-10 06:16:08
【问题描述】:

我需要根据活动对话框的 ID 做一个条件,如下所示。

var dc = await _dialogs.CreateContextAsync(turnContext);
if (dc.ActiveDialog != null && dc.ActiveDialog.id == "SkipLuisDialog")
{
    var interruptedQnaMaker = await IsTurnInterruptedDispatchToQnAMakerAsync(turnContext, topDispatch, QnaConfiguration, cancellationToken);
}

当我像这样检查OnTurnAsync() 上的活动对话框时:

            if (dc.ActiveDialog != null)
            {
                await dc.Context.SendActivityAsync(dc.ActiveDialog.Id.ToString());
            }   

它总是说“mainDialog”,这是我的主对话框的 ID。即使即时在“FAQDialog”或“complaintDialog”上,活动对话 ID 始终是“mainDialog”。为什么当我在常见问题解答对话框中时,活动对话框 ID 没有更改为“FAQDialog”,或者当我在投诉对话框中时,活动对话框 ID 没有更改为“complaintDialog”?

我的主对话框和其他对话框在另一个类中。 这就是我在OnTurnAsync() 上调用mainDialog 的方式:

  if (!dc.Context.Responded)
            {
                switch (dialogResult.Status)
                {
                    case DialogTurnStatus.Empty:

                        switch (topIntent) // topIntent // text
                        {
                            case GetStartedIntent:
                                await dc.BeginDialogAsync(MainDialogId);
                                break;                          

                            case NoneIntent: 
                            default:
                                await dc.Context.SendActivityAsync("I didn't understand what you just said to me.");
                                break;
                        }

                        break;

                    case DialogTurnStatus.Waiting:
                        // The active dialog is waiting for a response from the user, so do nothing.
                        break;

                    case DialogTurnStatus.Complete:
                        await dc.EndDialogAsync();
                        break;

                    default:
                        await dc.CancelAllDialogsAsync();
                        break;
                }
            }

主对话框:

namespace CoreBot.Dialogs

{ 公共类 MainDialog : ComponentDialog { 私有常量字符串 InitialId = "mainDialog"; 私有常量字符串 TextPromptId = "textPrompt";

    private const string ComplaintDialogId = "complaintDialog";
    private const string FAQDialogId = "FAQDialog";

    public MainDialog(string dialogId) : base(dialogId)
    {
        WaterfallStep[] waterfallSteps = new WaterfallStep[]
        {
            FirstStepAsync,
            SecondStepAsync,
            ThirdStepAsync,

        };
        AddDialog(new WaterfallDialog(InitialId, waterfallSteps));
        AddDialog(new FAQDialog(FAQDialogId));
        AddDialog(new FileComplaintDialog(ComplaintDialogId));
        AddDialog(new TextPrompt(TextPromptId));
    }

    private static async Task<DialogTurnResult> FirstStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken = default(CancellationToken))
    {
        return await stepContext.PromptAsync(
            TextPromptId,
            new PromptOptions
            {
                Prompt = new Activity
                {
                    Type = ActivityTypes.Message,
                    Text = $"What can i do for you?",
                    SuggestedActions = new SuggestedActions()
                    {
                        Actions = new List<CardAction>()
                        {
                                new CardAction() { Title = "Frequently Asked Questions", Type = ActionTypes.ImBack, Value = "Frequently Asked Questions" },
                                new CardAction() { Title = "File a Complaint Ticket", Type = ActionTypes.ImBack, Value = "File a Complaint Ticket" },
                        },
                    },
                },
            });
    }

    private static async Task<DialogTurnResult> SecondStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken = default(CancellationToken))
    {
        var response = stepContext.Result.ToString().ToLower();

        string[] FAQArray = { "frequently asked questions", "question", "ask question" };
        string[] ComplaintsArray = { "file a complaint ticket", "complaint", "file a complaint" };

        if (FAQArray.Contains(response))
        {
            return await stepContext.BeginDialogAsync(FAQDialogId, cancellationToken);
        }

        if (ComplaintsArray.Contains(response))
        {
            await stepContext.EndDialogAsync();
            return await stepContext.BeginDialogAsync(ComplaintDialogId, cancellationToken: cancellationToken);
        }

        return await stepContext.NextAsync();
    }

    private static async Task<DialogTurnResult> ThirdStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken = default(CancellationToken))
    {
        return await stepContext.ReplaceDialogAsync(InitialId);
    }

}

}

主对话框调用 2 个对话框。这是其中之一。

    namespace CoreBot.Dialogs
{
    public class FAQDialog : ComponentDialog
    {
        private const string InitialId = "FAQDialog";
        private const string ChoicePromptId = "choicePrompt";
        private const string TextPromptId = "textPrompt";
        private const string ConfirmPromptId = "confirmPrompt";

        public FAQDialog(string dialogId) : base(dialogId)
        {
            WaterfallStep[] waterfallSteps = new WaterfallStep[]
            {
                FirstStepAsync,
                SecondStepAsync,
                ThirdStepAsync,
                FourthStepAsync,
            };
            AddDialog(new WaterfallDialog(InitialId, waterfallSteps));
            AddDialog(new ChoicePrompt(ChoicePromptId));
            AddDialog(new ConfirmPrompt(ConfirmPromptId));
            AddDialog(new TextPrompt(TextPromptId));

        }
        private static async Task<DialogTurnResult> FirstStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken = default(CancellationToken))
        {
            var choices = new List<Choice>();
            choices.Add(new Choice { Value = "This is Sample Question 1", Synonyms = new List<string> { "Question 1" } });
            choices.Add(new Choice { Value = "This is Sample Question 2", Synonyms = new List<string> { "Question 2" } });

            return await stepContext.PromptAsync(
                ChoicePromptId,
                new PromptOptions
                {
                    Prompt = MessageFactory.Text($"Welcome to FAQ! Choose the number of the question or type your own question."),
                    Choices = choices,
                });
        }

        private static async Task<DialogTurnResult> SecondStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken = default(CancellationToken))
        {
            var choiceResult = (stepContext.Result as FoundChoice).Value.ToLower();


            switch (choiceResult)
            {
                case "this is sample question 1":
                    await stepContext.Context.SendActivityAsync(MessageFactory.Text($"Answer to question 1."));
                    break;

                case "this is sample question 2":
                    await stepContext.Context.SendActivityAsync(MessageFactory.Text($"Answer to question 2."));
                    break;

                default:
                    break;
            }

            return await stepContext.NextAsync();
        }

        private static async Task<DialogTurnResult> ThirdStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken = default(CancellationToken))
        {
            return await stepContext.PromptAsync(
                ConfirmPromptId,
                new PromptOptions
                {
                    Prompt = MessageFactory.Text($"Have another question?"),
                });

        }

        private static async Task<DialogTurnResult> FourthStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken = default(CancellationToken))
        {
            var confirmResult = (bool)stepContext.Result;

            if (confirmResult)
            {
                await stepContext.Context.SendActivityAsync(MessageFactory.Text($"Ask away!"));
                return await stepContext.ReplaceDialogAsync(InitialId);
            }
            else
            {
                await stepContext.Context.SendActivityAsync(MessageFactory.Text($"Great!"));
                return await stepContext.EndDialogAsync();
            }
        }

    }
}

【问题讨论】:

    标签: c# botframework


    【解决方案1】:

    这是因为您的所有对话框都在 MainDialog 内调用。

    Dialog Stack Concept

    这或多或少是发生了什么:

    MyBot.cs(或任何你的主要.cs文件)中,你有类似Dialogs = new DialogSet(_dialogStateAccessor)的东西,它会创建一个空对话框stack

     ___________
    

    然后您可以使用 await dc.BeginDialogAsync(MainDialogId); 之类的东西,这会将 MainDialog 添加到对话框堆栈的顶部:

     ______________
    |  MainDialog  |
    |______________|
    

    如果您在 MainDialog 之外调用 return await stepContext.BeginDialogAsync(FAQDialogId, cancellationToken);,您的堆栈将如下所示:

     ______________
    |  FAQDialogId |
    |______________|
     ______________
    |  MainDialog  |
    |______________|
    

    但是,您从 MainDialog 中调用 return await stepContext.BeginDialogAsync(FAQDialogId, cancellationToken);,它有自己的堆栈,因此 FAQDialog 是 MainDialog 中的活动对话框,但您的 Dialogs 堆栈仍然将 MainDialog 作为 ActiveDialog。所以,你的堆栈或多或少是这样的:

     _______________________
    | ______________        |
    | |__FAQDialog__|       |
    |                       |
    |            MainDialog |
    |_______________________|
    

    做你想做的事

    你有几个选择:

    1.从MainDialog 之外调用所有其他对话框。

    如果您不打算回到MainDialog,您可以执行以下操作:

    MainDialog:

    return await stepContext.EndDialogAsync("FAQDialog");
    

    MyBot.cs:

    switch (dialogResult)
    {
        case "FAQDialog":
            dc.BeginDialogAsync(FAQDialogId, cancellationToken);
            break;
        case "ComplaintDialog":
            dc.BeginDialogAsync(ComplaintDialogId, cancellationToken);
            break;
    }
    

    2。在 ActiveDialog 的堆栈中挖掘对话框的 ID

    if (dc.ActiveDialog != null && IsDialogInStack(dc.ActiveDialog, "SkipLuisDialog"))
    {
        var interruptedQnaMaker = await IsTurnInterruptedDispatchToQnAMakerAsync(turnContext, topDispatch, QnaConfiguration, cancellationToken);
    }
    
    [...]
    
    private bool IsDialogInStack(DialogInstance activeDialog, string searchId)
    {
        if (activeDialog.Id == searchId)
        {
            return true;
        };
        foreach (KeyValuePair<string, object> item in activeDialog.State)
        {
            if (item.Key == "dialogs" && item.Value is DialogState)
            {
                DialogState dialogState = (DialogState)item.Value;
                foreach (DialogInstance dialog in dialogState.DialogStack)
                {
                    return IsDialogInStack(dialog, searchId);
                }
            }
        }
        return false;
    }
    

    注意:我不是 C# 专家,可能有更好的方法来编写 IsDialogInStack,但上面的代码可以正常工作。

    【讨论】:

    • 感谢您的视觉效果。真的很有帮助!
    • 没问题!我不确定你对堆栈有多熟悉,所以我想我应该包括它们,以防万一。
    猜你喜欢
    • 2019-09-16
    • 2011-05-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-29
    相关资源
    最近更新 更多