孜孜不倦的程序员

Oak 入门:数据验证和结束语

Ted Neward

Ted Neward对于现在的三个列,我一直在探索的橡木为 Web 应用程序空间,带来的"动态-y"对象方法,它一直骑一个有趣,完成着长期持有的信念有关 Web 应用程序需要如何生成 (或有关的平台,他们被建) 的几个挑战。 但每个骑了某个时候,来结束,是时候来总结我的橡树的探索。 我要弄清楚如何确保数据放入系统的用户是其实不错的数据,对于初学者。

但第一次......

评注

如果你回去和我走的了,上次看系统,试图添加一个注释产量的那些乐于助人的错误,另一个通知您"Blog.Controllers.Blog 不包含一个定义为 'AddComment'"这次。相反在静态类型化的系统中可能会发生什么 — — 一个编译错误在前门的编译,部署,运行周期并缺乏的这种方法会绊倒 — — 直到他们实际上企图不会看到在动态系统中,这些错误。 一些动态语言支持者声称这是动态语言的魅力的一部分,当然不必担心让一切一致保持整个系统可以是一件幸事,头脑是着火的想法和你只需要把这一想法出来到世界时。 但我知道谁做了一个大于典型的 Todo 列表中应用程序的项目大多数红宝石对 Rails 开发人员将首先承认在动态语言中的应用,全面的测试是保持项目的质量高和强大的开发人员的理智的关键。 所以测试都必须与橡木任何认真的努力的一部分。

不幸的是,当我开始谈论测试,我开始进入讨论 (单元测试、 性能测试、 集成测试、 聊发展等等),可以轻松地消耗自己,另一个打杂志问题的几个领域和我不想破解这的潘多拉的盒子。 不管您测试方法或偏好,我只想说你必须有某种测试存在橡木应用程序中 (或任何应用程序,但必须是在任何环境中动态类型化高得多),但是你选择要测试。

同时,AddComment 方法是仍然失踪。

注释掉

在这特定的情况下,当用户键入注释到视图,它职位给 HomeController 的评论方法,看上去像这样:

[HttpPost]
public ActionResult Comments(dynamic @params)
{
  dynamic blog = blogs.Single(@params.BlogId);
  blog.AddComment(@params);
  return RedirectToAction("Index");
}

你可以看到,控制器第一次获得来自窗体上,然后用它来在 DynamicRepository 上找到相应的博客条目通过单个方法的 BlogId 参数中藏的博客 ID 然后调用博客。AddComment 来存储该注释。 (再次,只是想做点,"赞成"和"反对":此代码自本系列的第二部分以来一直在地方和我只是现在跑到事实直到现在还没有存在的 AddComment 方法)。

定义此方法是相当简单 ; 关于博客类中,添加此方法:

void AddComment(dynamic comment)
{
  // Ignore addition if the body is empty
  if (string.IsNullOrEmpty(comment.Body)) return;
  // Any dynamic property on this instance can be accessed
  // through the "_" property
  var commentToSave = _.NewComment(comment);
  comments.Insert(commentToSave);
}

在此方法中唯一真正的问号是使用下划线 (_。NewComment(comment)),是"本"参考的占位符。 下划线已充分认识到的动态性质的此对象,也不会"本"参考 ; 不必担心差异,下划线允许您使用"这个"会,再加上更多的一切。

请注意如何系统的动态性质允许您在代码中非常节俭。 在命名捆绑在"@params"来控制器中,捕获窗体参数和那些传递而不打开其中的任何包装直接进入添加­发表评论,这反过来将它们传递到 NewComment,构造出他们一个动态对象,并将所产生的对象只是获取插入 DynamicRepository 的评论。 对象的属性的名称从何而来? 从起源的这所有的 HTML 窗体。

奇怪的是吧吗? 几乎感觉你是懒人或东西。

无论如何,都要给它运行,并确保有足够,现在评论正在添加中。

验证我

写到,不过,系统有一个重大缺陷 (好吧,根据用户的要求,无论如何):它是完全允许的两个博客条目有确切相同的标题,并不是确定。 (读者可能会感到迷惑读哪一个,一个,标题为"LOL Kittehs"或另一个标题为"LOL Kittehs")因此,您需要执行某种独特性的代码,所以用户不能意外地把放在重复标题为博客条目的方式。

在传统的 Web 框架,这将是一个两部分工作。 第一,模型对象 (Blog) 将必须定义某种"问我是否我有效"的方法,就没有什么真正的原始打 IsValid 和一个定义中的对象,我会打电话给验证说验证。 作为一个可能会怀疑从同伙方法工作来帮助定义 Blog 和发表评论的对象 (即,它为其橡树知道来看看一个预定义的名称) 之间的联系的方式,该验证方法的工作相同的方式 — — 如果一个对象定义了一种验证方法,然后橡树将调用对象,反过来将寻找一种验证方法,问它使此对象有效的所有条件的已定义的 IsValid 方法。

在代码中,这看起来像这样:

IEnumerable<dynamic> Validates()
{
  // And define the association
  // For other examples of validations, check out the Oak wiki
  yield return new Uniqueness("Name", blogs);
}

再次,你看到一个流对象描述的验证要求,交回作为 IEnumerable < 动态 > 的使用 到流中,生成的 C# 的"收益率回归"设施使用。 从最后一次的架构类,来扩展这是只是跨行"产量返回,"的额外元素就像这样:

IEnumerable<dynamic> Validates()
{
  // And define the association
  // For other examples of validations check out the Oak wiki
  yield return new Uniqueness("Name", blogs);
  yield return new Length("Name") { Minimum=10, Maximum=199 };
}

橡木 Wiki 定义的完整列表和验证对象的用法,但一些值得注意的是:

  • 存在:一个字段不是可选的并且必须是对象的存在。
  • 接受:对象上的字段必须包含一个特定的值,如 LegalDocument 对象,该对象包含一个 TypedOutAcceptance 字段在用户键入字符串"我接受",表明他接受了法律的限制。
  • 排除:字段不能包含某些值。
  • 列入:字段必须是一套的某些值。
  • 格式:通用正则表达式 (使用 Microsoft.NET 框架 Regex 类) 验证。
  • Numericality:几乎一切都要与数值包括标记字段为"整数仅,"做更大-­大于和小于-比的限制或奇偶的简单测试。
  • 条件:全部捕获的任何验证的"逃生舱口"不涵盖其他地方 — — 一个字段必须满足的条件描述使用 lambda 函数。

最后一个条件,不是实际上验证类型中和的本身,但功能上 (如果不是全部) 的大多数其他验证类型,目前和因此有点值得更多的解释。 想象一下一个命令对象,在传统的电子商务系统中的订单。 为表示系统、 信用卡卡号只是必要的如果用户想要使用一张信用卡进行付款。 同样,要运到的产品只是必要的如果用户购买数字下载之外的任何地址。 这些两个意外情况则整齐地使用表示两个条件验证,如中所示图 1

图 1 条件验证

public class Order : DynamicModel
{
  public Order()
  {
  }
  public IEnumerable<dynamic> Validates()
  {
    yield return new Presence("CardNumber") {
      If = d => d.PaidWithCard()
    };
    yield return new Presence("Address") {
      Unless = d => d.IsDigitalPurchase()
    };
  }
  public bool PaidWithCard()
  {
    // Could use This().PaymentType instead
    return _.PaymentType == "Card";
  }
  public bool IsDigitalPurchase()
  {
    // Could use This().ItemType instead
    return _.ItemType == "Digital";
  }
}

每个条件对象正在使用的对象的属性的存在 — — 以及产生一个真/假值 lambda — — 以指示是否存在验证成功。 在第一个案件中,存在则返回 true (通行证) 如果 d.PaidWithCard,一个本地的方法,如果 PaymentType 字段等于"一卡通,"则返回 true,则返回 true。 在第二个案件中,存在返回 true 除非 isDigitalPurchase 返回 true,意思如果它是一个数字的项目,没有地址必要。

所有这些都是准备好与橡木 DynamicModel 派生的任何对象,使用和,如指出事先列中 (msdn.microsoft.com/magazine/dn519929),在介绍这一个,DynamicModel-­派生的对象不必显式定义的字段,这些验证引用。 这些验证功能不应足够的任务,顺便说一句,这些是所有定义的基架项目橡木文件夹内的 Validations.cs 文件中。 它是定义一个新,如果需要很简单:只是从 Oak.Validation 继承并定义至少一种验证方法,返回真/假。 排斥验证,例如,是这样的:

public class Exclusion : Validation
{
  public Exclusion(string property)
    : base(property)
  {
  }
  public dynamic[] In { get; set; }
  public bool Validate(dynamic entity)
  {
    return !In.Contains(PropertyValueIn(entity) as object);
  }
}

在此代码中的属性是被排除在外的值存储 ; 的域 除此之外,这是相当简单的。 如果需要包含描述性错误消息,验证提供了一个基本的属性,错误信息,在其中的描述性消息可以存储为使用如果验证失败。

(对于那些好奇的"协会"从数据库讨论最后一次,这些定义在同一文件夹中的 Association.cs、 Oak.Association,从派生和 — — 正如你可能期望 — — 是有点难度的解释。 幸运的是,橡树拥有的大多数传统关系协会已经定义了,所以不应该会有很多需要在这里定制。)

橡树的片断

有时一个图书馆的部分看起来很酷,但障碍站不能采用整件事,和你只是希望你能挖出它的一小部分和继续前进。 时,通常最好使用橡木作为一个整体,它不支持的撕扯着它的位想法 (如动态数据库部分或也许只是动态对象的某些部分,称为双子座,我被盖在 2013 年 8 月问题,在 msdn.microsoft.com/magazine/dn342877) 和使用他们的独立,没有系统的其余部分。 主题上的橡树 GitHub 页 (bit.ly/1cjGuou) 针对每个独立橡木有 NuGet 包部件,为您提供便利转载 (写这篇文章):

  • 橡木安装软件包:这是全橡木套件,它包括 MVC 模型联编程序,生成架构、 橡木 DynamicModel 和支持类、 改版的大规模 (DynamicRepository) 和橡木的核心动态构造双子星座。
  • 安装软件包橡木-json:这是关于 JSON 序列化 (可以用在其他 Api) 橡木的一部分。
  • 形成层安装软件包:这是橡树,MVC 特定的组件不包括和不包括架构生成的一部分。 形成层包括橡木 DynamicDb,DynamicModel,大量的改编版 (动态­存储库) 和橡木核心动态构造双子星座。
  • 安装包种子:这是橡木的架构生成。 此 NuGet 包还包括改版的大规模 (用于插入示例数据)。 它不包含任何 MVC 模型联编程序,或橡木 DynamicModel 或支持类。
  • 双子座安装软件包:这将安装只是的核心动态构造动态善良在橡木生成的所有基础。

之前他们在片中的任何尝试,我建议想感受如何他们每个人都适合入较大图片的整个经验。

利益、 成本和痛苦

可能从这些四列推断出,有一定好处能够只是"海阔天空",与更多动态类型化的系统工作。 毫无疑问,成本和疼痛会提高他们丑陋的头在这样一个系统 (特别是粗心,和那些对编写测试未使用),但甚至那些最顽固的静态类型化偏执者可以学习一些宝贵的意见,从像橡树的系统。 更重要的是,橡树可以是原型系统的早期发展极具价值的工具时,对象模型仍然是高度可变和未定义。 最重要的是,橡木 (就是.NET) 的基础平台,它变得很可行建议建设在橡树的 MVC 应用程序中的早期阶段,然后慢慢地翻转它的部分到更静态类型化 (和,因此,编译器检查和编译器强制执行) 方法如紧紧锁定的应用程序获取更多详细信息。

个人来说,毫无疑问,橡树是一个很酷的小项目。 对于我来说,这是很多有趣的功能和想法时出来很小 (相对来说) 包那些时期之一。 橡木肯定进入我个人的工具箱的把戏。

祝您工作愉快!

Ted Neward 是 Neward & Associates LLC. 的负责人。他写过 100 多篇文章,独自撰写并与人合著过十几本书,包括《Professional F# 2.0》(Wrox,2010 年)。他是一位 F# MVP,经常在全球会议上发表演讲。他征求意见,并定期导师 — — 联系到他在 ted@tedneward.com 如果你感兴趣让他来与您的团队一起工作或阅读他的博客在 blogs.tedneward.com

衷心感谢以下技术专家对本文的审阅:Amir Rajan(Oak 项目创建者)
埃米尔 Rajan 是橡木的创建者。 他是积极发展共同体的成员,在ASP.NETMVC,HTML5,有专长其他体系结构中,红宝石、 JavaScript/CoffeeScript、 NodeJS,iOS/ObjectiveC 和 F #。 拉詹是真正通晓多种语言与软件毫不动摇地激情。 他是在 Twitter 上 @amirrajan 和在 Web 上 github.com/amirrajanamirrajan.netimprovingenterprises.com