实现一个对象验证库系列 -- 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,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,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,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 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,string error)
	{
		if (!m_MustBeTrue(value))
		{
			AddFailure(result,error);
		}
		return result;
	}
}

  

然后我们接口绑定加上:

public static class Container
{
	public static ILifetimeScope CurrentScope { get; set; }

	public static void Init(Action<ContainerBuilder> action)
	{
		ParamHelper.CheckParamNull(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,"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,string name)
	{
		(builder as IValidateRuleBuilder).ValueName = name;
		return builder;
	}

	public static IRuleMessageBuilder<T,TProperty> OverrideError<T,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: 大神们快快给我些批评吧,冰天雪地跪求了

相关文章

项目中经常遇到CSV文件的读写需求,其中的难点主要是CSV文件...
简介 本文的初衷是希望帮助那些有其它平台视觉算法开发经验的...
这篇文章主要简单记录一下C#项目的dll文件管理方法,以便后期...
在C#中的使用JSON序列化及反序列化时,推荐使用Json.NET——...
事件总线是对发布-订阅模式的一种实现,是一种集中式事件处理...
通用翻译API的HTTPS 地址为https://fanyi-api.baidu.com/api...