使用AlarmBot Sample和改进的 Sandwich Bot,我试图了解如何将 FormBuilder 与自定义对话框行为结合起来。具体来说,我想采用 Alarm Bot SetAlarm 方法
SetAlarm(IDialogContext context, LuisResult result)
并将 SandwichBot 中相同的 Chronic.Parser 用于 DeliveryTime 字段。那怎么做?我已经看过实现 IField 但我不确定它会是什么样子。
使用AlarmBot Sample和改进的 Sandwich Bot,我试图了解如何将 FormBuilder 与自定义对话框行为结合起来。具体来说,我想采用 Alarm Bot SetAlarm 方法
SetAlarm(IDialogContext context, LuisResult result)
并将 SandwichBot 中相同的 Chronic.Parser 用于 DeliveryTime 字段。那怎么做?我已经看过实现 IField 但我不确定它会是什么样子。
在查看并浏览源代码后,最简单的答案不是关于 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();
最初我们在 FormFlow 中将 Chronic 用于 DateTime 字段,但是当我们开始对 dll 进行签名时,我们无法再使用未签名的 C# Chronic 库。我已经联系了作者,请他签名,但还没有收到回复——如果他这样做了,我们会将其作为 FormFlow 的一部分。
至于实现 IField,如果您愿意,可以从 Field 派生并提供您自己的 IFieldState 实现。Field 具有所有声明性内容的数据结构,您可以覆盖所有方法。这将允许您在未签名的 DLL 中使用 Chronic。