Доступ к включающей модели представления при использовании FluentValidation на внутренней модели представления - PullRequest
0 голосов
/ 21 мая 2019

Предположим, у меня есть 2 модели просмотра: Project и Task.Проект содержит список задач.У проекта есть BeginDate и EndDate.Каждая из задач также имеет BeginDate и EndDate.

Класс проекта

public class Project
{
  public DateTime BeginDate { get; set; }
  public DateTime EndDate { get; set; }
  public List<Task> Tasks { get; set; }
}

Класс задачи

public class Task
{
  public DateTime BeginDate { get; set; }
  public DateTime EndDate { get; set; }
}

Я хочу использовать FluentValidation, чтобы проверить, что каждая дата вЗадача попадает в сроки Проекта.Я могу сделать это следующим образом:

ProjectValidator class

public class ProjectValidator : AbstractValidator<Project>
{
  public ProjectValidator()
  {
    RuleForEach(x => x.Tasks)
      .Must(((p, t) => t.BeginDate >= p.BeginDate && t.BeginDate <= p.EndDate))
      .WithMessage("Task Begin Date must be within Project's Begin and End Dates.");

    RuleForEach(x => x.Tasks)
      .Must(((p, t) => t.EndDate >= p.BeginDate && t.EndDate <= p.EndDate))
      .WithMessage("Task End Date must be within Project's Begin and End Dates.");
  }
}

Однако это не помечает конкретную дату начала или окончания задачи, которая является проблемой.Сообщение об ошибке отображается в верхней части экрана в сводке об ошибке, а не рядом с датой проблемы.Может быть дюжина или более дат, чтобы проверить, что затруднит выяснить, какая из них на самом деле является проблемой.

Предположительно, это потому, что я проверяю на уровне Проекта, а не на уровне отдельных задач.Чтобы получить поля внутри Задачи, помеченные сообщениями об ошибках, мне нужно проверить задачу.

Поэтому мне нужно написать TaskValidator, что-то вроде этого:

Класс TaskValidator

public class TaskValidator : AbstractValidator<Task>
{
  Project _proj;

  public TaskValidator(Project proj)
  {
    _proj = proj;

    RuleFor(x => x.BeginDate)
      .Must(BeWithinProjectDates)
      .WithMessage("Task Begin Date must be within Project's Begin and End Dates.");

    RuleFor(x => x.EndDate)
      .Must(BeWithinProjectDates)
      .WithMessage("Task End Date must be within Project's Begin and End Dates.");
  }

  private bool BeWithinProjectDates(DateTime date)
  {
    return (date >= _proj.BeginDate &&
            date <= _proj.EndDate);
  }
}

Затем я могу передать модель представления Project в TaskValidator в виде правила, такого как:

Класс ProjectValidator (модифицированный)

public class ProjectValidator : AbstractValidator<Project>
{
  public ProjectValidator()
  {
    RuleForEach(x => x.Tasks)
      .SetValidator(x => new TaskValidator(x));
  }
}

К сожалению, это не работает.Конструктор TaskValidator вызывается один раз, чтобы инициализировать валидатор.Он не вызывается во время проверки.Это означает, что объект Project является пустым.

Можно ли каким-либо образом ссылаться на модель представления Project из TaskValidator, чтобы я мог получить доступ к значениям дат начала и окончания проекта?

Ответы [ 2 ]

0 голосов
/ 24 мая 2019

С помощью Джереми Скиннера, автора FluentValidation, я смог заставить это работать!

Во-первых, параметр SetValidator должен быть лямбда-выражением. Это заставит это быть вызванным в соответствующее время. Я на самом деле уже делал это, хотя я ошибочно оставил это при публикации моего вопроса.

Во-вторых, я использую SimpleInjector и фабрику валидаторов для реализации Dependency Injection. Это означает, что я должен либо не регистрировать TaskValidator, либо убедиться, что фабрика не возвращает его.

ValidatorFactory

public class ValidatorFactory : ValidatorFactoryBase
{
    private readonly Container _container;

    public ValidatorFactory(Container container)
    {
        _container = container;
    }

    public override IValidator CreateInstance(Type validatorType)
    {
        if (_container.GetRegistration(validatorType) == null)
        {
            return null;
        }

        if (validatorType == typeof(IValidator<Task>))
        {
            return null;
        }

        return (IValidator)_container.GetInstance(validatorType);
    }
}

Это обеспечит, что лямбда-выражение в вызове SetValidator создает TaskValidator напрямую, а не через фабрику валидаторов.

0 голосов
/ 21 мая 2019

Я правильно понимаю, что вы делаете что-то вроде этого

 RuleForEach(p => p.Tasks).SetValidator(project => new TaskValidator(project));

а это не работает? Почему бы вам просто не добавить свойство Project в Task ViewModel?

public class Task
{
    public DateTime BeginDate { get; set; }
    public DateTime EndDate { get; set; }

    public Project Project { get; set; }
}

Нетрудно установить это свойство при создании ваших ViewModels.

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