FluentValidation:验证仅设置了一个属性
Posted
技术标签:
【中文标题】FluentValidation:验证仅设置了一个属性【英文标题】:FluentValidation: validate only one property is set 【发布时间】:2021-06-21 08:25:04 【问题描述】:我正在为一个类实现一个验证器而苦苦挣扎,其中应该只设置一个属性。
假设我们有以下类:
public class SomeClass
public DateTime SomeDate get; set;
public IEnumerable<int> FirstOptionalProperty get; set;
public IEnumerable<int> SecondOptionalProperty get; set;
public IEnumerable<int> ThirdOptionalProperty get; set;
这个类有一个强制属性 - SomeDate
。其他属性是可选的,只能设置一个,例如如果设置了FirstOptionalProperty
- SecondOptionalProperty
和ThirdOptionalProperty
应该为空,如果设置了SecondOptionalProperty
- FirstOptionalProperty
和ThirdOptionalProperty
应该为空等等。
换句话说:如果设置了 IEnumerable 属性之一 - 其他 IEnumerables 应该为空。
关于为这种类型的类实现验证器的任何提示/想法?我想出的唯一办法就是编写大块的When
规则,但是这种编写代码的方式容易出错,而且结果看起来很丑。
【问题讨论】:
所以基本上可以归结为NotBeNull
只允许 3 个可选属性中的 1 个?我没听错吗?
@MongZhu 如果设置了这些属性之一 - 其他属性应该为空
【参考方案1】:
您可以利用 Must
重载来访问整个类对象,以便您可以针对其他属性进行属性验证。详情请见FluentValidation rule for multiple properties。
public class SomeClassValidator : AbstractValidator<SomeClass>
private const string OneOptionalPropertyMessage = "Only one of FirstOptionalProperty, SecondOptionalProperty, or ThirdOptionalProperty can be set.";
public SomeClassValidator()
RuleFor(x => x.FirstOptionalProperty)
.Must(OptionalPropertiesAreValid)
.WithMessage(OneOptionalPropertyMessage);
RuleFor(x => x.SecondOptionalProperty)
.Must(OptionalPropertiesAreValid)
.WithMessage(OneOptionalPropertyMessage);
RuleFor(x => x.ThirdOptionalProperty)
.Must(OptionalPropertiesAreValid)
.WithMessage(OneOptionalPropertyMessage);
// this "break out" method only works because all of the optional properties
// in the class are of the same type. You'll need to move the logic back
// inline in the Must if that's not the case.
private bool OptionalPropertiesAreValid(SomeClass obj, IEnumerable<int> prop)
// "obj" is the important parameter here - it's the class instance.
// not going to use "prop" parameter.
// if they are all null, that's fine
if (obj.FirstOptionalProperty is null &&
obj.SecondOptionalProperty is null &&
obj.ThirdOptionalProperty is null)
return true;
// else, check that exactly 1 of them is not null
return new []
obj.FirstOptionalProperty is not null,
obj.SecondOptionalProperty is not null,
obj.ThirdOptionalProperty is not null
.Count(x => x == true) == 1;
// yes, the "== true" is not needed, I think it looks better
您可以调整检查功能。按照目前的情况,如果您设置了 2 个或更多可选属性,它们都会抛出错误。这可能适合您的需求,也可能不适合您的需求。
您也可以只为第一个可选属性而不是所有属性创建RuleFor
,因为所有属性都将执行相同的 IsValid 代码并返回相同的消息,如果出现以下情况,您的用户可能会有点困惑他们收到 OptionalProperty1 的错误消息,但他们没有提供那个。
这种方法的缺点是您需要在编译时知道所有属性是什么(以便您可以为其编写代码),并且如果您添加/删除可选条目,则需要维护此验证器。这个缺点对你来说可能很重要,也可能不重要。
【讨论】:
感谢您的解决方案,我会使用它 只有在设置了奇数个属性时,才能使用 XOR 检查是否设置了单个属性...这是一个相关问题***.com/questions/14888174/… @JasonC 谢谢。我以为我在发布此内容时进行了测试,但是阅读您的链接并进行更多调查证明我错了。我已经编辑了我的帖子,我也会给你一个赞成票。 您可以通过检查计数小于 1 来轻松消除第一次空检查....Count(x => x == true) < 1;
【参考方案2】:
我想到的一件事是在这里使用反射:
SomeClass someClass = new SomeClass
SomeDate = DateTime.Now,
FirstOptionalProperty = new List<int>(),
//SecondOptionalProperty = new List<int>() // releasing this breakes the test
;
var info = typeof(SomeClass).GetProperties()
.SingleOrDefault(x =>
x.PropertyType != typeof(DateTime) &&
x.GetValue(someClass) != null);
基本上如果info
是null
则实例化了多个可选属性
【讨论】:
想过,但不知道如何在验证器中访问类的实例。感谢@gunr2171 的回答,现在我知道如何获取类实例。而且由于在我的情况下 IEnumerable 属性具有不同的类型(我在问题中将它们简化为相同的类型,但在实际情况下它们并不相同)我想我会结合你的两个答案。感谢您的帮助,谢谢。 @AnonAnon 如果您将两者结合起来,当您解决问题后会很高兴看到结果。然后请发布:) 强制属性的排除标准是可变的,如果您有 2 个属性,您也可以使用它的名称,例如DateTime
类型 ;)【参考方案3】:
我会为此使用辅助函数。
private static bool OnlyOneNotNull(params object[] properties) =>
properties.Count(p => p is not null) == 1;
你会这样使用它。
SomeClass s = new SomeClass();
/* ... */
if(!OnlyOneNotNull(s.FirstOptionalProperty, s.SecondOptionalProperty, s.ThirdOptionalProperty))
/* handle error case */
【讨论】:
以上是关于FluentValidation:验证仅设置了一个属性的主要内容,如果未能解决你的问题,请参考以下文章
FluentValidation - 仅当值不为空时检查值是表达式
使用 FluentValidation 如何在控制器中使用 validationContext 进行测试
FluentValidation 和服务器+客户端远程验证器
FluentValidation:一个非常受欢迎的,用于构建强类型验证规则的.NET 库