【问题标题】:Custom fields with FormBuilder in the Microsoft Bot FrameworkMicrosoft Bot Framework 中使用 FormBuilder 的自定义字段
【发布时间】:2017-08-16 14:03:21
【问题描述】:

使用AlarmBot SampleImproved Sandwich Bot,我试图了解如何将FormBuilder 与自定义对话框行为结合起来。具体来说,我想采用Alarm Bot SetAlarm方法

SetAlarm(IDialogContext context, LuisResult result)

并将 SandwichBot 中的相同 Chronic.Parser 用于 DeliveryTime 字段。那怎么做?我已经看过实现 IField 但我不确定它会是什么样子。

【问题讨论】:

    标签: c# botframework


    【解决方案1】:

    在查看并逐步浏览源代码后,最简单的答案不是关于 IFieldState。关键接口是 IRecognize。正确使用它需要稍微了解幕后发生的事情。

    首先创建您自己的自定义字段。该框架很好地允许我们从完成大部分工作的 FieldReflector 派生。

    public class BetterDateTimeField : FieldReflector<MyOrder> 
    {
      public BetterDateTimeField(string name, bool ignoreAnnotations = false) 
              : base(name, ignoreAnnotations)  { }
    
       public override IForm<MyOrder> Form
       {
          set
          {
            base.Form = value;
            base.SetRecognizer(new BetterDateTimeRecognizer<MyOrder>(this, CultureInfo.CurrentCulture));
          }
        }
    }
    

    这里的主要思想是创建自己的识别器,因为这是获取原始输入文本的部分。诀窍是知道何时可以创建识别器的实例。必须在设置字段表单之后完成。基础识别器基类将查看构造函数中的字段表单。 (如果您完全放弃使用基础识别器类,这可能不是问题。)

    接下来,您可以创建自己的自定义 IRecognize 实现。不幸的是,机器人框架密封了许多基本/原始类型识别器类,因此从 RecognizeDateTime 派生和重载 Parse 不是一种选择(希望有一天他们会解封)。但是,复制和编辑到您自己的自定义类中很容易。

    using Chronic;
    public class BetterDateTimeRecognizer<T> : RecognizePrimitive<T> where T : class
    {
       private CultureInfo _culture;
       private Parser _parser;
    
       public BetterDateTimeRecognizer(IField<T> field, CultureInfo culture) 
              : base(field)
       {
          _culture = culture;
          _parser = new Chronic.Parser();
       }
    
       public override string Help(T state, object defaultValue)
       {
         var prompt = new Prompter<T>(_field.Template(TemplateUsage.DateTimeHelp), _field.Form, null);
         var args = HelpArgs(state, defaultValue);
         return prompt.Prompt(state, _field.Name, args.ToArray());
    
       }
    
       public override TermMatch Parse(string input)
       {
         TermMatch match = null;
         // the original code
         //DateTime dt;
         //if (DateTime.TryParse(input, out dt))
         //{
         //    match = new TermMatch(0, input.Length, 1.0, dt);
         //}
    
         var parse = _parser.Parse(input);
         if (parse != null && parse.Start.HasValue)
         {
             match = new TermMatch(0, input.Length, 1.0, parse.Start.Value);
         }
    
         return match;
      }
    
      public override IEnumerable<string> ValidInputs(object value)
      {
         yield return ValueDescription(value);
      }
    
      public override string ValueDescription(object value)
      {
         return ((DateTime)value).ToString(CultureInfo.CurrentCulture.DateTimeFormat);
      }
    }
    

    最后,您只需将其连接到 BuildForm() 中的表单构建器:

    var form = builder.Message("Hello there. What can I help you today")
               .Field(new BetterDateTimeField("<NAME of YOUR DateTime Field HERE>")
               .Build();
    

    【讨论】:

    • 我还应该补充一点,原始字段可能是 BetterDateTimeField。这可能同样有效,并且是更通用的解决方案。
    【解决方案2】:

    最初我们在 FormFlow 中将 Chronic 用于 DateTime 字段,但是当我们开始对 dll 进行签名时,我们无法再使用未签名的 C# Chronic 库。我已经联系了作者,请他签名,但还没有收到回复——如果他这样做了,我们会将其作为 FormFlow 的一部分。

    关于实现 IField,如果您愿意,可以从 Field 派生并提供您自己的 IFieldState 实现。 Field 具有所有声明性内容的数据结构,您可以覆盖所有方法。这将允许您在未签名的 DLL 中使用 Chronic。

    【讨论】:

    • 它似乎比这更复杂。派生似乎需要设置提示和其他不清楚的项目(即使使用调试器单步执行源代码)。
    猜你喜欢
    • 2016-09-05
    • 2022-08-19
    • 1970-01-01
    • 2018-12-29
    • 2018-04-14
    • 1970-01-01
    • 1970-01-01
    • 2017-11-25
    相关资源
    最近更新 更多