通用参数中的协变和基于参数类型的约定

Covariance in generic parameter and convention based on parameter type

我真的很难为 FluentValidator 创建基于 interface/convention 的规则。它具有以下 class

   abstract class AbstractValidator<T>
    {

    IRuleBuilderInitial<T, TProperty> RuleFor<TProperty>(Expression<Func<T, TProperty>> expression)
    ...
    }


public interface IWithPropertyA
{
  string PropertyA{get;set;}
}

public interface IWithPropertyB
{
 string PropertyB{get;set;}
}

public class Handler1Data: IWithPropertyA
{
 public string PropertyA {get;set;}
}
public class Handler2Data: IWithPropertyA, IWithPropertyB
{
 public string PropertyA {get;set;}
 public string PropertyB {get;set;}
}

public class Handler1 : AbstractValidator<Handler1Data> {}
public class Handler2 : AbstractValidator<Handler2Data> {}

我正在尝试创建扩展方法,它基本上会检查通用参数是否实现特定接口,然后向其添加规则:

public static void ValidateAll<T>(this AbstractValidator<T> validator)
        {

           (validator as AbstractValidator<IWithPropertyA>)?.RuleFor(x => x.PropertyA).NotEmpty().WithMessage("PropertyA Missing");
           (validator as AbstractValidator<IWithPropertyB>)?.RuleFor(x => x.PropertyB).NotEmpty().WithMessage("PropertyB Missing");
        }

这里的问题显然是 AbstractValidator 不是协变的,因此验证器不能转换为 AbstractValidator<PropertyA>AbstractValidator<PropertyB>。我试过像下面这样创建自己的基本验证器,然后基于它创建扩展方法,但我做不到。

public interface IMyValidator<in T>
{
   void AddMyRule<TProperty>(Expression<Func<T, TProperty>> expression) //it doesn't work because Expression<Func<T,Property> cannont be covariant
}

public abstract class MyBaseValidator<T>: AbstractValidator<T> ,IMyValidator<T>
{
   void AddMyRule<TProperty>(Expression<Func<T, TProperty>> expression)
}

方法将在每个处理程序中调用,如下所示:

public class Handler1 : AbstractValidator<Handler1Data> {
  this.ValidateAll();
}

你不是想写:

(validator as AbstractValidator<IWithPropertyA>)
    ?.RuleFor(x => x.PropertyA)
     .NotEmpty()
     .WithMessage("Property1 Missing");

因为Property1没有在任何地方定义。

我找到了一种避免使用表达式的解决方法,这显然是这里的问题。它的缺点是我们丢失了 属性 名称,我们必须手动配置消息。

 public interface IMyValidator<out T>
    {
        void AddMyRule<TProperty>(Func<T, TProperty> expression, string message);
    }

    public abstract class MyBaseValidator<T> : AbstractValidator<T>, IMyValidator<T>
        {
            public void AddMyRule<TProperty>(Func<T, TProperty> expression, string message)
            {
                var exp = FuncToExpression(expression);
                RuleFor(exp).NotEmpty().WithMessage(message);
            }

            private static Expression<Func<T, P>> FuncToExpression<T, P>(Func<T, P> f) => x => f(x);
        }

public static class Ext
    {
        public static void ValidateAll<T>(this AbstractValidator<T> validator)
        {
            (validator as IMyValidator<IWithPropertyA>)?.AddMyRule(x => x.PropertyA, "PropA Cant be empty");
            (validator as IMyValidator<IWithPropertyB>)?.AddMyRule(x => x.PropertyB, "PropB Cant be empty");
        }
    }

 public class Handler1 : MyBaseValidator<Handler1Data>
    {
        public Handler1()
        {
            this.ValidateAll();
        }
    }
    public class Handler2 : MyBaseValidator<Handler2Data> { }