Функция выражения и действие как передать функцию формы значения в действие

#c# #expression

#c# #выражение

Вопрос:

У меня есть класс, который имеет 2 метода:

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

Поскольку пользователь может использовать его, передавая каждое из значений. Я хочу сделать это проще и больше похоже на automapper, когда они хотят проверить класс. например

 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));
    }
}
  

итак, на основе automapper у меня есть:

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

но это позволило бы мне настроить .ForMember(x => x.Name, m => m.MinValue(18)) , что было бы неверно и ошибочно во время выполнения.

Я хотел бы иметь возможность ограничить действие memberAction их правильным типом (строки против строки, целые числа для целых чисел и т.д.). Поэтому, когда пользователь настраивает свои сопоставления, он знает, какие методы действия допустимы для типа свойства, который они указали в первой части выражения.

Затем он может использовать эту информацию, чтобы иметь возможность генерировать допустимые методы проверки для каждого из свойств.

Ответ №1:

Вместо 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));
    }
}