基于C#的Microsoft Bot Framework:FormFlow后实现多输入循环问询
嘿,我懂你现在的困扰——FormFlow走完后要循环问用户一堆属性还得存下来,光看官方瀑布流教程确实容易摸不着头绪。其实把FormFlow和瀑布流(Waterfall Dialog)结合起来就能完美解决,我给你捋个可行的实现方案,代码都给你写好啦:
整体思路
核心逻辑是:FormFlow流程完成后,跳转到一个专门处理循环问询的瀑布流Dialog。我们用一个状态类来追踪当前询问到第几个属性、已收集的回答,再通过瀑布流的步骤循环调用自己,直到所有属性都收集完毕。
具体实现步骤
1. 定义待询问的属性集合
先把你需要问的属性列出来,比如:
// 替换成你实际需要询问的属性名称 private readonly List<string> _requiredUserProperties = new List<string> { "项目编号", "文件描述", "联系电话" };
2. 修改FormFlow的OnCompletion逻辑
让FormFlow完成后自动跳转到循环问询的瀑布流,同时传递当前的UploadFile状态:
return new FormBuilder<UploadFile>() .OnCompletion(async (context, state) => { // 先执行你原来的processOrder逻辑 await processOrder(context, state); // 初始化循环状态,跳转到循环问询Dialog var loopState = new PropertyLoopTrackingState { UploadState = state, CurrentPropertyIndex = 0 }; await context.BeginDialogAsync(nameof(PropertyLoopDialog), loopState); }) // 你原来的Field定义保持不变 .Field(new FieldReflector<UploadFile>(nameof(UploadFile.Str)) .SetType(null) .SetDefine((state, field) => { foreach (string str in listStr) field .AddDescription(str, str) .AddTerms(str, str); return Task.FromResult(true); }) .SetNext((value, state) => { // 这里保留你原来的保存逻辑 return new NextStep(); })) .Build();
3. 创建循环状态追踪类
这个类用来保存循环过程中的所有关键数据,避免状态丢失:
// 用于追踪循环问询的状态 public class PropertyLoopTrackingState { // 保存FormFlow阶段的UploadFile状态 public UploadFile UploadState { get; set; } // 当前询问到第几个属性 public int CurrentPropertyIndex { get; set; } // 存储用户的所有回答 public Dictionary<string, string> CollectedAnswers { get; set; } = new Dictionary<string, string>(); }
4. 实现循环问询的瀑布流Dialog
这个Dialog是核心,通过两步实现循环:第一步询问当前属性,第二步保存回答并判断是否继续循环:
public class PropertyLoopDialog : WaterfallDialog { private readonly List<string> _propertiesToAsk; public PropertyLoopDialog(string dialogId, List<string> propertiesToAsk) : base(dialogId) { _propertiesToAsk = propertiesToAsk; // 第一步:检查是否还有属性要问,若有则发起询问 AddStep(async (stepContext, cancellationToken) => { var loopState = stepContext.Options as PropertyLoopTrackingState; // 所有属性都问完了,结束循环 if (loopState.CurrentPropertyIndex >= _propertiesToAsk.Count) { await stepContext.Context.SendActivityAsync("所有信息已收集完成!"); // 这里可以把CollectedAnswers和UploadState合并处理,比如保存到数据库 return await stepContext.EndDialogAsync(loopState); } // 获取当前要询问的属性 var currentProp = _propertiesToAsk[loopState.CurrentPropertyIndex]; await stepContext.Context.SendActivityAsync($"请输入{currentProp}:"); // 用文本Prompt接收用户输入,需要提前注册这个Prompt return await stepContext.PromptAsync(nameof(TextPrompt), new PromptOptions()); }); // 第二步:保存用户回答,更新索引,重新进入循环 AddStep(async (stepContext, cancellationToken) => { var loopState = stepContext.Options as PropertyLoopTrackingState; var currentProp = _propertiesToAsk[loopState.CurrentPropertyIndex]; // 保存当前属性的回答 loopState.CollectedAnswers[currentProp] = (string)stepContext.Result; // 索引加1,准备询问下一个属性 loopState.CurrentPropertyIndex++; // 替换当前Dialog,重新执行第一步,实现循环 return await stepContext.ReplaceDialogAsync(nameof(PropertyLoopDialog), loopState); }); } }
5. 注册Dialog到Bot
在Bot的初始化代码里(比如构造函数或ConfigureServices),把循环Dialog和文本Prompt注册进去:
// 注册循环问询Dialog Dialogs.Add(new PropertyLoopDialog(nameof(PropertyLoopDialog), _requiredUserProperties)); // 注册文本Prompt,用于接收用户输入 Dialogs.Add(new TextPrompt(nameof(TextPrompt)));
关键细节说明
- 循环实现:用
ReplaceDialogAsync代替BeginDialogAsync,避免Dialog栈无限叠加,同时传递更新后的状态,实现循环逻辑。 - 状态管理:
PropertyLoopTrackingState统一管理所有状态,确保在循环过程中不会丢失FormFlow的数据和已收集的回答。 - 灵活扩展:如果需要询问不同类型的输入(比如数字、日期),可以把
TextPrompt换成对应的NumberPrompt、DateTimePrompt,还能给Prompt添加验证逻辑(比如检查电话格式)。
内容的提问来源于stack exchange,提问作者MaLa
相关产品推荐
相关产品推荐

