Диалоговое окно возобновления работы каркаса не вызывается - PullRequest
1 голос
/ 28 февраля 2020


У меня проблема с диалогами компонентов в botframework V4.

Я использую диалог root, который содержит Waterfalldialog и Setupdialog. Начальный диалог - это Setupdialog. Примерно так:

public RootDialog(SetupDialog setupDialog)
            : base(nameof(RootDialog))
        {
            AddDialog(setupDialog);
            AddDialog(new TextPrompt(nameof(TextPrompt)));
            AddDialog(new WaterfallDialog(nameof(WaterfallDialog), new WaterfallStep[]
            {
                ProcessStep
            }));
            InitialDialogId = nameof(SetupDialog);
        }

В диалоге настройки я запрашиваю какое-то значение. Если диалоговые окна настройки продолжаются, я проверяю значение и, если оно мне нужно, я заканчиваю диалоговое окно.

public override Task<DialogTurnResult> ContinueDialogAsync(DialogContext outerDc, CancellationToken cancellationToken = default)
        {
            if (!int.TryParse(outerDc.Context.Activity.Text, out var recursions))
            {
                return outerDc.PromptAsync(nameof(TextPrompt), new PromptOptions()
                {
                    Prompt = MessageFactory.Text($"{outerDc.Context.Activity.Text} konnte nicht in eine Zahl konvertiert werden.")
                });
            }

            return outerDc.EndDialogAsync(recursions);
        }

Если я заканчиваю диалоговое окно, как это, разве не должен быть вызван ResumeDialog в RootDialog?

Здесь целые диалоги:

public class RootDialog : ComponentDialog
    {
        private int recursions;

        public RootDialog(SetupDialog setupDialog)
            : base(nameof(RootDialog))
        {
            AddDialog(setupDialog);
            AddDialog(new TextPrompt(nameof(TextPrompt)));
            AddDialog(new WaterfallDialog(nameof(WaterfallDialog), new WaterfallStep[]
            {
                ProcessStep
            }));
            InitialDialogId = nameof(SetupDialog);
        }

        public async Task<DialogTurnResult> ProcessStep(WaterfallStepContext stepContext, CancellationToken cancellationToken)
        {

            await stepContext.PromptAsync(nameof(TextPrompt), new PromptOptions
            {
                Prompt = MessageFactory.Text("Process step in root dialog")
            });

            if(Dialogs.Find(nameof(RecursiveDialog)) == null)
            {
                AddDialog(new RecursiveDialog(new DialogSet(), recursions));
            }

            if (recursions > 0)
            {
                return await stepContext.BeginDialogAsync(nameof(RecursiveDialog));
            }

            return await stepContext.PromptAsync(nameof(TextPrompt), new PromptOptions
            {
                Prompt = MessageFactory.Text("Recursion lower or eqaul 0")
            });
        }

        public override async Task<DialogTurnResult> BeginDialogAsync(DialogContext outerDc, object options = null, CancellationToken cancellationToken = default)
        {
            var dialogContext = CreateChildContext(outerDc);
            await dialogContext.PromptAsync(nameof(TextPrompt), new PromptOptions
            {
                Prompt = MessageFactory.Text("Begin root dialog")
            });

            return await base.BeginDialogAsync(outerDc, options, cancellationToken);
        }

        public override async Task<DialogTurnResult> OnContinueDialogAsync(DialogContext innerDc, CancellationToken cancellationToken = default)
        {
            if(innerDc.ActiveDialog != null)
            {
                return await innerDc.ContinueDialogAsync();
            }

            return await base.OnContinueDialogAsync(innerDc, cancellationToken);
        }

        public override async Task<DialogTurnResult> ResumeDialogAsync(DialogContext outerDc, DialogReason reason, object result = null, CancellationToken cancellationToken = default)
        {
            recursions = Convert.ToInt32(result);

            await outerDc.PromptAsync(nameof(TextPrompt), new PromptOptions
            {
                Prompt = MessageFactory.Text($"Resume root dialog with recursion value {recursions}")
            });

            return await outerDc.BeginDialogAsync(nameof(WaterfallDialog));
        }
    }

SetupDialog:

 public class SetupDialog : ComponentDialog
    {
        public SetupDialog()
            : base(nameof(SetupDialog))
        {
            AddDialog(new TextPrompt(nameof(TextPrompt)));
        }

        public override Task<DialogTurnResult> ContinueDialogAsync(DialogContext outerDc, CancellationToken cancellationToken = default)
        {
            if (!int.TryParse(outerDc.Context.Activity.Text, out var recursions))
            {
                return outerDc.PromptAsync(nameof(TextPrompt), new PromptOptions()
                {
                    Prompt = MessageFactory.Text($"{outerDc.Context.Activity.Text} konnte nicht in eine Zahl konvertiert werden.")
                });
            }

            return outerDc.EndDialogAsync(recursions);
        }

        public override Task<DialogTurnResult> OnBeginDialogAsync(DialogContext innerDc, object options, CancellationToken cancellationToken = default)
        {
            return innerDc.PromptAsync(nameof(TextPrompt), new PromptOptions()
            {
                Prompt = MessageFactory.Text("Wie viel rekursionen sollen erstellt werden?")
            });
        }
    }

1 Ответ

1 голос
/ 28 февраля 2020

Итак, я получил метод ResumeDialog для запуска.

Чтобы запустить метод ResumeDialog, диалоговое окно, которое закончилось, и диалоговое окно, к которому вы хотите возобновить, должно быть в одном стеке!

Мой сценарий был BotDialogContext [RootDialog] -> RootDialogContext [SetupDialog], но мне нужен такой контекст, как этот BotDialogContext [RootDialog, SetupDialog].

Одна из проблем заключается в том, что каждый запускаемый вами ComponentDialog создает свой собственный DialogContext. Поэтому, если вы начинаете диалог внутри диалога, он помещается в стек внутреннего DialogContext и так далее. Но описание метода ResumeDialog:

Вызывается, когда дочерний диалог в стеке родительского диалога завершил этот ход, возвращая управление этому компоненту диалога.

Чтобы поместить дочерний диалог в родительском стеке диалога, который вы должны были вызвать методом BeginDialog во внешнем диалоговом контексте. Этот контекст также должен иметь «дочерний диалог» в своем наборе диалогов.

Вот мой пример:

RootDialog.cs:

public class RootDialog : ComponentDialog
{
    private int recursions;

    public RootDialog(SetupDialog setupDialog)
        : base(nameof(RootDialog))
    {
        AddDialog(setupDialog);
        AddDialog(new TextPrompt(nameof(TextPrompt)));
        AddDialog(new WaterfallDialog(nameof(WaterfallDialog), new WaterfallStep[]
        {
            ProcessStep
        }));
        InitialDialogId = nameof(SetupDialog);
    }

    public async Task<DialogTurnResult> ProcessStep(WaterfallStepContext stepContext, CancellationToken cancellationToken)
    {

        await stepContext.PromptAsync(nameof(TextPrompt), new PromptOptions
        {
            Prompt = MessageFactory.Text("Process step in root dialog")
        });

        if(Dialogs.Find(nameof(RecursiveDialog)) == null)
        {
            AddDialog(new RecursiveDialog(new DialogSet(), recursions));
        }

        if (recursions > 0)
        {
            return await stepContext.BeginDialogAsync(nameof(RecursiveDialog));
        }

        return await stepContext.PromptAsync(nameof(TextPrompt), new PromptOptions
        {
            Prompt = MessageFactory.Text("Recursion lower or eqaul 0")
        });
    }

    public override async Task<DialogTurnResult> BeginDialogAsync(DialogContext outerDc, object options = null, CancellationToken cancellationToken = default)
    {
        if (true)
        {
            return await outerDc.BeginDialogAsync(nameof(SetupDialog));
        }

        var dialogContext = CreateChildContext(outerDc);
        await dialogContext.PromptAsync(nameof(TextPrompt), new PromptOptions
        {
            Prompt = MessageFactory.Text("Begin root dialog")
        });

        return await base.BeginDialogAsync(outerDc, options, cancellationToken);
    }

    protected override async Task<DialogTurnResult> OnContinueDialogAsync(DialogContext innerDc, CancellationToken cancellationToken = default)
    {
        if(innerDc.ActiveDialog != null)
        {
            return await innerDc.ContinueDialogAsync();
        }
        return await base.OnContinueDialogAsync(innerDc, cancellationToken);
    }

    public override async Task<DialogTurnResult> ResumeDialogAsync(DialogContext outerDc, DialogReason reason, object result = null, CancellationToken cancellationToken = default)
    {
        recursions = Convert.ToInt32(result);

        await outerDc.PromptAsync(nameof(TextPrompt), new PromptOptions
        {
            Prompt = MessageFactory.Text($"Resume root dialog with recursion value {recursions}")
        });

        return await outerDc.BeginDialogAsync(nameof(WaterfallDialog));
    }
}

SetupDialog.cs:

public class SetupDialog : ComponentDialog
{
    public SetupDialog()
        : base(nameof(SetupDialog))
    {
        AddDialog(new TextPrompt(nameof(TextPrompt)));
    }

    public override Task<DialogTurnResult> ContinueDialogAsync(DialogContext outerDc, CancellationToken cancellationToken = default)
    {
        if (!int.TryParse(outerDc.Context.Activity.Text, out var recursions))
        {
            return outerDc.PromptAsync(nameof(TextPrompt), new PromptOptions()
            {
                Prompt = MessageFactory.Text($"{outerDc.Context.Activity.Text} konnte nicht in eine Zahl konvertiert werden.")
            });
        }

        return outerDc.EndDialogAsync(recursions);
    }

    public override Task<DialogTurnResult> BeginDialogAsync(DialogContext outerDc, object options = null, CancellationToken cancellationToken = default)
    {
        return base.BeginDialogAsync(outerDc, options, cancellationToken);
    }

    protected override Task<DialogTurnResult> OnBeginDialogAsync(DialogContext innerDc, object options, CancellationToken cancellationToken = default)
    {
        return innerDc.PromptAsync(nameof(TextPrompt), new PromptOptions()
        {
            Prompt = MessageFactory.Text("Wie viel rekursionen sollen erstellt werden?")
        });
    }
}

DialogBot.cs:

public class DialogBot<T> : ActivityHandler
    where T : Dialog
{
    protected readonly DialogSet Dialogs;
    protected readonly BotState ConversationState;
    protected readonly BotState UserState;
    protected readonly ILogger Logger;

    public DialogBot(ConversationState conversationState, UserState userState, IEnumerable<Dialog> dialogs, ILogger<DialogBot<T>> logger)
    {
        ConversationState = conversationState;
        UserState = userState;
        Logger = logger;

        Dialogs = new DialogSet(conversationState.CreateProperty<DialogState>(nameof(DialogState)));
        foreach(var dialog in dialogs)
        {
            Dialogs.Add(dialog);
        }
    }

    public override async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default(CancellationToken))
    {
        await base.OnTurnAsync(turnContext, cancellationToken);

        // Save any state changes that might have occured during the turn.
        await ConversationState.SaveChangesAsync(turnContext, false, cancellationToken);
        await UserState.SaveChangesAsync(turnContext, false, cancellationToken);
    }

    protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
    {
        Logger.LogInformation("Running dialog with Message Activity.");

        var dc = await Dialogs.CreateContextAsync(turnContext, cancellationToken).ConfigureAwait(false);

        if (dc.ActiveDialog != null)
        {
            await dc.ContinueDialogAsync();
        }
        else
        {
            // Run the Dialog with the new message Activity.
            await dc.BeginDialogAsync(typeof(T).Name, ConversationState.CreateProperty<DialogState>("DialogState"), cancellationToken);
        }
    }
}

Внутри IEnumerable находятся оба (RootDialog & SetupDialog) для получения обоих диалогов в BotDialogContext и DialogSet

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...