FluentValidation 将有效结果设置为自定义 属性

FluentValidation Set Valid Result to a Custom Property

我正在验证文件导入的内容,每行都有一个 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 属性(无论是 header、详细信息还是预告片)验证。

现在可能的是,因为我正在使用 WithState 来存储 LineNumber,我可以将 ValidationResultImportFile 实例进行匹配以设置每个行的有效性如下:

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

而且我还必须检查 header 和预告片。

有什么方法可以在验证器中执行此操作?我正在尝试浏览 FluentValidation 的文档,但我似乎无法在那里找到我需要的东西。

当我探索 FluentValidation 中的可用方法时,我看到了 OnFailureOnAnyFailure 方法。这些方法可能对我需要做的事情有很好的帮助,但问题是它们在 10.3.0 后已经过时,并将在 11 版本中删除。他们建议改用自定义验证器。

  1. Header、详细信息和预告片摘要验证器保持原样。

  2. 我为这 3 个创建了自定义验证程序扩展。

每个扩展方法都会创建相应验证器的实例并执行它。我可以将它们设为通用的 header、细节和预告片,因为它们会做同样的事情,将 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);
    }
}
  1. 我不得不更改 ImportFileValidator 以调用自定义验证器,而不是使用 setvalidator。

ImportFileValidator 如下所示:

public class ImportFileValidator : AbstractValidator<ImportFile>
{
    public ImportFileValidator()
    {
        RuleFor(f => f.Header)
            .IsHeaderValid();

        RuleForEach(f => f.Details)
            .IsDetailValid();

        ...
    }   
}

这几乎就是我能够设置 IsValid 属性 的方式,而无需进行我最初在问题中所做的匹配。