假设我有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的日期之内。我可以这样:
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,如下所示:
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:
public class ProjectValidator : AbstractValidator<Project>
{
public ProjectValidator()
{
RuleForEach(x => x.Tasks)
.SetValidator(x => new TaskValidator(x));
}
}
不幸的是,这不起作用。 TaskValidator构造函数被调用一次,以初始化验证器。验证发生时未调用它。这意味着Project对象为空。
有什么方法可以从TaskValidator中引用项目视图模型,以便我可以访问项目开始日期和结束日期的值?
答案 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。
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,而不是通过验证器工厂来创建。