基于规则的验证层

时间:2019-06-19 09:45:19

标签: c# validation generics design-patterns

我正在尝试实现this pattern

从呼叫者开始,我想做这样的事情:

var validatore = new Validator();

validatore.AddRule<TestRule>("OK");
validatore.AddRule<int>(45);

validatore.Validate();

执行规则:

public interface IValidationRule<T>
{
    string Error { get; set; }
    bool Validate(T arg);
}

public class TestRule : IValidationRule<string>
{
    public string Error { get; set; }
    public bool Validate(string arg)
    {
        return arg == "test";
    }
}

问题是验证器的具体实现。 我假设是这样的:

public interface IValidator
{
    void AddRule<TRule>(dynamic arg);
    ValidationResult Validate();
}

public class Validator : IValidator
{
    public void AddRule<T>(dynamic arg)
    {
        ???
    }

    public ValidationResult Validate()
    {
        forEach ...
    }
}

我应该在哪里将每个通用规则放在一个集合对象(AddRule)中? 我的实现方式正确吗?

1 个答案:

答案 0 :(得分:0)

此模式从一开始就太抽象了。从某种意义上说,如果您从想要做的事情开始,然后在此之上进行抽象,那么最终会得到更多有用的概括。就像在这种情况下,不明显,正在验证什么?用户输入某些UI?还是来自某些xml的数据?在第一种情况下,您不仅需要显示错误消息,还可能需要根据您的错误执行一些更新UI的操作。然后,验证抽象可能会采取完全不同的形式。实际的代码从何而来呢?必须为每个验证规则实现特殊的类,才能在您真正想要使用它的时候提供一个令人讨厌的抽象但不必要的复杂代码。

此外,仅在绝对没有其他方法时,我才使用动态类型。

因此,假设您仍然感兴趣,下面的代码将与验证引用类型一起使用。再说一次,如果您需要一个验证系统,则需要验证的值类型通常是某个类的属性,但是要记住,它不是通用的。

public class ValidationResult
{
    public bool HasError { get; set; } = false;
    public string ErrorText { get; set; } = "No error";
    public void SetError(string errorMsg)
    {
        this.HasError = true;
        ErrorText = errorMsg;
    }
}

public interface IValidationRule
{
    ValidationResult Validate();
}

public class ValidationRule<T> : IValidationRule
{
    private Func<T, ValidationResult> validatorFunc;
    private T validationSubject;
    public ValidationRule(T validationSubject, Func<T, ValidationResult> validatorFunc)
    {
        this.validationSubject = validationSubject;
        this.validatorFunc = validatorFunc;
    }

    public ValidationResult Validate() => validatorFunc?.Invoke(validationSubject);
}

public interface IValidator
{
    void AddRule<T>(T validationSubject, Func<T, ValidationResult> validationFunc);
    ValidationResult Validate();
}

public class Validator : IValidator
{
    private readonly List<IValidationRule> rules = new List<IValidationRule>();

    public void AddRule<T>(T validationSubject, Func<T,ValidationResult> validationFunc)
    {
        rules.Add(new ValidationRule<T>(validationSubject, validationFunc));
    }

    public ValidationResult Validate()
    {
        foreach (var rule in rules)
        {
            var result = rule.Validate();
            if (result.HasError) return result;
        }

        return new ValidationResult();
    }
}

要添加验证规则,请传入两个参数:一段时间后要验证的对象,以及包含逻辑并返回ValidationResult的Func。像这样:

public class ValidatableObject
{
    public int intValue;
}

private void Test()
{
    var target = new ValidatableObject();
    target.intValue = 1;

    var validator = new Validator();
    validator.AddRule(target, (x) =>
    {
        var validationResult = new ValidationResult();
        if (x.intValue > 10) validationResult.SetError("Exceeds max value (10)");
        return validationResult;
    });

    log(validator.Validate().ErrorText);
    target.intValie = 100;
    log(validator.Validate().ErrorText);
}