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