在内部视图模型上使用 FluentValidation 时访问封闭的视图模型
Accessing the enclosing viewmodel when using FluentValidation on an inner viewmodel
假设我有 2 个视图模型,项目和任务。该项目包含一个任务列表。该项目有一个 BeginDate 和一个 EndDate。每个任务都有一个 BeginDate 和一个 EndDate。
项目class
public class Project
{
public DateTime BeginDate { get; set; }
public DateTime EndDate { get; set; }
public List<Task> Tasks { get; set; }
}
任务class
public class Task
{
public DateTime BeginDate { get; set; }
public DateTime EndDate { get; set; }
}
我想使用 FluentValidation 来验证任务中的每个日期是否都在项目的日期之内。我可以这样做:
项目验证器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,像这样:
任务验证器class
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);
}
}
然后我可以在规则中将项目视图模型传递给 TaskValidator,如下所示:
ProjectValidatorclass(已修改)
public class ProjectValidator : AbstractValidator<Project>
{
public ProjectValidator()
{
RuleForEach(x => x.Tasks)
.SetValidator(x => new TaskValidator(x));
}
}
不幸的是,这不起作用。 TaskValidator 构造函数被调用一次,以初始化验证器。验证发生时不会调用它。这意味着 Project 对象是空的。
有什么方法可以从 TaskValidator 中引用项目视图模型,以便我可以访问项目开始日期和结束日期的值?
我正确理解你正在做这样的事情
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 时设置此 属性 并不难。
在 FluentValidation 的作者杰里米·斯金纳 (Jeremy Skinner) 的帮助下,我得以完成这项工作!
首先,SetValidator参数必须是一个lambda表达式。这将导致它在适当的时间被调用。我实际上已经在这样做了,尽管我在发布问题时错误地忽略了它。
其次,我使用 SimpleInjector 和验证器工厂来实现依赖注入。这意味着我必须 不注册 TaskValidator 或确保工厂没有 return 一个。
验证器工厂
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,而不是通过验证器工厂。
假设我有 2 个视图模型,项目和任务。该项目包含一个任务列表。该项目有一个 BeginDate 和一个 EndDate。每个任务都有一个 BeginDate 和一个 EndDate。
项目class
public class Project
{
public DateTime BeginDate { get; set; }
public DateTime EndDate { get; set; }
public List<Task> Tasks { get; set; }
}
任务class
public class Task
{
public DateTime BeginDate { get; set; }
public DateTime EndDate { get; set; }
}
我想使用 FluentValidation 来验证任务中的每个日期是否都在项目的日期之内。我可以这样做:
项目验证器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,像这样:
任务验证器class
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);
}
}
然后我可以在规则中将项目视图模型传递给 TaskValidator,如下所示:
ProjectValidatorclass(已修改)
public class ProjectValidator : AbstractValidator<Project>
{
public ProjectValidator()
{
RuleForEach(x => x.Tasks)
.SetValidator(x => new TaskValidator(x));
}
}
不幸的是,这不起作用。 TaskValidator 构造函数被调用一次,以初始化验证器。验证发生时不会调用它。这意味着 Project 对象是空的。
有什么方法可以从 TaskValidator 中引用项目视图模型,以便我可以访问项目开始日期和结束日期的值?
我正确理解你正在做这样的事情
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 时设置此 属性 并不难。
在 FluentValidation 的作者杰里米·斯金纳 (Jeremy Skinner) 的帮助下,我得以完成这项工作!
首先,SetValidator参数必须是一个lambda表达式。这将导致它在适当的时间被调用。我实际上已经在这样做了,尽管我在发布问题时错误地忽略了它。
其次,我使用 SimpleInjector 和验证器工厂来实现依赖注入。这意味着我必须 不注册 TaskValidator 或确保工厂没有 return 一个。
验证器工厂
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,而不是通过验证器工厂。