表达式函数和动作如何将值形式函数传递给动作

时间:2019-04-15 06:57:56

标签: c# expression

我有一个具有2个方法的类:

MaxLength(string text, int maxLength, string propName, string message = null); MinValue(int value, int min, string propName, string message = null);

由于用户可以通过传入每个值来使用它。当他们想验证类时,我想使其变得更简单,更像是自动映射器。例如

public class MyModel {
   public string Category { get; set; }
   public int Number { get; set; }
}

目前他们必须写

MyModel model = //comes from API call or where ever
var validator = new Validator();
validator.MaxLength(model.Category, 4, "Category");
validator.MinValue(model.Number , 3, "Number ");

我想要实现的是能够为该类设置映射:

public class Profile1 : ClassValidationProfile
{
    public Profile1()
    {
        CreateMap<Model1>()
            .ForMember(x => x.Category, m => m.MaxLength(4))
            .ForMember(x => x.Number , m => m.MinValue(3));
    }
}

因此基于自动映射器,我有:

IMappingExpression<T> ForMember<TMember>(Expression<Func<T, TMember>> member, Action<IValidatorExpression> memberAction)

但是,这将允许我设置.ForMember(x => x.Name, m => m.MinValue(18)),这是错误的,并且在运行时会出错。

我希望能够将操作memberAction限制为其正确的类型(字符串对字符串,整数对整数等)。因此,当用户设置其映射时,它知道哪些操作方法对于表达式第一部分中给出的属性类型有效。

然后可以使用此信息为每个属性生成有效的验证器方法。

1 个答案:

答案 0 :(得分:2)

您应该使用Action<IValidatorExpression>来代替TMember来保留类型-例如Func<TMember, bool>(该函数将成员值作为输入并返回一个布尔值,指示是否通过了验证。)

我尝试构建一些示例程序,最终验证如下所示:

var validator = Validator<MyModel>.Create()
    .ForMember(x => x.Category, cat => cat == "A")
    .ForMember(x => x.Number, num => num < 200);

MyModel model = new MyModel {
    Category = "A",
    Number = 100,
};

validator.Validate(model);

这是您要找的吗?

以下是实现上述目标的代码:

class Validator<TClass> {
    private List<IObjectValidator<TClass>> _constraints = new List<IObjectValidator<TClass>>();

    public static Validator<TClass> Create() {
        return new Validator<TClass>();
    }

    public Validator<TClass> ForMember<TMember>(Expression<Func<TClass, TMember>> memberSelectorExpression, Func<TMember, bool> memberValidation, string errorMessage = null) {
        _constraints.Add(new ObjectValidator<TClass, TMember>(memberSelectorExpression.Compile(), memberValidation, errorMessage ?? memberSelectorExpression.ToString() + " did not pass validation"));
        return this;
    }

    public void Validate(TClass obj) {
        foreach (var constraint in _constraints) {
            if (!constraint.Validate(obj))
                throw new Exception(constraint.ErrorMessage);
        }
    }
}

interface IObjectValidator<T> {
    bool Validate(T obj);
    string ErrorMessage { get; }
}

class ObjectValidator<T, TMember> : IObjectValidator<T>
{
    private Func<T, TMember> _memberSelector;
    private Func<TMember, bool> _memberValidation;
    public string ErrorMessage { get; }


    public ObjectValidator(Func<T, TMember> memberSelector, Func<TMember, bool> memberValidation, string errorMessage) {
        _memberSelector = memberSelector;
        _memberValidation = memberValidation;
        ErrorMessage = errorMessage;
    }

    public bool Validate(T obj)
    {
        return _memberValidation.Invoke(_memberSelector.Invoke(obj));
    }
}