在内部视图模型上使用FluentValidation时访问封闭的视图模型

时间:2019-05-21 19:20:37

标签: c# validation fluentvalidation

假设我有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来验证Task中的每个日期都在Project的日期之内。我可以这样:

ProjectValidator类

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对象为空。

有什么方法可以从TaskValidator中引用项目视图模型,以便我可以访问项目开始日期和结束日期的值?

2 个答案:

答案 0 :(得分:0)

我正确地理解你在做这样的事情

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

在创建ViewModel时设置此属性并不难。

答案 1 :(得分:0)

在FluentValidation的作者Jeremy Skinner的帮助下,我能够使这项工作成功!

首先,SetValidator参数必须是lambda表达式。这将导致它在适当的时间被调用。我实际上已经在做这件事,尽管我在发布问题时误将其遗漏了。

第二,我正在使用SimpleInjector和验证器工厂来实现依赖注入。这意味着我必须不注册或确保工厂不返回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调用中的lambda表达式直接创建TaskValidator,而不是通过验证器工厂来创建。