在 Fluent 验证之前转换 属性

Transform property before Fluent Validation

我需要先转换我的 属性(删除字符串上的白色 space),然后再应用验证。

具体来说,我想检查一个字符串是否是枚举的一部分,但该字符串可能包含白色spaces(据我所知枚举不允许)

类似...

RuleFor(x => x.Value).IsEnumName(typeof(EnumType)) 

(x.Value 应该先去掉白色space)

您可以使用自定义方法

 RuleFor(x => x.Valor).Must(BeEnumType);

...

      private bool BeEnumType(string v)
      {
        return Enum.IsDefined(typeof(EnumType), v.Replace(" ", string.Empty));
      }

FluentValidation 方法Transform 就是为这种情况设计的。以下使用 Everson 回答中的基本白色 space 去除剂:

RuleFor(x => x.Value)
  .Transform(x => x.Replace(" ", string.Empty))
  .IsEnumName(typeof(EnumType));

我会选择使用更强效的白色 space 去除剂来去除标签等

RuleFor(x => x.Value)
  .Transform(x => new string(x.Where(c => !Char.IsWhiteSpace(c)).ToArray()))
  .IsEnumName(typeof(EnumType));

不需要自定义验证器(例如,Must),我个人会避免编写一个,除非别无他法。

我会把那个白色的 space 移除器放到一个扩展中(MVP,你应该处理 null 的情况;我的偏好是 null guard 但那是另一个话题):

public static class StringExtensions
{
    public static string RemoveWhiteSpace(this string target){
        return new string(target.Where(c => !Char.IsWhiteSpace(c)).ToArray());
    }   
}

那么规则的可读性就高了很多:

RuleFor(x => x.Value)
  .Transform(x => x?.RemoveWhiteSpace())
  .IsEnumName(typeof(EnumType))
  .When(x => x != null);

需要注意的事项:我发现如果 Transform 返回 null,则 IsEnumName 规则将通过。我个人不喜欢这样,所以我会包含一个 When 规则构建器选项,以仅在提供 Value 时进行测试,或者包含一个非空规则以确保提供它。

工作 LINQPad 示例:

public enum EnumType
{
    Value1,
    Value2,
    Value3
}

public class Foo
{
    public Guid Id { get; set; }
    public string Value { get; set; }
}

public class FooValidator : AbstractValidator<Foo>
{
    public FooValidator()
    {
        RuleFor(x => x.Value)
            .Transform(x => x?.RemoveWhiteSpace())
            .IsEnumName(typeof(EnumType));
            .When(x => x != null);
    }
}

public static class StringExtensions
{
    public static string RemoveWhiteSpace(this string target)
    {
        return new string(target.Where(c => !Char.IsWhiteSpace(c)).ToArray());
    }
}

void Main()
{
    var validator = new FooValidator();

    var foo1 = new Foo { Id = Guid.NewGuid(), Value = "Value 1" };
    var result1 = validator.Validate(foo1);
    Console.WriteLine(result1.IsValid);

    var foo2 = new Foo { Id = Guid.NewGuid(), Value = "Value2" };
    var result2 = validator.Validate(foo2);
    Console.WriteLine(result2.IsValid);

    var foo3 = new Foo { Id = Guid.NewGuid(), Value = "Value    3" };
    var result3 = validator.Validate(foo3);
    Console.WriteLine(result3.IsValid);

    var foo4 = new Foo { Id = Guid.NewGuid(), Value = "This is not a valid enum value" };
    var result4 = validator.Validate(foo4);
    Console.WriteLine(result4.IsValid);

    var foo5 = new Foo { Id = Guid.NewGuid(), Value = null };
    var result5 = validator.Validate(foo5);
    Console.WriteLine(result5.IsValid);
}

编辑:

根据您关于将所有这些包装到扩展中的附加评论:

public static class FluentValidationExtensions
{
    public static IRuleBuilderOptions<T, string> IsEnumTypeMember<T>(this IRuleBuilderInitial<T, string> target)
    {
        return target
            .Transform(x => x?.RemoveWhiteSpace())
            .IsEnumName(typeof(EnumType))
            .When(x => x != null);
    }
}

然后更新规则以使用它:

RuleFor(x => x.Value).IsEnumTypeMember();

这只是一个 MVP,我并不真正了解您的所有用例;你可能想让它更通用,这样你就可以将它应用于其他枚举。