FluentValidation 子验证器中的 PropertyName

PropertyName in FluentValidation child validators

我正在 FluentValidation 中尝试一些奇怪的东西,但我似乎无法让它发挥作用。我正在验证的模型中有字符串,我想 运行 对它们解析的对象进行验证,例如,序列化为字符串的日期早于现在,如下所示:

RuleFor(x => x.SentDateString).AsDate(d => d.DateInPast());

我知道这样做的正常方法是让模型本身包含解析的日期和其他对象,然后验证反序列化的数据,但我有理由想这样做。

我是这样工作的:

// general mechanism for adding "casting" rules
public static IRuleBuilderOptions<T, TFrom> Xform<T, TFrom, TTo>(
    this IRuleBuilder<T, TFrom> ruleBuilder,
     Func<TFrom, TTo> xform,
     Func<IRuleBuilder<TFrom, TTo>, IRuleBuilderOptions<TFrom, TTo>> innerRules)
{
    var innerValidator = new InlineValidator<TFrom>() {
        v => innerRules(v.RuleFor(x => xform(x)))
    };
    return ruleBuilder.SetValidator(innerValidator);
}

// specific rule for making a date
public static IRuleBuilder<T, string> AsDate<T>(
    this IRuleBuilder<T, string> ruleBuilder,
    Func<IRuleBuilder<string, DateTime>, IRuleBuilderOptions<string, DateTime>> innerRules)
{
    Func<string, DateTime> parseDate = (s) =>
    {
        DateTime d;
        DateTime.TryParse(s, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out d);
        return d;
    };
    return Xform(ruleBuilder, parseDate, innerRules);
}

// inner rule used in validating the date object
public static IRuleBuilderOptions<T, DateTime> DateInPast<T>(this IRuleBuilder<T, DateTime> ruleBuilder) =>
    ruleBuilder.LessThan(DateTime.Now.Add(TimeSpan.FromMinutes(5)))
        .WithMessage("{PropertyName} must be in the past");

这确实验证了 属性!但是,属性 名称不会传递给子验证器,所以我收到如下消息:

  must be in the past

我期望 "SentDateString" 在前面。我一直无法弄清楚如何进行这项工作。有简单的方法吗?我做错了整件事吗?

事实证明这很简单,只需要传入 IRuleBuilderOptions 而不是 IRuleBuilder,然后使用 Configure 复制显示名称。

public static IRuleBuilderOptions<T, TFrom> Xform<T, TFrom, TTo>(
    this IRuleBuilderOptions<T, TFrom> ruleBuilder,
    Func<TFrom, TTo> xform,
    Func<IRuleBuilder<TFrom, TTo>, IRuleBuilderOptions<TFrom, TTo>> innerRules)
{
    var innerValidator = new InlineValidator<TFrom>() {
        v => innerRules(v.RuleFor(x => xform(x)))
                .Configure(c1 => ruleBuilder.Configure(c2 => c1.DisplayName = c2.DisplayName))
    };
    return ruleBuilder.SetValidator(innerValidator);
}