zoukankan      html  css  js  c++  java
  • 实现一个对象验证库系列 -- 3) Fluent以及扩展方法实现 (请大神批评)

    前情回顾:

    上一篇 2) 验证器实现 简单描述了下验证器的简单实现

    本文将说说Fluent方式的实现,欢迎大神们指点指点

    3) Fluent以及扩展方法实现

    我们按照之前 Fluent 的设想以及我们解耦的方式,所以我们先实现一个创建验证器创建者的静态类:

    public static class Validation
    {
    	public static IValidatorBuilder<T> NewValidatorBuilder<T>()  // 创建验证器创建者
    	{
    		return Container.Resolve<IValidatorBuilder<T>>();
    	}
    
    	public static ValidateContext CreateContext(object validateObject,
    		ValidateOption option = ValidateOption.StopOnFirstFailure, params string[] ruleSetList)  // 创建验证数据上下文参数
    	{
    		var result = Container.Resolve<ValidateContext>();
    		result.Option = option;
    		result.RuleSetList = ruleSetList;
    		result.ValidateObject = validateObject;
    		return result;
    	}
    }
    

    我们接着实现 IValidatorBuilder 

    public class ValidatorBuilder<T> : IValidatorBuilder<T>
    {
    	public ObservableCollection<IValidateRuleBuilder> Builders { get; set; }
    
    	public ValidatorBuilder()
    	{
    		Builders = new ObservableCollection<IValidateRuleBuilder>();
    	}
    
    	public IValidator Build()	// 最终build 方法
    	{
    		var result = Container.Resolve<IValidatorSetter>();
    		result.SetRules(Builders.Select(i => i.Build()));
    		return result;
    	}
    
    	public IFluentRuleBuilder<T, TProperty> RuleFor<TProperty>(Expression<Func<T, TProperty>> expression)  // 验证规则创建者方法
    	{
    		ParamHelper.CheckParamNull(expression, "expression", "Can't be null");
    		var builder = Container.Resolve<IRuleBuilder<T, TProperty>>();
    		builder.SetValueGetter(expression);
    		Builders.Add(builder as IValidateRuleBuilder);
    		return builder;
    	}
    
    	public void RuleSet(string ruleSet, Action<IValidatorBuilder<T>> action)   // 规则分组标志设置方法
    	{
    		ParamHelper.CheckParamEmptyOrNull(ruleSet, "ruleSet", "Can't be null");
    		ParamHelper.CheckParamNull(action, "action", "Can't be null");
    
    		var upRuleSet = ruleSet.ToUpper();
    		var updateRuleSet = new NotifyCollectionChangedEventHandler<IValidateRuleBuilder>((o, e) =>
    		{
    			if (e.Action != NotifyCollectionChangedAction.Add) return;
    			foreach (var item in e.NewItems)
    			{
    				item.RuleSet = upRuleSet;
    			}
    		});
    		Builders.CollectionChanged += updateRuleSet;
    		action(this);
    		Builders.CollectionChanged -= updateRuleSet;
    	}
    	
        // 规则分组标志设置方法这样实现可以简化设置的格式,让代码更清晰
        // 比如
        //	      var builder = Validation.NewValidatorBuilder<Student>();
        //        builder.RuleSet("A", b =>
        //        {
        //            b.RuleFor(i => i.Name).NotNull()
        //                    .Must(i=>i.Length > 10)
        //                    .OverrideName("student name")
        //                    .OverrideError("no name")
        //              .ThenRuleFor(i => i.Age)
        //                    .Must(i => i >= 0 && i <= 18)
        //                    .OverrideName("student age")
        //                    .OverrideError("not student");
        //        });
    }
    

      

    接着我们实现 IRuleBuilder:

    public class RuleBuilder<T, TValue> : IRuleBuilder<T, TValue>
    {
    	public string RuleSet { get; set; }
    
    	public Func<object, TValue> ValueGetter { get; protected set; }
    
    	public Expression<Func<T, TValue>> ValueExpression { get; protected set; }
    
    	public string ValueName { get; set; }
    
    	public string Error { get; set; }
    
    	public IValidateRuleBuilder NextRuleBuilder { get; set; }
    
    	public Func<ValidateContext, bool> Condition { get; set; }
    
    	public Func<ValidateContext, string, string, IValidateResult> ValidateFunc { get; set; }
    
    	public void SetValueGetter(Expression<Func<T, TValue>> expression)  // 设置获取值的方法
    	{
    		ValueExpression = expression;
    		var stack = new Stack<MemberInfo>();
    		var memberExp = expression.Body as MemberExpression;
    		while (memberExp != null)
    		{
    			stack.Push(memberExp.Member);
    			memberExp = memberExp.Expression as MemberExpression;
    		}
    
    		var p = Expression.Parameter(typeof(object), "p");
    		var convert = Expression.Convert(p, typeof(T));
    		Expression exp = convert;
    
    		if (stack.Count > 0)
    		{
    			while (stack.Count > 0)
    			{
    				exp = Expression.MakeMemberAccess(exp, stack.Pop());
    			}
    
    			ValueName = exp.ToString().Replace(convert.ToString() + ".", "");  // 设置默认的属性名
    		}
    		else
    		{
    			ValueName = string.Empty;
    		}
    
    		ValueGetter = Expression.Lambda<Func<object, TValue>>(exp, p).Compile(); // 用表达式生成动态获取不同对象的值的方法
    	}
    
    	public IFluentRuleBuilder<T, TProperty> ThenRuleFor<TProperty>(Expression<Func<T, TProperty>> expression) // 创建子级规则接口方法
    	{
    		var builder = Utils.RuleFor(expression);
    		NextRuleBuilder = builder as IValidateRuleBuilder;
    		return builder;
    	}
    
    	public IValidateRule Build() // 规则创建方法
    	{
    		var rule = Container.Resolve<IValidateRule>();
    		rule.ValueName = ValueName;
    		rule.Error = Error;
    		rule.ValidateFunc = ValidateFunc;
    		rule.Condition = Condition;
    		rule.RuleSet = RuleSet;
    		var nextBuilder = NextRuleBuilder;
    		if (nextBuilder != null)
    			rule.NextRule = nextBuilder.Build();
    		return rule;
    	}
    
    }
    

      

    貌似我们完成了大部分了,但是好像哪里不对,

    回忆一下,好像这个持有如何验证逻辑方法的属性没有相关代码处理

    public class ValidateRule : IValidateRule
    {
    	public Func<ValidateContext, string, string, IValidateResult> ValidateFunc { get; set; }
    }

    好吧,我们来建立一个基类先:

    public abstract class BaseChecker<T, TProperty>
    {
    	public virtual IRuleMessageBuilder<T, TProperty> SetValidate(IFluentRuleBuilder<T, TProperty> builder) // 设置验证规则逻辑方法
    	{
    		ParamHelper.CheckParamNull(builder, "builder", "Can't be null");
    		var build = builder as IRuleBuilder<T, TProperty>;
    		build.ValidateFunc = (context, name, error) =>
    		{
    			var value = build.ValueGetter(context.ValidateObject);
    			var result = Container.Resolve<IValidateResult>();
    			return Validate(result, value, name, error);
    		};
    		return build as IRuleMessageBuilder<T, TProperty>;
    	}
    
    	public IValidateResult GetResult()        // 获取验证结果实例对象
    	{
    		return Container.Resolve<IValidateResult>();
    	}
    
    	public void AddFailure(IValidateResult result, string name, object value, string error) // 添加错误信息
    	{
    		result.Failures.Add(new ValidateFailure()
    		{
    			Name = name,
    			Value = value,
    			Error = error
    		});
    	}
    
    	public abstract IValidateResult Validate(IValidateResult result, TProperty value, string name, string error); // 验证规则逻辑接口
    }
    

      

    再接着我们实现一个Must check 类:

    public class MustChecker<T, TProperty> : BaseChecker<T, TProperty>
    {
    	private Func<TProperty, bool> m_MustBeTrue;
    
    	public MustChecker(Func<TProperty, bool> func)
    	{
    		ParamHelper.CheckParamNull(func, "func", "Can't be null");
    		m_MustBeTrue = func;
    	}
    
    	public override IValidateResult Validate(IValidateResult result, TProperty value, string name, string error)
    	{
    		if (!m_MustBeTrue(value))
    		{
    			AddFailure(result, name, value, error);
    		}
    		return result;
    	}
    }
    

      

    然后我们接口绑定加上:

    public static class Container
    {
    	public static ILifetimeScope CurrentScope { get; set; }
    
    	public static void Init(Action<ContainerBuilder> action)
    	{
    		ParamHelper.CheckParamNull(action, "action", "Can't be null");
    		Clear();
    		var builder = new ContainerBuilder();
    		action(builder);
    		var container = builder.Build();
    		CurrentScope = container.BeginLifetimeScope();
    	}
    
    	public static void Init()
    	{
    		Init(builder =>
    		{
    			builder.RegisterType<RuleSelector>().As<IRuleSelector>().SingleInstance();
    			builder.RegisterGeneric(typeof(RuleBuilder<,>)).As(typeof(IRuleBuilder<,>)).InstancePerDependency();
    			builder.Register(c => new ValidateContext() { RuleSelector = c.Resolve<IRuleSelector>() });
    			builder.RegisterType<ValidateRule>().As<IValidateRule>().InstancePerDependency();
    			builder.RegisterType<ValidateResult>().As<IValidateResult>().InstancePerDependency();
    			builder.RegisterGeneric(typeof(ValidatorBuilder<>)).As(typeof(IValidatorBuilder<>)).InstancePerDependency();
    			builder.RegisterType<Validator>().As<IValidatorSetter>().InstancePerDependency();
    		});
    	}
    
    	public static void Clear()
    	{
    		var scope = CurrentScope;
    		if (scope != null)
    			scope.Dispose();
    	}
    
    	public static T Resolve<T>()
    	{
    		return CurrentScope.Resolve<T>();
    	}
    }
    

      

    再然后我们添加 must 的扩展方法:

    public static class Syntax
    {
    	public static IRuleMessageBuilder<T, TProperty> Must<T, TProperty>(this IFluentRuleBuilder<T, TProperty> builder, Func<TProperty, bool> func)
    	{
    		return new MustChecker<T, TProperty>(func).SetValidate(builder);
    	}
    }
    

      

    我们再添加一些消息设置相关的扩展方法:

    public static class Syntax
    {
    	....
    
    	public static IRuleMessageBuilder<T, TProperty> When<T, TProperty>(this IRuleMessageBuilder<T, TProperty> builder, Func<TProperty, bool> func)
    	{
    		ParamHelper.CheckParamNull(func, "func", "Can't be null");
    		var ruleBuilder = builder as IRuleBuilder<T, TProperty>;
    		ruleBuilder.Condition = (context) =>
    		{
    			var value = ruleBuilder.ValueGetter(context.ValidateObject);
    			return func(value);
    		};
    		return builder;
    	}
    
    	public static IRuleMessageBuilder<T, TProperty> OverrideName<T, TProperty>(this IRuleMessageBuilder<T, TProperty> builder, string name)
    	{
    		(builder as IValidateRuleBuilder).ValueName = name;
    		return builder;
    	}
    
    	public static IRuleMessageBuilder<T, TProperty> OverrideError<T, TProperty>(this IRuleMessageBuilder<T, TProperty> builder, string error)
    	{
    		(builder as IValidateRuleBuilder).Error = error;
    		return builder;
    	}
    }
    

      

    大功告成,我们现在就可以这样使用了:

    Container.Init();
    
    var builder = Validation.NewValidatorBuilder<Student>();
    builder.RuleSet("A", b =>
    {
    	b.RuleFor(i => i.Name).Must(i=>i.Length > 10)
    			.OverrideName("student name")
    			.OverrideError("no name")
    	  .ThenRuleFor(i => i.Age)
    			.Must(i => i >= 0 && i <= 18)
    			.OverrideName("student age")
    			.OverrideError("not student");
    });
    var v = builder.Build();
    
    var student = new BigStudent() { Age = 13, Name = "v" };
    var context = Validation.CreateContext(student);
    var result = v.Validate(context);
    Assert.IsNotNull(result);
    Assert.True(result.IsValid);
    Assert.True(result.Failures.Count == 0);
    

      

    最后代码和dll可以通过如下方法获取:

    nuget:https://www.nuget.org/packages/ObjectValidator/

    github:https://github.com/fs7744/ObjectValidator

    PS: 大神们快快给我些批评吧,冰天雪地跪求了

  • 相关阅读:
    高手详解:sscanf函数的高级用法
    堆排序——BuildHeap和Heapify函数的实现
    递归与动态规划求解最长公共子序列
    分享:crpcut 1.8.4 发布,C++ 的单元测试框架
    团队展示 京拍档 电商运营服务、电子商务服务外包 首家京东代运营电子商务服务平台
    Linux中link,unlink,close,fclose详解
    常用排序算法的c++实现(冒泡,选择,插入,堆,shell,快速,归并 )与sort()对比 coder_xia的专栏 博客频道 CSDN.NET
    CAJ文件转PDF文件方法
    递归与动态规划求解最长公共子序列
    NLP Job 专注自然语言处理&机器学习等领域的求职招聘 | 关注自然语言处理|机器学习|数据挖掘|搜索引擎|计算广告|推荐算法等相关领域的工作机会
  • 原文地址:https://www.cnblogs.com/fs7744/p/4898280.html
Copyright © 2011-2022 走看看