代码改变世界

小试牛刀:自制一个轻量级验证框架(A Lightweight Model-Validation Framework Of Sopaco)

2010-03-14 18:06  姜 萌@cnblogs  阅读(4415)  评论(5编辑  收藏  举报

在软件开发中经常需要写数据验证的代码(有的是通过xml进行配置),对于web客户端而言我们可以使用jquery的validation插件,得意于js语言的灵活特性,可以快速的写出整洁高效的js验证代码。

而在服务端这边,用静态语言写出的代码给人印象就不那么灵巧了。幸好C#在3.x中加入了lambda expression、Extension Methods等新语法特性以及相关的编译器支持,再加上即将发布的DLR赋予C#动态语言能力,使得我们可以在语言层次上写出与js相媲美的十足灵活的漂亮代码。

这个小验证框架内部大量使用lambda表达式和linq集成查询,使得对框架的使用和功能扩展都非常容易。

先看下下效果:

 

效果示例
//我们定义一个简单类型AccountModel
public class AccountModel
    {
        
public int Sid { getset; };
        
public string Balance { getset; };
    } 

//我们需要对AccountModel进行数据验证,使用如下代码初始化一个验证器。 
      ValidateTypeDictionary.Instance//获取验证字典实例
                .RegisterType<AccountModel>()//注册一个AccountModel类型的Validator
                .ResolveValidator<AccountModel>()//得到类型为AccountModel的Validator
                .Setup(p => p.a != 5)//设置自定义规则,需要AccountModel.a不等于5
                .Setup(p => p.b.EndsWith("abc"))//设置自定义规则,需要AccountModel.b以”abc“结尾
                .IntRule<AccountModel>(p => p.a, 150)//这是扩展方法,需要a介于1与50之间
                .EmailRule(p => p.b);//这是扩展的方法,验证是否为邮件格式,同样你也可以扩展自己的验证方法。

//以后如果需要验证对象就这样使用:
            if(ValidateTypeDictionary.Instance.Validate<AccountModel>(model2)){ …… };

 

通过类型字典注册每一个实体模型对应的验证规则,然后只需要通过Setup()方法放入验证逻辑,也可以在合适的时候重置验证逻辑。除此以外,可以使用扩展方法特性为Validate加入特定的验证逻辑(将在下面给出示范),方便的第三方扩充框架功能。

先来看看接口定义

接口定义 
namespace Sopaco.Lib.Validate.ModelValidate
{
    
public interface IValidateTypeDictionary
    {
        IValidateTypeDictionary RegisterType
<TModel>();
        IValidator
<TModel> ResolveValidator<TModel>();
        
bool Validate<TModel>(TModel model);
    }


namespace Sopaco.Lib.Validate.ModelValidate
{
    
public interface IValidator<TModel>
    {
        IValidator
<TModel> Setup(Func<TModel, bool> f);
        
bool ValidateModel(TModel model);
        
void ResetValidator();
    }
}

 

 

IValidatorTypeDictionary为我们提供注册(为了方便使用也提供了验证接口)、获取验证对象的方法。IValidator提供具体的自定义验证设置和验证方法调用。

具体实现上我使用了泛型字典,Validator类实现IValidator,每个Validator<TModel>都是单例,由于CLR提供真泛型支持,所以每个需要验证的类型都能对应一个Validator实例。

 

实现 
namespace Sopaco.Lib.Validate.ModelValidate
{
    
public class ValidateTypeDictionary : IValidateTypeDictionary
    {
        
#region Fields
        
public static readonly IValidateTypeDictionary Instance;
        
#endregion 

        
#region Constructors & Initializer
        
static ValidateTypeDictionary()
        {
            Instance 
= new ValidateTypeDictionary();
        }
        
private ValidateTypeDictionary()
        {
        }
        
#endregion 

        
#region IValidateTypeDictionary 成员 

        
public IValidateTypeDictionary RegisterType<TModel>()//注册到类型字典中
        {
            
if (Validator<TModel>.Instance == null)
                Validator
<TModel>.Instance = new Validator<TModel>();
            
return this;
        } 

        
public bool Validate<TModel>(TModel model)
        {
            var validator 
= ResolveValidator<TModel>();
            
if(validator == null)
            {
                
throw new NonValidatorException();
            }
            
return validator.ValidateModel(model);
        } 

        
public IValidator<TModel> ResolveValidator<TModel>()
        {
            
return Validator<TModel>.Instance;
        }
        
#endregion
    }
}



namespace Sopaco.Lib.Validate.ModelValidate
{
    
public class Validator<TModel> : IValidator<TModel>
    {
        
#region Fields
        
internal IList<Func<TModel, bool>> _validateRules;
        
internal static Validator<TModel> Instance;
        
#endregion 

        
#region Constructors & Initializer
        
public Validator()
        {
            Init();
        }
        
private void Init()
        {
            
//_validateRules = Enumerable.Empty<Func<TModel, bool>>();
            _validateRules = new List<Func<TModel, bool>>();
        }
        
#endregion 

        
#region IValidator<TModel> 成员 

        
public IValidator<TModel> Setup(Func<TModel, bool> rule)
        {
            _validateRules.Add(rule);;
            
return this;
        } 

        
public bool ValidateModel(TModel model)
        {
            
return _validateRules.Aggregate(
                
true,
                (prev, f) 
=> f.Invoke(model)
                );
        } 

        
public void ResetValidator()
        {
            _validateRules.Clear();
        }
        
#endregion 
    }
}

 

 

 

下面说说如何扩展:

框架本身并没有提供验证逻辑,验证逻辑是使用者去写,对于常用的验证,比如验证电子邮件格式,身份证好等如果频繁的调用Setup方法就很臃肿了,所以我们可以基于框架借助扩展方法来增强框架功能。

下面我给出了示范,添加一个正则表达式验证扩展方法和更为具体的电子邮件验证扩展方法还有一个对整数范围的验证扩展方法。可以根据需要扩展更多的验证逻辑。

可以由第三方来扩展
namespace Sopaco.Lib.Validate.ModelValidate.ValidateExtensions
{
    
public static class ValidateRules
    {
        
public static IValidator<TModel> IntRule<TModel>(this IValidator<TModel> validator, Func<TModel, int> func, int min, int max)
        {
            Func
<TModel, bool> f
                
= p =>
                    {
                        
int property = func.Invoke(p);
                        
return property >= min && property <= max;
                    };
            validator.Setup(f);
            
return validator;
        } 

        
public static IValidator<TModel> StringRule<TModel>(this IValidator<TModel> validator, Func<TModel, string> func, string pattern)
        {
            Func
<TModel, bool> f
                
= p =>
                {
                    
string property = func.Invoke(p);
                    
return new Regex(pattern).IsMatch(property);
                };
            validator.Setup(f);
            
return validator;
        }
        
public static IValidator<TModel> EmailRule<TModel>(this IValidator<TModel> validator, Func<TModel, string> func)
        {
            StringRule
<TModel>(validator, func, @"^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$");
            
return validator;
        }
    }
}
SourceCode放在SkyDriver上了:
 

 

The reproduction or distribution of copyright works in wJiang's blog for research, private study or internal reference purpose is permitted provided there is an acknowledgement of the source where appropriate

网名:

无疆_炎戎
无疆_寒冰

实名:

姜萌

姜萌@LiveSpace:
姜萌@cnblogs