Ковариантность в общем параметре и соглашении, основанном на типе параметра - PullRequest
0 голосов
/ 14 сентября 2018

Я действительно изо всех сил пытаюсь создать основанные на интерфейсе / соглашениях правила для FluentValidator.Он имеет следующий класс

   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();
}

Ответы [ 2 ]

0 голосов
/ 14 сентября 2018

Я нашел один обходной путь, который избегает использования Expression, что, очевидно, является проблемой здесь. Недостатком этого является то, что мы теряем имя свойства и нам приходится настраивать сообщение вручную.

 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> { }
0 голосов
/ 14 сентября 2018

Разве вы не хотели написать:

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

Потому что Property1 нигде не определено.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...