上一篇 2) 驗證器實現 簡單描述了下驗證器的簡單實現
本文將說說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: 大神們快快給我些批評吧,冰天雪地跪求了