FluentValidator, как валадировать св-во во втором валидаторе, исходя из первого валидатора?
Допустим, что есть такие интерфейсы и классы их реализующие:
class FirstTest : IHasId, IHasGuid
{
public int Id { get; set; }
public Guid Guid { get; set; }
}
class SecondTest : IHasId, IHasString
{
public int Id { get; set; }
public string String { get; set; }
}
interface IHasId
{
int Id { get; set; }
}
interface IHasGuid
{
Guid Guid { get; set; }
}
interface IHasString
{
string String { get; set; }
}
Вопрос, как сделать общий валидатор для IHasId, чтобы его можно было использовать так, что если валидатор для IHasId не смог найти значение, например, в БД, то валидация для IHasGuid или IHasString не использовалась?
Не хочется писать один и тот же валидатор для 2-х разных классов, где будет одинаковый метод валидации Id.
Как бы хотелось видеть валидаторы:
class SecondTestValidator : AbstractValidator<SecondTest>
{
public SecondTestValidator()
{
Include(new IdValidator()).DependentRules(idValidator =>
{
if (idValidator.IdValid)
{
RuleFor(x => x.String).NotEmpty().MinimumLength(20).MaximumLength(50).Matches("[a-z]");
}
});
}
}
class FirstTestValidator : AbstractValidator<FirstTest>
{
public FirstTestValidator()
{
Include(new IdValidator()).DependentRules(idValidator =>
{
if (idValidator.IdValid)
{
RuleFor(x => x.Guid).NotEmpty();
}
});
}
}
Но Include возвращает void.
Такой вариант тоже не сработал, проверка вызывается в любом случае:
class FirstTestValidator : AbstractValidator<FirstTest>
{
public FirstTestValidator()
{
Include(new IdValidator(() => RuleFor(x => x.Guid).NotEmpty());
}
}
class IdValidator : AbstractValidator<IHasId>
{
public IdValidator(Action whenValid)
{
RuleFor(x => x.Id).Must(x => x > 10).DependentRules(whenValid);
}
}
Ответы (2 шт):
Примерное решение - использовать расширения для валидации полей.
Как это примерно выглядит:
- Создать расширения для нужных полей (можно передавать DbContext, UoW, etc...)
- В каждом валидаторе просто вешать правило валидации для нужного поля
internal static class RuleExtensions
{
public static IRuleBuilderOptions<T, int> ValidateId<T>(this IRuleBuilder<T, int> builder)
where T : IHasId
{
return builder.Must((t, x) => t.Id > 5);
}
}
И создаем валидатор:
class FirstTestValidator : AbstractValidator<FirstTest>
{
public FirstTestValidator()
{
RuleFor(x => x.Id).ValidateId().DependentRules(() =>
{
RuleFor(x => x.Guid).NotEmpty();
});
}
}
Таким образом валидация для поля Guid будет проводиться только если пройдет валидация для Id.
class SecondTestValidator : AbstractValidator<SecondTest>
{
public SecondTestValidator()
{
Include(new IdValidator());
RuleFor(x => x.String)
.NotEmpty()
.MinimumLength(20)
.MaximumLength(50)
.Matches("[a-z]")
.When(x => ((IdValidator)x).IdValid);
}
}
class FirstTestValidator : AbstractValidator<FirstTest>
{
public FirstTestValidator()
{
Include(new IdValidator());
RuleFor(x => x.Guid)
.NotEmpty()
.When(x => ((IdValidator)x).IdValid);
}
}
class IdValidator : AbstractValidator<IHasId>
{
public bool IdValid { get; private set; }
public IdValidator()
{
RuleFor(x => x.Id)
.Must(x =>
{
bool isValid = // проверка
IdValid = isValid;
return isValid;
})
.WithMessage("Некорректное значение Id");
}
}