Botframework v4:从子对话框调用父对话框,而没有stackoverflow异常

时间:2019-04-01 06:17:10

标签: c# botframework

根据this,我认为现在可以从子对话框中调用父对话框了。在我不能这样做之前,因为它将导致堆栈溢出异常。我已经更新到SDK 4.3,有人知道如何实现此更改吗?

主对话框调用对话框A。

WaterfallStep[] waterfallSteps = new WaterfallStep[]
     {
        FirstStepAsync,
        SecondStepAsync,
        ThirdStepAsync,
     };
    AddDialog(new WaterfallDialog(InitialId, waterfallSteps));
    AddDialog(new DialogA(DialogAId));

  return await stepContext.BeginDialogAsync(DialogAId, cancellationToken: cancellationToken);

对话框A调用对话框Achild

    WaterfallStep[] waterfallSteps = new WaterfallStep[]
     {
        FirstStepAsync,
        SecondStepAsync,
        ThirdStepAsync,
     };
    AddDialog(new WaterfallDialog(InitialId, waterfallSteps));
    AddDialog(new DialogAchild(DialogAchildId));

   return await stepContext.BeginDialogAsync(DialogAchildId, cancellationToken: cancellationToken);

Dialog Achild调用MainDialog,但这会产生堆栈溢出异常。

WaterfallStep[] waterfallSteps = new WaterfallStep[]
     {
        FirstStepAsync,
        SecondStepAsync,
        ThirdStepAsync,
     };
    AddDialog(new WaterfallDialog(InitialId, waterfallSteps));
    AddDialog(new MainDialog(MainDialogId));

  return await stepContext.BeginDialogAsync(MainDialogId, cancellationToken: cancellationToken);

1 个答案:

答案 0 :(得分:0)

假设您要在对话框的构造函数中添加对话框,那么您将产生一个无限循环,最终导致所描述的堆栈溢出。

MainDialog --> DialogA --> DialogAchild --> MainDialog --> infinite loop

您提到的PR是一个稍微不同的问题。

解决此问题的一种方法是从构造函数中删除导致最终循环的AddDialog方法。而是将调用移至类似AddDialogA()的方法,并仅在需要时调用它。

根据您的情况,您可以构建一个提供此类功能的基本对话框。

以下是AuthenticatedDialog的示例,可让您在必要时添加OnboardingDialog。请注意,“载入对话框”本身是从AuthenticatedDialog继承的,当您不卸载AddDialog()调用时,也会导致无限循环。

在基本对话框中对此进行抽象很不错,因为它为您提供了一些可以使用的API。考虑将您的东西命名为AddComponentDialog或UseComponentDialog。这样,您就可以很好地表达自己的意图,并且潜在的读者可以一开始就知道您正在使用可重复使用的组件。

AuthenticatedDialog.cs

using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Schema;
using Bot.Dialogs.Authentication;
using Bot.Dialogs.Main;
using Bot.Dialogs.Onboarding;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace Bot.Dialogs.Shared
{
    public class AuthenticatedDialog : EnterpriseDialog
    {
        private BotStateAccessors _accessors;
        private BotServices _botServices;

        public AuthenticatedDialog(BotServices botServices, string dialogId, BotStateAccessors accessors) : base(botServices, dialogId)
        {
            _accessors = accessors;
            _botServices = botServices;

            AddDialog(new AuthenticationDialog("", accessors));
        }

        protected async Task<DialogTurnResult> AskForOnboardingAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken, object stepResult = null)
        {
            return await stepContext.BeginDialogAsync(nameof(OnboardingDialog), stepResult, cancellationToken);
        }

        protected void AddOnboardingDialog()
        {
            AddDialog(new OnboardingDialog(_botServices,_accessors));
        }

    }
}

DialogA.cs

public class DialogA : AuthenticatedDialog
{

        public DialogA(BotServices botServices, BotStateAccessors accessors) : base(botServices, nameof(DialogA), accessors)
        {

            var preferencesDispatchSteps = new WaterfallStep[]
            {
                WaterfallStepA,
                WaterfallStepB
            };

            AddDialog(new FooDialog);
            AddOnboardingDialog();
        }
}