问题描述
我正在使用Fluent validation
库通过以下模型结构来验证来自webApi
的传入请求:
public class BaseClass
{
public MyEnum MyProperty { get; set; }
}
public class DerivedClass : BaseClass
{
public new string MyProperty { get; set; }
}
请注意new
属性的DerivedClass.MyProperty
关键字。
这是我流畅的验证实现:
public class BaseClassValidator<T> : AbstractValidator<T> where T : BaseClass
{
public BaseClassValidator()
{
RuleFor(prop => prop.MyProperty).IsInEnum();
}
}
public class DerivedClassValidator<T> : BaseClassValidator<DerivedClass>
{
public DerivedClassValidator()
{
RuleFor(prop => prop.MyProperty).NotEmpty();
}
}
要验证的传入模型的类型为DerivedClass
。
不过,我还有其他几个属性,我将其删除以作进一步说明。
问题是,FluentValidator
调用BaseClass
和DerivedClass
的验证,而所需的行为是在覆盖属性时仅验证DerivedClass
的属性使用new
关键字,并取消选中BaseClass
的同名属性。
问题是:在这种情况下,是否有任何内置功能支持“抑制BaseClass
中被DerivedClass
覆盖的DerivedClass
属性的验证,而是使用相应的{{1}}的验证来这些属性”?
解决方法
底部的代码段仍然有些怪异。 “ RuleFor”无处不在,并且不显示其基于什么输入。
通常,您会使用继承来覆盖属性的getter和setter,因此无论您是抓住其基类还是派生类,都必须由派生类设置该属性。 new
键用于覆盖对该特定级别上给定成员的访问,这意味着如果要处理派生类,则不能再直接访问该隐藏成员。但是您可以将其强制转换为它的基类,并且仍然可以这样做。
您还要做的是将验证放入构造函数中。派生类必须调用其基类的现有构造函数(如果未定义,则为公共,无参数的空构造函数)。这意味着,无论何时创建派生类的实例,都首先触发基类的构造函数,然后触发派生类的构造函数。
通常是这样的:
public class MyClass : BaseClass
{
public MyClass (string p1,int p2,bool p3) : base (p1,p2)
{
P3 = p3;
}
}
如果您无法访问代码(如果它在外部库中),我认为您不能避免触发基类的构造函数。否则,您可以添加受保护的无参数构造函数,在这种情况下,您可以编写) : base ()
或将其忽略。
如果您有权访问代码,则应让构造函数仅在基类中触发虚拟或抽象方法,然后简单地重写该方法。
,最后,我得到了以下解决方案:
public class BaseValidator<T> : AbstractValidator<T>
{
#region Constructor
public BaseValidator(params string[] suppressPropertyValidations)
{
var type = typeof(T);
if (!hiddenPropertiesDictionary.ContainsKey(type))
{
lock (type)
{
if (!hiddenPropertiesDictionary.ContainsKey(type))
{
var notMappedProperties = new List<string>();
var hiddenProperties = from property in type.GetProperties()
where property.IsHidingMember()
select property.Name;
notMappedProperties.AddRange(hiddenProperties);
hiddenPropertiesDictionary.Add(type,notMappedProperties);
}
}
}
this.suppressPropertyValidations = new List<string>(suppressPropertyValidations);
}
#endregion
#region Fields & Properties
private static Dictionary<Type,List<string>> hiddenPropertiesDictionary = new Dictionary<Type,List<string>>();
private readonly List<string> suppressPropertyValidations;
#endregion
#region Fields & Properties
private IEnumerable<string> GetSuppressedPropertyValidation()
{
var type = typeof(T);
var hiddenProperties =
hiddenPropertiesDictionary.ContainsKey(type) ?
hiddenPropertiesDictionary[type] :
null;
return suppressPropertyValidations.Union(hiddenProperties);
}
public override ValidationResult Validate(ValidationContext<T> context)
{
var result = base.Validate(context);
var errors =
from suppressProperty in GetSuppressedPropertyValidation()
join error in result.Errors on suppressProperty equals error.PropertyName
select error;
foreach (var error in errors)
result.Errors.Remove(error);
return result;
}
#endregion
}
我使用扩展方法IsHidingMember()
来检查隐藏的属性(from this link)。
现在,按如下所示更改BaseClass
:
public class BaseClassValidator<T> : BaseValidator<T> where T : BaseClass
{
public BaseClassValidator(params string[] suppressPropertyValidations)
: base(suppressPropertyValidations)
{
//Some codes and rules here
}
}
最后我们有了DerivedClass
:
public class DerivedClassValidator : BaseClassValidator<DerivedClass>
{
public DerivedClassValidator()
: base("foo","bar")
//some arbitrary property names (other than the hidden ones e.g. MyProperty) that you want to be suppressed in the baseclass goes here.
{
//Some codes and Rules here.
}
}