如何用FluentValidation实现Address类Phone/Mobile至少一项必填验证?
解决Address类中Phone与Mobile至少填写一个的验证问题
问题描述
我有一个包含Phone和Mobile属性的Address类,需要验证这两个属性中至少有一个已填写。尝试了三种FluentValidation规则均存在问题:
- 第一种实体级规则完全无效;
- 后两种字段级规则仅在对应字段修改时才生效,导致另一个字段的错误状态无法实时更新。
原验证器代码
public class AddressValidator : BaseValidator<Address> { public AddressValidator() : base() { // NOT WORKING RuleFor(x => x) .Must(x => !string.IsNullOrEmpty(x.Phone) || !string.IsNullOrEmpty(x.Mobile)) .WithMessage("Phone or mobile mandatory"); // WORKS ONLY ON MOBILE FIELD CHANGED RuleFor(m => m.Mobile).Must((a, Mobile) => (!string.IsNullOrEmpty(a.Phone) || !string.IsNullOrEmpty(Mobile))) .WithMessage("Phone or mobile mandatory"); // WORKS ONLY ON PHONE FIELD CHANGED RuleFor(m => m.Phone).Must((a, Phone) => (!string.IsNullOrEmpty(a.Mobile) || !string.IsNullOrEmpty(Phone))) .WithMessage("Phone or mobile mandatory"); } }
原Blazor表单代码
<EditForm Context="billing" Model=@Address> <FluentValidationValidator DisableAssemblyScanning="@true" @ref="validatorAddress"></FluentValidationValidator> <div class="row"> <div class="col-sm-6"> <TextBox TValue="string" @bind-Value=@Address.Phone /> <ValidationMessage For="@(() => Address.Phone )" /> </div> <div class="col-sm-6"> <TextBox TValue="string" @bind-Value=@Address.Mobile/> <ValidationMessage For="@(() => Address.Mobile)" /> </div> </div> </Editform>
解决方案
1. 修正FluentValidation验证器
调整验证规则,确保两个字段的规则互相关联,同时配置级联模式保证所有规则执行:
public class AddressValidator : BaseValidator<Address> { public AddressValidator() { // 设置级联模式为Continue,确保所有规则都能执行 CascadeMode = CascadeMode.Continue; // 验证Phone:若Phone为空,Mobile必须非空 RuleFor(x => x.Phone) .Must((model, phone) => !string.IsNullOrEmpty(phone) || !string.IsNullOrEmpty(model.Mobile)) .WithMessage("Phone or mobile is mandatory"); // 验证Mobile:若Mobile为空,Phone必须非空 RuleFor(x => x.Mobile) .Must((model, mobile) => !string.IsNullOrEmpty(mobile) || !string.IsNullOrEmpty(model.Phone)) .WithMessage("Phone or mobile is mandatory"); // 可选:添加实体级规则,确保表单提交时整体验证通过 RuleFor(x => x) .Must(x => !string.IsNullOrEmpty(x.Phone) || !string.IsNullOrEmpty(x.Mobile)) .WithMessage("Phone or mobile is mandatory") .OverridePropertyName("Phone"); // 将实体级错误关联到Phone字段,方便前端显示 } }
2. 调整Blazor表单触发跨字段验证
由于Blazor默认仅验证当前修改的字段,需在字段变更时手动触发另一个字段的验证,确保错误状态实时更新:
表单代码修改
<EditForm Context="billing" Model=@Address> <FluentValidationValidator DisableAssemblyScanning="@true" @ref="validatorAddress"></FluentValidationValidator> <div class="row"> <div class="col-sm-6"> <TextBox TValue="string" @bind-Value=@Address.Phone ValueChanged="@(() => ValidateField(() => Address.Mobile))" /> <ValidationMessage For="@(() => Address.Phone )" /> </div> <div class="col-sm-6"> <TextBox TValue="string" @bind-Value=@Address.Mobile ValueChanged="@(() => ValidateField(() => Address.Phone))" /> <ValidationMessage For="@(() => Address.Mobile)" /> </div> </div> </Editform>
添加验证触发方法
在Blazor组件代码中添加以下方法,用于手动触发指定字段的验证:
private FluentValidationValidator? validatorAddress; private async Task ValidateField<T>(Expression<Func<T>> field) { if (validatorAddress != null) { await validatorAddress.ValidateAsync(field); } }
问题原因说明
- 实体级规则:Blazor的FluentValidation集成默认不会在单个字段变更时触发实体级验证,仅在表单提交或手动调用整体验证时生效,因此无法实时显示错误。
- 单一字段级规则:仅在对应字段修改时执行验证,导致另一个字段的错误状态无法同步更新,比如修改Phone后,Mobile的旧错误不会自动消失。
内容的提问来源于stack exchange,提问作者Martina
相关产品推荐
相关产品推荐

