禁止基类属性验证,而支持派生类重写属性验证

问题描述

我正在使用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调用BaseClassDerivedClass的验证,而所需的行为是在覆盖属性时仅验证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.
    }
}