#.net-5 #fluentvalidation
Вопрос:
Я проверяю содержимое для импорта файла, и у меня есть IsValid
свойство для каждой строки.
public class Header
{
public int LineNumber { get; set; }
public string Property1 { get; set; }
public string Property2 { get; set; }
public bool IsValid { get; set; }
}
public class Detail
{
public int LineNumber { get; set; }
public string Property1 { get; set; }
public string Property2 { get; set; }
public string Property3 { get; set; }
public bool IsValid { get; set; }
}
public class Trailer
{
public int LineNumber { get; set; }
public string Property1 { get; set; }
public bool IsValid { get; set; }
}
public class ImportFile
{
public Header Header { get; set; }
public List<Detail> Details { get; set; }
public Trailer Trailer { get; set; }
}
и мои валидаторы выглядят примерно так:
public class DetailValidator : AbstractValidator<Detail>
{
public DetailValidator()
{
RuleFor(d => d.Property1)
.Cascade(CascadeMode.Stop)
.NotEmpty()
.WithState(d => d.LineNumber)
.Length(3)
.WithState(d => d.LineNumber);
RuleFor(d => d.Property2)
.Cascade(CascadeMode.Stop)
.NotEmpty()
.WithState(d => d.LineNumber)
.MaximumLength(50)
.WithState(d => d.LineNumber);
...
}
}
public class ImportFileValidator : AbstractValidator<ImportFile>
{
public ImportFileValidator()
{
RuleFor(f => f.Header)
.SetValidator(new HeaderValidator());
RuleForEach(f => f.Details)
.SetValidator(new DetailsValidator());
...
}
}
После того, как я вызову проверку, я хотел установить IsValid
свойство каждой строки файла (будь то заголовок, детали или трейлер) на основе результата проверки.
Что возможно на данный момент, так это то , что, поскольку я использую WithState
для хранения LineNumber
, я могу сопоставить ValidationResult
экземпляр с ImportFile
экземпляром, чтобы установить действительность каждой строки, как показано ниже:
ImportFile file = // parsed file content
var result = new ImportFileValidator().Validate(file);
foreach (var detail in file.Details)
{
var error = result.Errors.FirstOrDefault(e =>
Convert.ToInt32(e.CustomState) == detail.LineNumber);
detail.IsValid = error == null;
}
И я также должен проверить заголовок и трейлер.
Есть ли способ сделать это внутри валидаторов? Я пытаюсь изучить документацию FluentValidation, но, похоже, не могу найти там то, что мне было нужно.
Ответ №1:
Когда я изучал доступные методы в FluentValidation, я увидел OnFailure
и OnAnyFailure
методы. Эти методы могут быть хорошим подспорьем в том, что мне нужно было сделать, но проблема в том, что они устарели 10.3.0
и будут удалены в версии 11
. Вместо этого они предлагают использовать пользовательский валидатор.
- Валидаторы заголовка, Деталей и аннотации трейлера остаются такими, как есть.
- Я создал пользовательские расширения валидатора для этих 3.
Каждый метод расширения создает экземпляр соответствующего валидатора и выполняет его. Я могу сделать их универсальными для заголовка, деталей и трейлера, так как они будут делать одно и то же, установите свойство isValid для результата проверки.
public static IRuleBuilderOptionsConditions<ImportFile, T> IsHeaderValid<T>(this IRuleBuilder<ImportFile, T> ruleBuilder)
where T : Header
{
return builder.Custom((header, context) =>
{
// Create the Header Abstract Validator Instance
var validator = new HeaderValidator();
var result = validator.Validate(Header);
header.IsValid = result.IsValid;
// Pass the errors to the context
result.Errors.ForEach(context.AddFailure);
}
}
- Мне пришлось изменить ImportFileValidator, чтобы вызывать пользовательские валидаторы, вместо использования setvalidator.
ImportFileValidator выглядит следующим образом:
public class ImportFileValidator : AbstractValidator<ImportFile>
{
public ImportFileValidator()
{
RuleFor(f => f.Header)
.IsHeaderValid();
RuleForEach(f => f.Details)
.IsDetailValid();
...
}
}
Это в значительной степени то, как я смог установить IsValid
свойство без необходимости выполнять сопоставление, которое я изначально сделал в вопросе.