Microsoft Bot Framework中的FormBuilder自定义字段

时间:2016-04-01 17:01:15

标签: c# botframework

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

SetAlarm(IDialogContext context, LuisResult result)

并在SandwichBot中使用相同的Chronic.Parser作为DeliveryTime字段。怎么办?我已经看过实施IField,但我不确定它会是什么样子。

2 个答案:

答案 0 :(得分:2)

查看并逐步查看源代码后,最简单的答案与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));
      }
    }
}

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

接下来,您可以创建自己的自定义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();

答案 1 :(得分:1)

最初我们在FormFlow中使用Chronic for DateTime字段,但是当我们转移到签署我们的dll时,我们再也无法使用未签名的C#Chronic库。我已经联系了作者但要求他签名,但还没有回复 - 如果他这样做,我们会把它作为FormFlow的一部分。

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