随笔 - 8  文章 - 0  评论 - 84 

环境:

Windows 2008, VS 2008 SP1, Asp.Net Mvc 1.0

------------------------------------------------------------------------------

今晚喝茶看片之时,脑子里忽然闪过一个念头,于是打开VS简单的做了一个实现,于是有了本文...是关于实体验证的,嗯,从头开始说吧。

一. 可能是最原始的验证代码

为了让问题尽量简单,我们就拿一个简单得不能再简单的User类开刀吧。

现在我们要在User类里面加一个方法用于验证User实例的合法性,相信很多人写过如下类似的代码:

Check()方法很单纯的为User类的属性应用各种规则,然后返回非法的提示信息集。

验证的时候只是简单的调用一下实体的Check()方法即可:

好吧,我承认,一般情况下这个方法似乎够用了,只不过,很多人看到一连串的if可能会开始抓狂了,是呀,我也不喜欢,那就想个最即便的办法消除它们吧。

二. 基于C#3.0新特性的尝试

为了消除if,考虑把那些验证规则用一条线链起来,于是:

编写实体类的验证扩展方法如下:

那么实体的Check()方法相应改为:

这样,我们通过扩展方法,消除了if,但是我们把验证规则链了起来,并已异常的方式来抛出实体非法信息,那么每次验证只能得到一条验证信息,这显然往往不符合实际情况。想想..

三. 继续演进

接上,为了得到验证错误信息集合,改进:

为了让代码看起来更像那么回事,给实体加个基类吧,就叫EntityBase了。

然后编写一个验证类:

注意:这里的predicate改为使Property非法的Criteria,与前面的方法刚好相反,这样是为了保证当UserName为null时,不至于把"The length of User name should not exceed 10 chars"的信息也抛出。

然后看下修改后的Check()方法,这个验证类的作用就一目了然了:

不用解释吧?其实也是链起来,同时返回验证错误信息集合,看看结果:

看到这里,你可能会说:搞什么啊,不就消除了个if嘛,有什么大不了的。

呃。。是的。。确实如此。。。雕虫小技,如有雷同,纯属巧合。

 

Copyright 作者: Tristan G
本文版权归作者和博客园共有,欢迎转载 :-)
posted on 2009-05-15 01:35 Tristan G 阅读(3878) 评论(46) 编辑 收藏

 回复 引用 查看   
#1楼 2009-05-15 06:50 | 孤星赏月      
不错,支持
 回复 引用 查看   
#2楼 2009-05-15 07:02 | Astral.Road      
放在实体内?
 回复 引用 查看   
#3楼 2009-05-15 07:32 | 关注网页游戏      
看起来不错,用用看
 回复 引用 查看   
#4楼 2009-05-15 08:12 | 温景良(Jason)      
up
 回复 引用   
#5楼 2009-05-15 08:34 | feiyang[未注册用户]
楼主的截图用的什么工具,挺漂亮的
可否告知一下?

 回复 引用 查看   
#6楼 2009-05-15 08:37 | 老鼠      
不错
 回复 引用 查看   
#7楼 2009-05-15 08:42 | 徐少侠      
顶博主

不用if和for是我一直以来的的追求

 回复 引用 查看   
#8楼 2009-05-15 08:58 | Q.Lee.lulu      
哈哈,哇哈哈....
 回复 引用 查看   
#9楼 2009-05-15 09:04 | 黄泉天上来      
我觉得楼主还是把代码粘上 要不 不好拷贝 哈哈哈
 回复 引用 查看   
#10楼 2009-05-15 09:05 | GUO Xingwang      
其实我们有的时候学习的不是知识本身,而是学习楼主处理问题的思路与方法,楼主,you are very NB!
 回复 引用   
#11楼 2009-05-15 09:33 | anonymous123[未注册用户]
不错不错!
 回复 引用 查看   
#12楼[楼主] 2009-05-15 09:34 | Tristan G      
哎,昨晚显然头脑不是很清醒,最后的Check()方法有点小错误,第三个Validate需要加上对UserName的null检查,回家再更新了。
 回复 引用 查看   
#13楼 2009-05-15 09:35 | Anytao      
实体验证,EntLib的Validation组件提供了更好的解决方式:-)
 回复 引用 查看   
#14楼[楼主] 2009-05-15 09:36 | Tristan G      
--引用--------------------------------------------------
Astral.Road: 放在实体内?
--------------------------------------------------------
这个问题可能要另开贴讨论了。有机会我们再来研究一下。:)

 回复 引用 查看   
#15楼 2009-05-15 09:36 | 阿鹏      
呵,写的不错。
我之前也一直在头痛服务端验证与客户端验证的问题。
不过现在找到一种解决方案了:jquery.validate验证客户端,Enterprise validation验证服务端。使用MVC的ModelState来显示错误信息。代码漂亮多了。不过webforms由于没有ModelState。有点小麻烦。

 回复 引用 查看   
#16楼[楼主] 2009-05-15 09:37 | Tristan G      
--引用--------------------------------------------------
feiyang: 楼主的截图用的什么工具,挺漂亮的
可否告知一下?
--------------------------------------------------------
WinSnap

 回复 引用 查看   
#17楼[楼主] 2009-05-15 09:37 | Tristan G      
--引用--------------------------------------------------
徐少侠: 顶博主

不用if和for是我一直以来的的追求
--------------------------------------------------------
握手!

 回复 引用 查看   
#18楼[楼主] 2009-05-15 09:38 | Tristan G      
--引用--------------------------------------------------
黄泉天上来: 我觉得楼主还是把代码粘上 要不 不好拷贝 哈哈哈
--------------------------------------------------------
贴代码感觉有点乱,没掌握技巧贴不来。。
所以还是贴图比较快一点啊,呵呵

 回复 引用 查看   
#19楼[楼主] 2009-05-15 09:39 | Tristan G      
--引用--------------------------------------------------
GUO Xingwang: 其实我们有的时候学习的不是知识本身,而是学习楼主处理问题的思路与方法,楼主,you are very NB!
--------------------------------------------------------
过奖了,只是一点小技巧

 回复 引用 查看   
#20楼[楼主] 2009-05-15 09:41 | Tristan G      
--引用--------------------------------------------------
Anytao: 实体验证,EntLib的Validation组件提供了更好的解决方式:-)
--------------------------------------------------------
没错。

 回复 引用 查看   
#21楼[楼主] 2009-05-15 09:42 | Tristan G      
--引用--------------------------------------------------
阿鹏: 呵,写的不错。
我之前也一直在头痛服务端验证与客户端验证的问题。
不过现在找到一种解决方案了:jquery.validate验证客户端,Enterprise validation验证服务端。使用MVC的ModelState来显示错误信息。代码漂亮多了。不过webforms由于没有ModelState。有点小麻烦。
--------------------------------------------------------
我也是前台jQ。
后台嘛,感觉Ent比较重,所以比较少用。

 回复 引用   
#22楼 2009-05-15 10:03 | huinet[未注册用户]
我是新手,问个问题。
代码中的 => 是什么运算符。

 回复 引用 查看   
#23楼 2009-05-15 10:48 | JimLiu      
不错,挺漂亮的!
 回复 引用 查看   
#24楼 2009-05-15 11:09 | daodao      
看不懂最后一个Check()用的是什么语法?有相关的介绍吗?
 回复 引用 查看   
#25楼 2009-05-15 11:13 | oec2003      
这种方式确实写起来很方便也很舒服
老赵在讲mvc时 写过类似的

 回复 引用 查看   
#26楼 2009-05-15 11:26 | 老李飞刀      
是不是可以用Enterprise Library 中的Validation , 会不会更方便!
http://msdn.microsoft.com/en-us/library/dd140088.aspx

 回复 引用 查看   
#27楼 2009-05-15 11:27 | 老姜      
学习了
 回复 引用 查看   
#28楼 2009-05-15 13:30 | Q.Lee.lulu      
@老李飞刀
我也是这认为来着...

 回复 引用 查看   
#29楼[楼主] 2009-05-15 13:50 | Tristan G      
--引用--------------------------------------------------
huinet: 我是新手,问个问题。
代码中的 => 是什么运算符。
--------------------------------------------------------
Lambda表达式
http://msdn.microsoft.com/zh-cn/library/bb397687.aspx

 回复 引用 查看   
#30楼 2009-05-15 13:50 | 麒麟.NET      
兄台IDE的配色不错,很舒服,呵呵
 回复 引用 查看   
#31楼[楼主] 2009-05-15 13:51 | Tristan G      
--引用--------------------------------------------------
daodao: 看不懂最后一个Check()用的是什么语法?有相关的介绍吗?
--------------------------------------------------------
没什么特别啊?就是折个行

 回复 引用 查看   
#32楼[楼主] 2009-05-15 13:52 | Tristan G      
--引用--------------------------------------------------
麒麟.NET: 兄台IDE的配色不错,很舒服,呵呵
--------------------------------------------------------
哈哈,照顾眼睛啊

 回复 引用 查看   
#33楼[楼主] 2009-05-15 13:54 | Tristan G      
--引用--------------------------------------------------
老李飞刀: 是不是可以用Enterprise Library 中的Validation , 会不会更方便!
<a href="http://msdn.microsoft.com/en-us/library/dd140088.aspx" target="_new">http://msdn.microsoft.com/en-us/library/dd140088.aspx</a>
--------------------------------------------------------
不反对。
如果我不用Ent...

 回复 引用   
#34楼 2009-05-15 16:56 | rex xiang@匿名[未注册用户]
俺稍微篡改了下,写成这样了.
-------------
public static class ValidateExtension {
public static ValidateResult<T> Validate<T>(this T target, Predicate<T> predicate, string errorMessage) {
var result = new ValidateResult<T>(target);

if (!predicate(target)) {
result.Errors.Add(errorMessage);
}

return result;
}

public static ValidateResult<T> Validate<T>(this ValidateResult<T> target, Predicate<T> predicate, string errorMessage) {

if (!predicate(target.Entity)) {
target.Errors.Add(errorMessage);
}

return target;
}
}

public class ValidateResult<T> {
internal List<string> Errors { get; set; }
internal T Entity { get; private set; }

internal ValidateResult(T entity) {
Errors = new List<string>();

Entity = entity;
}

public string[] ErrorMessages { get { return Errors.ToArray(); } }
}

 回复 引用 查看   
#35楼 2009-05-15 18:04 | HCOONa      
很厉害啊,学习了
 回复 引用 查看   
#36楼 2009-05-15 18:16 | Jeffrey Zhao      
C#多nb。
 回复 引用 查看   
#37楼[楼主] 2009-05-15 20:52 | Tristan G      
@rex xiang@匿名
真是很有动手精神啊。

 回复 引用 查看   
#38楼[楼主] 2009-05-15 20:52 | Tristan G      
--引用--------------------------------------------------
Jeffrey Zhao: C#多nb。
--------------------------------------------------------
确实nb

 回复 引用 查看   
#39楼 2009-05-16 09:49 | kyorry      
牛人啊,学习了
但是我觉得后面的做法和前面的if其实本质是一样的,只是语法的变化
如果真能像EntLib的Validate那样,在实体上加标签,就好了
收藏!

 回复 引用   
#40楼 2009-05-16 10:48 | 老衲2009[未注册用户]
楼主能分享下你的IDE配色背景的RGB值吗?感激不尽。。
 回复 引用 查看   
#41楼 2009-05-16 16:38 | 李涛      
RGB大概是:255,255,173
 回复 引用 查看   
#42楼[楼主] 2009-05-17 10:13 | Tristan G      
--引用--------------------------------------------------
kyorry: 牛人啊,学习了
但是我觉得后面的做法和前面的if其实本质是一样的,只是语法的变化
如果真能像EntLib的Validate那样,在实体上加标签,就好了
收藏!
--------------------------------------------------------
那就是另一种解决方案了。

 回复 引用 查看   
#43楼[楼主] 2009-05-17 10:14 | Tristan G      
--引用--------------------------------------------------
老衲2009: 楼主能分享下你的IDE配色背景的RGB值吗?感激不尽。。
--------------------------------------------------------
255 255 181

 回复 引用   
#44楼 2009-07-13 16:49 | xzcvzxcvzxcvxzcv[未注册用户]
什么烂玩意
还有说人NB

 回复 引用 查看   
#45楼[楼主] 2009-07-13 16:53 | Tristan G      
引用xzcvzxcvzxcvxzcv:
什么烂玩意
还有说人NB


那是那是,您才NB

 回复 引用 查看   
#46楼 2009-12-25 10:31 | 修改      
根据你写的修改了一下, 主要加入了,一个实体有多种验证方式, 我修改成了,实体要验证什么有使用者决定,
usingSystem;
usingSystem.Collections.Generic;
usingSystem.Linq;
usingSystem.Text;

namespaceConsoleApplication2
{
classProgram
{
staticvoidMain(string[]args)
{
Useru=newUser(){Name="Dylan",Age=17};

u.CheckList.Add(u.CheckList_Age);
u.CheckList.Add(u.CheckList_Name);
IEnumerable<String>errorList=u.Check();

foreach(stringainerrorList)
{
Console.WriteLine(a);
}
Console.Read();
}
}

publicclassEntityBase
{
publicList<CustomePredicate<User>>CheckList=newList<CustomePredicate<User>>();
publicvirtualIEnumerable<String>Check()
{returnnull;}
}

publicclassUser:EntityBase
{
publicCustomePredicate<User>CheckList_Name=newCustomePredicate<User>("Namelenghtxiao20",u=>u.Name.Length<20);
publicCustomePredicate<User>CheckList_Age=newCustomePredicate<User>("Agexiao20",user=>user.Age<20);

privateValidator<User>validate=newValidator<User>();

publicStringName
{get;set;}

publicInt32Age
{get;set;}

publicUser()
:base()
{}

publicoverrideIEnumerable<String>Check()
{
for(inti=0;i<CheckList.Count;i++)
{
validate.Validate(CheckList[i].ErrorMessage,CheckList[i].Predicate,this);
}
returnvalidate.ErrorList;
}
}

publicclassCustomePredicate<T>whereT:EntityBase
{
privatePredicate<T>m_Predicate=null;
publicPredicate<T>Predicate
{get{returnm_Predicate;}}

privateStringm_erroeMessage=String.Empty;
publicStringErrorMessage
{get{returnm_erroeMessage;}}

publicCustomePredicate(StringerrorMessage,Predicate<T>p)
{
m_erroeMessage=errorMessage;
m_Predicate=p;
}
}

publicclassValidator<T>whereT:EntityBase
{
privateList<String>m_errorlist=newList<String>();
publicIEnumerable<String>ErrorList
{
get{returnm_errorlist;}
}

publicvoidValidate(StringerrorMessage,Predicate<T>predicate,Tentity)
{
if(predicate(entity))
m_errorlist.Add(errorMessage);
}
}
}