在文章:这些.NET开源项目你知道吗?让.NET开源来得更加猛烈些吧!(第二辑)中,给大家初步介绍了一下FluentValidation验证组件。那里只是概述了一下,并没有对其使用和强大功能做深入研究,所以今天以及接下去的几篇文章就专门介绍这个组件。不仅仅是它小,轻量级,优雅,而且一直在持续更新中。本人对这个感触很深是源于4年前自己在做一个数据过滤软件时,自己也设计了一套验证过滤的东西,虽然勉强能用,但太复杂了,复杂到我看到就想吐。。。指导我遇到了FluentValidation,彻底颠覆了我的看法,原来代码是可以很优雅的。。。这就是所谓的架构技术吧,虽然我不是很懂,但一直追求中。

为了保持内容的完整性,大部分内容我都是参考FluentValidation提供的帮助文档,自己经过翻译和理解加工。更好的呈现给大家。

.NET开源目录:【目录】本博客其他.NET开源项目文章目录

本文原文地址:.NET平台开源项目速览(6)FluentValidation验证组件介绍与入门(一)

1.基本介绍

FluentValidation是一个使用Linq表达式,非常流畅的小型业务对象验证组件。流畅也可以说优雅。类似链式操作。易于理解,功能完善。还可以配合MVC使用直接在页面进行验证,当你看到它的语法时,非常优雅,非常令人心动。不仅可以使用Linq的操作,还能自带验证返回信息。更重要的是,组件内部已经封装好了10几种验证器。当然可以自定义一个复杂的哦。核心dll文件也不大,130多k。如果好用,可以自己移植到自己的系统哦。直接更好。目前一直在更新中,主要是bug修复。

官方网站:https://github.com/JeremySkinner/FluentValidation

NuGet Packages:Install-Package FluentValidation

ASP.NET MVC集成包:Install-Package FluentValidation.MVC5

下面我们将从一个简单的验证器的创建以及使用来介绍它的基本功能。

2.创建验证器

使用之前,要引用FluentValidation.dll,太简单,就省略了吧。为了给特定的对象定义一组验证规则,必须创建一个继承AbstractValidator<T>的类,T是需要验证的类型。例如,假设我们有一个Customer类,如下所示:

public class Customer {
  public int Id { get; set; }
  public string Surname { get; set; }
  public string Forename { get; set; }
  public decimal Discount { get; set; }
  public string Address { get; set; }
}

所以按照上面要求,我们要给Customer定义一组验证规则,就要继承AbstractValidator<Customer>,如下面代码:

using FluentValidation;

public class CustomerValidator : AbstractValidator<Customer> 
{
}

验证规则本身在验证器的构造函数中定义。为了给特定属性指定一个验证规则,需要调用RuleFor方法,通过属性的lambda表达式来进行你想要的验证。例如,确保Surname属性不是null,验证器应该这样写:

using FluentValidation;
//Customer验证器
public class CustomerValidator : AbstractValidator<Customer>
{
  public CustomerValidator() 
  {
     RuleFor(customer => customer.Surname).NotNull();
  }
}

3.针对相同属性的链式编程验证

针对同一个属性编写验证代码时,我们可以使用链式的方式进行,非常方便,也容易理解。如下面的代码:

using FluentValidation;
public class CustomerValidator : AbstractValidator<Customer> 
{
  public CustomerValidator() 
  {  //Surname不为空,且不等于foo
     RuleFor(customer => customer.Surname).NotNull().NotEqual("foo");
  }
}

上面的注释已经很明显了,针对Surname属性,直接进行判断和编写代码,同样可以写其他的,一直写下去。。。为了执行验证器,我们首先要创建一个验证器的实例对象,然后将要验证的对象传递给Validate方法,即可进行验证。如下面代码:

//要验证的对象实例
Customer customer = new Customer();
//验证器实例
CustomerValidator validator = new CustomerValidator();
//进行验证操作,获取验证结果
ValidationResult results = validator.Validate(customer);

至于结果的内容,请接着往下看。

4.验证结果

在使用验证器的Validate进行验证后,获取的ValidationResult对象里面提供了2个主要信息:

1.IsValid: 标记是否验证成功

2.Errors :错误情况,验证失败的对象集合,包括所有验证失败对象的细节。

例如下面的代码将验证失败的信息打印出来:

Customer customer = new Customer();
CustomerValidator validator = new CustomerValidator();
ValidationResult results = validator.Validate(customer);
//如果验证失败
if(! results.IsValid) {
  //遍历所有的失败对象
  foreach(var failure in results.Errors) {
  //失败的属性名称,如错误信息
    Console.WriteLine("Property " + failure.PropertyName + " failed validation. Error was: " + failure.ErrorMessage);
  }
}

5.异常与复杂验证

5.1 如何抛出异常

上一节中,我们看到了验证结果的处理情况,但是在很多情况下,如果验证失败的情况下,要及时抛出异常给用户,所以在验证的时候就要注意抛出异常,FluentValidation也提供了这样的机制,它为验证器提供了一个ValidateAndThrow 的扩展方法。使用这个方法后,如果碰到失败的情况,会及时抛出一个ValidationException 异常,让业务验证过程使用。主要代码如下:

Customer customer = new Customer();
CustomerValidator validator = new CustomerValidator();
validator.ValidateAndThrow(customer);

5.2 复杂验证的处理

我们前面处理的都是单个验证器的使用,构成也基本清楚了,但如果在当前验证的实体类中还有其他对象,也需要对这个对象进行各种属性验证,该怎么办。举个例子,假设我们有一个 客户类 Customer 以及地址类Address,在Customer 中包括了一个Address类型的属性,用来存储当前客户的地址信息,如下面所示代码:

//客户类
public class Customer 
{
public string Name { get; set; }
public Address Address { get; set; }
}
//地址类
public class Address
{
public string Line1 { get; set; }
public string Line2 { get; set; }
public string Town { get; set; }
public string County { get; set; }
public string Postcode { get; set; }
}

由于我们也需要对Address类进行,验证,所以先类似的构造一个Address验证器:

public class AddressValidator : AbstractValidator<Address> 
{
public AddressValidator() 
{
RuleFor(address => address.Postcode).NotNull();   
}
}

然后我们同理要构造CustomerValidator验证器,在验证Address的时候,及可以复用上面的AddressValidator,如下面代码:

public class CustomerValidator : AbstractValidator<Customer> 
{
public CustomerValidator() 
{
RuleFor(customer => customer.Name).NotNull();
//对Address使用验证器直接进行验证,可以重复使用代码
RuleFor(customer => customer.Address).SetValidator(new AddressValidator())
}
}

过程比较简单,更加复杂的处理也类似,应该没啥问题了。值得注意的是,如果Address 是集合类型,如List<Address> ,则要使用SetCollectionValidator来进行验证,和SetValidator的使用类似。

6.灵活的验证规则组设置

我们在编写验证过程中,可以编写大量的验证方法,针对不同使用场景,不同字段。但随着业务的复杂,并不是每一个验证器的使用的时候都要执行所有的验证规则。有的时候可能只需要执行一部分就可以了,否则需要针对同一个类型,编写很多个不同业务场景的验证器,显然则是非常残酷的。而FluentValidation则提供了处理这种问题的灵活性,将规则组集合在一起,并赋予一个名称,在执行的时候,可以只执行指定名称规则组的规则。看看下面的例子:

假设一个Person类有3个属性(Id,Surname,Forename),我们写一个验证器分别对几个属性进行验证,并将其中2个的验证规则放在一个规则集合里面RuleSet,名称为:Names,如下面代码:

public class PersonValidator : AbstractValidator<Person> 
{
public PersonValidator()
{   //名称为 Names 的规则组
RuleSet("Names", () => 
{
RuleFor(x => x.Surname).NotNull();
RuleFor(x => x.Forename).NotNull();
});
//其他没有集合的组名称,默认为:default
RuleFor(x => x.Id).NotEqual(0);
}
}

然后我们在验证的时候,就可以灵活指定规则集的名称了,一次可以指定单个或者多个,值得注意的是,没有放在集合中的规则,默认在 default 组中。如下面的代码:

var validator = new PersonValidator();
var person = new Person();
//只执行 Names 规则集
var result = validator.Validate(person, ruleSet: "Names");
//执行 Names,MyRuleSet,SomeOtherRuleSet 规则集
validator.Validate(person, ruleSet: "Names,MyRuleSet,SomeOtherRuleSet")
//执行默认的规则(不在集合中的) 和 MyRuleSet
validator.Validate(person, ruleSet: "default,MyRuleSet")

上面就是今天的内容,我们对验证器的完整过程和使用细节进行了很深入的研究,相信自己构造一个强大的验证器已经很容易了吧。接下来的内容,我们将继续介绍内置的一些验证方法和规则。对于该组件的源码可以直接从github获取,本文将在后面的文章中发布自己制作的CHM帮助文档。敬请关注!

.NET平台开源项目速览(6)FluentValidation验证组件介绍与入门(一)的更多相关文章

  1. NET平台开源项目速览(6)FluentValidation验证组件介绍与入门(转载)

    原文地址:http://www.cnblogs.com/asxinyu/p/dotnet_Opensource_project_FluentValidation_1.html 阅读目录 1.基本介绍 ...

  2. .NET平台开源项目速览(10)FluentValidation验证组件深入使用(二)

    在上一篇文章:.NET平台开源项目速览(6)FluentValidation验证组件介绍与入门(一) 中,给大家初步介绍了一下FluentValidation验证组件的使用情况.文章从构建间的验证器开 ...

  3. .NET平台开源项目速览(17)FluentConsole让你的控制台酷起来

    从该系列的第一篇文章 .NET平台开源项目速览(1)SharpConfig配置文件读写组件 开始,不知不觉已经到第17篇了.每一次我们都是介绍一个小巧甚至微不足道的.NET平台的开源软件,或者学习,或 ...

  4. .NET平台开源项目速览(15)文档数据库RavenDB-介绍与初体验

    不知不觉,“.NET平台开源项目速览“系列文章已经15篇了,每一篇都非常受欢迎,可能技术水平不高,但足够入门了.虽然工作很忙,但还是会抽空把自己知道的,已经平时遇到的好的开源项目分享出来.今天就给大家 ...

  5. .NET平台开源项目速览(13)机器学习组件Accord.NET框架功能介绍

    Accord.NET Framework是在AForge.NET项目的基础上封装和进一步开发而来.因为AForge.NET更注重与一些底层和广度,而Accord.NET Framework更注重与机器 ...

  6. .NET平台开源项目速览(1)SharpConfig配置文件读写组件

    在.NET平台日常开发中,读取配置文件是一个很常见的需求.以前都是使用System.Configuration.ConfigurationSettings来操作,这个说实话,搞起来比较费劲.不知道大家 ...

  7. .NET平台开源项目速览(12)哈希算法集合类库HashLib

    .NET的System.Security.Cryptography命名空间本身是提供加密服务,散列函数,对称与非对称加密算法等功能.实际上,大部分情况下已经满足了需求,而且.NET实现的都是目前国际上 ...

  8. .NET平台开源项目速览(11)KwCombinatorics排列组合使用案例(1)

    今年上半年,我在KwCombinatorics系列文章中,重点介绍了KwCombinatorics组件的使用情况,其实这个组件我5年前就开始用了,非常方便,麻雀虽小五脏俱全.所以一直非常喜欢,才写了几 ...

  9. .NET平台开源项目速览(9)软件序列号生成组件SoftwareProtector介绍与使用

    在文章:这些.NET开源项目你知道吗?让.NET开源来得更加猛烈些吧!(第二辑)中,给大家初步介绍了一下Software Protector序列号生成组件.今天就通过一篇简单的文章来预览一下其强大的功 ...

随机推荐

  1. 20161022 NOIP模拟赛 解题报告

     好元素 [问题描述] 小A一直认为,如果在一个由N个整数组成的数列{An}中,存在以下情况: Am+An+Ap = Ai (1 <= m, n, p < i <= N ,  m,n ...

  2. 【专业找水题】状压dp最水题,没有之一

    题目链接 现在代码能力没上升,倒是越来越会找水题了(比例题还水的裸题你值得拥有) 这网站不是针对竞赛的,所以时空限制都很宽松 然后就让我水过去了 对于每个点,包括自己的前m个元素是否取都是一种状态,所 ...

  3. VIM 解决中文乱码

    $ vim ~/.vimrc 加入以下内容 set fileencodings=utf-8,ucs-bom,gb18030,gbk,gb2312,cp936 set termencoding=utf- ...

  4. bash 源码分析

    下载解压 bash-3.2 编译bash export LFS=/my/soft/mylfs tar xvf $LFS/sources/bash-3.2.tar.gz -C $LFS/sources/ ...

  5. iOS AFNetWorking 3.1 的网络解析

    AFNetworking 3.0中废弃: AFURLConnectionOperation AFHTTPRequestOperation AFHTTPRequestOperationManager   ...

  6. 在update语句中使用子查询

    在update 中的 where 子句中使用子查询: UPDATE mg_page_log as a  SET  page_num=1 WHERE id in( SELECT id  from mg_ ...

  7. “LC.exe”错误

    错误“LC.exe”已退出,代码为 -1. 可能的原因是: 这个第三方组件是个商业组件,他在组件的主使用类定义了 LicenseProvider(typeof(LicFileLicenseProvid ...

  8. Oozie_初识

    Oozie 任务调度框架(基于工作流) oozie运行于hadoop集群,对hive,mr,flume,Soop,spark,shell等框架进行任务流调度 如: job1-->job2 &am ...

  9. java实现全排列

    前天上午的面试遇到了一个用java实现一串数字的全排列的题,想来想去用递归最方便,可是没有在规定的时间内完成555,今天上午有空便继续写,以下是完成后的代码: import java.util.Arr ...

  10. web api Route属性定义

    ASP.NET Web API路由,简单来说,就是把客户端请求映射到对应的Action上的过程.在"ASP.NET Web API实践系列03,路由模版, 路由惯例, 路由设置"一 ...