程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> 實現一個對象驗證庫系列,驗證庫系列

實現一個對象驗證庫系列,驗證庫系列

編輯:C#入門知識

實現一個對象驗證庫系列,驗證庫系列


前情回顧:

上一篇 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: 大神們快快給我些批評吧,冰天雪地跪求了

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved