实现一个对象验证库系列 -- 3) Fluent以及扩展方法实现 (请大神批评)

前情回顾:

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

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

 

3) Fluent以及扩展方法实现

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
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;
    }
 
}

  

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

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

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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 类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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;
    }
}

  

然后我们接口绑定加上:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
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 的扩展方法:

1
2
3
4
5
6
7
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);
    }
}

  

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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;
    }
}

  

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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 @   victor.x.qu  阅读(1159)  评论(6编辑  收藏  举报
编辑推荐:
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· .NET Core 托管堆内存泄露/CPU异常的常见思路
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· DeepSeek R1 简明指南:架构、训练、本地部署及硬件要求
· NetPad:一个.NET开源、跨平台的C#编辑器
· PowerShell开发游戏 · 打蜜蜂
点击右上角即可分享
微信分享提示