在FluentValidation中将子错误传播给父级

时间:2013-01-23 22:35:14

标签: asp.net-mvc-4 fluentvalidation razor-2

环境:
我正在使用MVC4,Razor 2和FluentValidation.MVC4(3.4.6.0)。

情境:
我有一个特定页面的复杂视图模型,它上面还有一个子视图模型,如下所示:

public class ProfileViewModel
{
    public string FirstName {get; set;}
    public PhoneNumberViewModel Primary {get; set;}
    // ... other stuff ... //
}

public class PhoneNumberViewModel
{
    public string AreaCode { get; set; }
    public string Exchange { get; set; }
    public string Suffix { get; set; }
    public string Extension { get; set; }
}

可以编辑此个人资料并将其发回以进行更新。我为两者创建了Fluent验证器,如下所示:

public class ProfileViewModelValidator : AbstractValidator<ProfileViewModel>
{
    public ProfileViewModelValidator()
    {
        RuleFor(m => m.FirstName).NotEmpty().WithMessage("Please enter a First Name,");
        RuleFor(m => m.Primary).SetValidator(new PhoneNumberViewModelValidator()).WithMessage("Hello StackOverflow!");
        // ... other validation ... //
    }
}

public class PhoneNumberViewModelValidator : AbstractValidator<PhoneNumberViewModel>
{
    public PhoneNumberViewModelValidator()
    {
        RuleFor(m => m.AreaCode).NotEmpty();
    }     
}

然后,当然,我有意见来展示一切。

个人资料视图代码段:

...
@Html.TextBoxFor(m => m.FirstName)
@Html.EditorFor(m => m.PrimaryPhoneNumber)
...

电话号码编辑器模板代码段:

...
@Html.ValidationLabelFor(m => m, "Primary Phone:")
@Html.TextBoxFor(m => m.AreaCode)
@Html.TextBoxFor(m => m.Exchange)
@Html.TextBoxFor(m => m.Suffix)
@Html.TextBoxFor(m => m.Extension) 
@Html.ValidationMessageFor(m => m)
...

如果它是相关的,我会设置一些东西,以便自动将验证器连接到各种对象。我实际上甚至不需要上面的.SetValidator()行......无论如何都会因为接线而验证所有内容。

目的:
如果我没有输入名字,我会在ValidationMessageFor创建的区域中收到上面提供的错误消息。但是,如果子PhoneNumberViewModel的任何元素未通过验证,我什么也得不到。文本框突出显示红色,这很棒,但我没有收到.WithMessage()中提供的消息,表明我的子属性无效。

目前我通过我的控制器中的额外工作实现它...在子对象上查找错误,然后向父对象添加错误。这种方法真的非常糟糕。它将与验证相关的问题放在控制器中,我只是不希望它们在那里。更不用说它最终还有很多字符串索引要深入到ModelState中,它只是......粗略。

是否有一种简单的方法可以为ProfileViewModelValidator定义验证规则,如果孩子无法验证,它将为ProfileViewModel添加错误?和/或它应该工作,但我做错了什么?我搜索和搜索,但我找不到满意的解决方案。

谢谢你的时间!

1 个答案:

答案 0 :(得分:0)

我发现了一个与我在问题中包含的臭味不同的解决方案。它也不太理想,但我认为我更喜欢上面提供的解决方案。也许其他人会同意,所以在一个更清晰的答案出现之前它可能是有用的。

我在PhoneNumberViewModel添加了一个属性,该属性将整个电话号码作为格式化字符串返回:

public string FullNumber
{
   get { return string.Format("{0}{1}{2}{3}", AreaCode, Exchange, Suffix, Extension); }
}

除了每个组件属性的规则之外,您还可以将验证规则应用于此属性。如果您只将.WithMessage()应用于FullNumber属性,那么最终将获得您期望的验证消息(并且只有一次),以及如果出现故障,则会为每个单独的框突出显示文本框。

此方法的主要缺点是您最终会复制验证规则。您可以单独验证每个组件,然后还必须验证组合属性,这基本上是您已经完成的验证的组合。您将代码加倍,并将处理量加倍。你也会把可能出错的地方数量增加一倍。

在我的情况下,我使用正则表达式,所以它不是太糟糕......我更喜欢它在控制器中嵌入错误传播代码。然而,它仍然不理想 - 希望能有更好的解决方案。