【问题标题】:Suppress base-class property validation in favor of derived-class overridden property validation禁止基类属性验证以支持派生类覆盖的属性验证
【发布时间】:2020-09-08 16:21:47
【问题描述】:

我正在使用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 的同名属性未选中。
问题是:是否有任何内置功能支持这种情况“以抑制对DerivedClass 中覆盖的BaseClass 属性的验证,而不是对这些属性使用相应的DerivedClass 验证”?

【问题讨论】:

  • 你确定这里需要继承吗?每当你觉得你需要使用new 关键字时,这很好地表明你在滥用继承——要么是从错误的基类继承,要么是使用继承而不是组合。当然,这是一个经验法则——在某些例外情况下,使用 new 关键字隐藏继承的成员是最明智的做法。
  • 我不确定您的第二个代码 sn-p 是否有意义。您是否只是将属于方法或属性的代码直接放入类中?
  • @Battle 当然,我修好了。

标签: c# asp.net-core fluentvalidation


【解决方案1】:

底部代码sn-p还是有点怪。 “RuleFor”突然出现,并没有显示它基于什么输入。

通常您会使用继承来覆盖属性的 getter 和 setter,这样无论您获取其基类还是派生类,都具有由派生类设置的属性。 new 键用于覆盖对该特定级别上给定成员的访问,这意味着如果您正在处理派生类,则无法再直接访问隐藏成员。但是您可以简单地将其转换为它的基类,然后无论如何都要这样做。

您还所做的是将验证放入构造函数中。派生类必须调用其基类的现有构造函数(如果未定义,则它是一个公共的、无参数的空构造函数)。这意味着每当您创建派生类的实例时,您首先触发基类的构造函数,然后触发派生类的构造函数。

通常是这样的:

public class MyClass : BaseClass
{
    public MyClass (string p1, int p2, bool p3) : base (p1, p2)
    {
        P3 = p3;
    }
}

如果您无法访问代码(如果它位于外部库中),我认为您无法避免触发基类的构造函数。否则,您可以添加一个受保护的无参数构造函数,在这种情况下,您可以编写 ) : base () 或将其省略。

如果您可以访问代码,则应让构造函数仅在基类中触发虚拟或抽象方法,然后简单地覆盖该方法。

【讨论】:

    【解决方案2】:

    最后,我得到了以下解决方案:

        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.
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-07-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-08-31
      • 1970-01-01
      • 2020-06-04
      • 1970-01-01
      相关资源
      最近更新 更多