实现一个对象验证库系列 -- 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: 大神们快快给我些批评吧,冰天雪地跪求了

posted @ 2015-10-21 17:25  victor.x.qu  阅读(1139)  评论(6编辑  收藏  举报