如何使用C#的NumberPrompt实现多轮用户输入问询(解决循环问题)
多问题逐个提问的解决方案(基于Bot Framework NumberPrompt)
我需要从数据库获取多个问题,然后逐个向用户提问。用for循环的话,所有问题会一次性发出去,没法等用户输入答案;用return虽然能等用户输入,但会终止循环。我不知道怎么用NumberPrompt处理这种需要多次用户输入的场景,原本打算在for循环里写这段代码,但循环没法等输入,return又会停掉循环:
var showQuestionMessageText = $"Here's your question: {ConnectDatabase.question[i]}"; var showQuestionMessage = MessageFactory.Text(showQuestionMessageText, showQuestionMessageText, InputHints.IgnoringInput); await stepContext.Context.SendActivityAsync(showQuestionMessage, cancellationToken); var showOptionsMessageText = $"Here's your options: \n1. {ConnectDatabase.option1[i]} \n2. {ConnectDatabase.option2[i]} \n3. {ConnectDatabase.option3[i]} \n4. {ConnectDatabase.option4[i]}"; var showOptionsMessage = MessageFactory.Text(showOptionsMessageText, showOptionsMessageText, InputHints.IgnoringInput); await stepContext.Context.SendActivityAsync(showOptionsMessage, cancellationToken); return await stepContext.PromptAsync(nameof(NumberPrompt<int>), new PromptOptions { Prompt = MessageFactory.Text("Please choose one of the options, using 1-4 to type in your answer")}, cancellationToken);
别用for循环,改用Bot Framework的**对话状态(Dialog State)**跟踪当前进度,配合瀑布流对话框(Waterfall Dialog)分步处理。核心思路是把问题列表、当前问题索引、用户答案都存在对话状态里,每处理完一个问题就更新索引,然后回到提问步骤,直到所有问题问完。
1. 定义对话状态类
先创建类存储对话过程中的数据:
public class QuestionnaireState { // 存储从数据库获取的问题集合 public List<Question> Questions { get; set; } // 当前处理的问题索引 public int CurrentQuestionIndex { get; set; } // 存储用户的答案(键为问题索引,值为用户选择的选项编号) public Dictionary<int, int> UserAnswers { get; set; } = new Dictionary<int, int>(); } // 自定义问题结构,对应数据库字段 public class Question { public string Text { get; set; } public string Option1 { get; set; } public string Option2 { get; set; } public string Option3 { get; set; } public string Option4 { get; set; } }
2. 初始化对话状态并加载问题
在瀑布流第一步,从数据库加载所有问题并初始化状态:
private async Task<DialogTurnResult> LoadQuestionsStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken) { // 替换为你的数据库查询逻辑,获取所有问题 var questions = await ConnectDatabase.GetAllQuestionsAsync(); // 将问题存入对话状态 var questionnaireState = await _conversationState .CreateProperty<QuestionnaireState>(nameof(QuestionnaireState)) .GetAsync(stepContext.Context, () => new QuestionnaireState()); questionnaireState.Questions = questions; questionnaireState.CurrentQuestionIndex = 0; // 进入提问步骤 return await stepContext.NextAsync(null, cancellationToken); }
3. 提问步骤(发送问题+选项,等待用户输入)
负责发送当前问题和选项,调用NumberPrompt获取用户答案:
private async Task<DialogTurnResult> AskQuestionStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken) { var questionnaireState = await _conversationState .CreateProperty<QuestionnaireState>(nameof(QuestionnaireState)) .GetAsync(stepContext.Context); var currentIndex = questionnaireState.CurrentQuestionIndex; // 所有问题处理完毕,结束对话 if (currentIndex >= questionnaireState.Questions.Count) { await stepContext.Context.SendActivityAsync("所有问题已回答完毕!", cancellationToken: cancellationToken); return await stepContext.EndDialogAsync(questionnaireState.UserAnswers, cancellationToken); } var currentQuestion = questionnaireState.Questions[currentIndex]; // 发送问题文本 var questionText = $"问题 {currentIndex + 1}: {currentQuestion.Text}"; await stepContext.Context.SendActivityAsync( MessageFactory.Text(questionText, questionText, InputHints.IgnoringInput), cancellationToken); // 发送选项列表 var optionsText = $"可选答案:\n1. {currentQuestion.Option1} \n2. {currentQuestion.Option2} \n3. {currentQuestion.Option3} \n4. {currentQuestion.Option4}"; await stepContext.Context.SendActivityAsync( MessageFactory.Text(optionsText, optionsText, InputHints.IgnoringInput), cancellationToken); // 调用NumberPrompt等待用户输入 return await stepContext.PromptAsync(nameof(NumberPrompt<int>), new PromptOptions { Prompt = MessageFactory.Text("请输入1-4之间的数字选择答案", inputHint: InputHints.ExpectingInput) }, cancellationToken); }
4. 处理用户答案的步骤
存储用户答案,更新索引,回到提问步骤继续下一个问题:
private async Task<DialogTurnResult> ProcessAnswerStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken) { var questionnaireState = await _conversationState .CreateProperty<QuestionnaireState>(nameof(QuestionnaireState)) .GetAsync(stepContext.Context); var currentIndex = questionnaireState.CurrentQuestionIndex; // 存储用户选择的答案(stepContext.Result为NumberPrompt返回的int值) var userAnswer = (int)stepContext.Result; questionnaireState.UserAnswers[currentIndex] = userAnswer; // 更新索引,准备下一个问题 questionnaireState.CurrentQuestionIndex++; // 回到提问步骤 return await stepContext.NextAsync(null, cancellationToken); }
5. 注册对话框和状态
在Bot构造函数中注册对话状态、NumberPrompt和瀑布流对话框:
private readonly ConversationState _conversationState; private readonly DialogSet _dialogs; public QuestionnaireBot(ConversationState conversationState) { _conversationState = conversationState; var dialogState = _conversationState.CreateProperty<DialogState>(nameof(DialogState)); _dialogs = new DialogSet(dialogState); // 注册NumberPrompt,添加输入验证(只接受1-4的数字) _dialogs.Add(new NumberPrompt<int>(nameof(NumberPrompt<int>), async (promptContext, cancellationToken) => { if (!promptContext.Recognized.Succeeded) { await promptContext.Context.SendActivityAsync("请输入有效的数字!", cancellationToken: cancellationToken); return false; } var answer = promptContext.Recognized.Value; if (answer < 1 || answer > 4) { await promptContext.Context.SendActivityAsync("请输入1-4之间的数字!", cancellationToken: cancellationToken); return false; } return true; })); // 注册瀑布流对话框,按顺序添加步骤 _dialogs.Add(new WaterfallDialog("QuestionnaireDialog", new WaterfallStep[] { LoadQuestionsStepAsync, AskQuestionStepAsync, ProcessAnswerStepAsync })); }
启动对话框
在OnTurnAsync方法中启动问卷对话框:
public async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default) { if (turnContext.Activity.Type == ActivityTypes.Message) { var dc = await _dialogs.CreateContextAsync(turnContext, cancellationToken); var results = await dc.ContinueDialogAsync(cancellationToken); // 无活跃对话框时,启动问卷流程 if (results.Status == DialogTurnStatus.Empty) { await dc.BeginDialogAsync("QuestionnaireDialog", cancellationToken: cancellationToken); } // 保存对话状态 await _conversationState.SaveChangesAsync(turnContext, false, cancellationToken); } }
内容的提问来源于stack exchange,提问作者Coder2656
相关产品推荐
相关产品推荐

