Blazor Server中如何将列表传入Fluent Validation验证器检查重复值
Blazor Server中验证输入值是否存在于列表的最优实现
为什么你最初的UI判断方案不生效
你直接用@if判断后显示CustomValidationMessage的方式无效,因为ValidationMessage组件依赖于EditContext中的验证错误集合,手动渲染不会触发验证状态更新,所以组件无法识别到错误并正确显示。
最优方案:直接将外部列表传入FluentValidation验证器
不需要修改模型,通过给验证器添加带参数的构造函数,直接把现有名称列表传入验证逻辑,这是最直接的实现方式:
1. 修改ResourceValidator验证器
添加构造函数接收现有名称列表,并编写正确的验证规则:
public class ResourceValidator : AbstractValidator<ResourceModel> { private readonly IEnumerable<string> _existingResourceNames; // 通过构造函数注入需要对比的名称列表 public ResourceValidator(IEnumerable<string> existingResourceNames) { _existingResourceNames = existingResourceNames; RuleFor(model => model.Name) .Must(name => !_existingResourceNames.Contains(name)) .WithMessage("该资源名称已存在"); } }
2. 在组件中手动创建验证器实例
放弃ValidatorType属性,改用Validator属性直接绑定带参数的验证器实例,传入你的resourcesSortedCollection名称列表:
<EditForm Model="@resourceToBeCreated"> <!-- 传入现有资源名称列表创建验证器 --> <FluentValidationValidator Validator="@(new ResourceValidator(resourcesSortedCollection.Select(x => x.Name)))" /> <!-- 使用标准ValidationMessage显示错误 --> <ValidationMessage For="() => resourceToBeCreated.Name" /> <InputTextOnInput @bind-Value="@resourceToBeCreated.Name" class="form-control" placeholder="名称..." /> </EditForm>
改进你的临时方案(若需保留ValidatorType)
如果坚持使用ValidatorType无参构造的方式,可优化你的模型和验证规则(注意你原规则逻辑有误):
修正验证规则逻辑
原规则中Null().When(...)逻辑错误,正确规则应该是判断名称是否存在于列表中:
RuleFor(x => x.Name) .Must(name => !x.ResourceNames.Contains(name)) .WithMessage("重复的资源名称");
同时确保在组件中及时将resourcesSortedCollection的名称列表赋值给模型的ResourceNames属性,比如在初始化或集合更新时同步:
protected override void OnInitialized() { resourceToBeCreated.ResourceNames = resourcesSortedCollection.Select(x => x.Name).ToList(); } // 若resourcesSortedCollection是动态更新的,需在集合变化时同步 private void OnResourcesCollectionChanged() { resourceToBeCreated.ResourceNames = resourcesSortedCollection.Select(x => x.Name).ToList(); // 通知验证器重新验证 _editContext?.Validate(); }
手动实现验证(不依赖FluentValidation)
如果不想用FluentValidation,可通过操作EditContext手动添加验证错误,让ValidationMessage正确显示:
<EditForm EditContext="@_editContext"> <ValidationMessage For="() => resourceToBeCreated.Name" /> <InputTextOnInput @bind-Value="@resourceToBeCreated.Name" class="form-control" placeholder="名称..." /> </EditForm> @code { private EditContext _editContext; private ResourceModel resourceToBeCreated = new(); private List<ResourceModel> resourcesSortedCollection = new(); protected override void OnInitialized() { _editContext = new EditContext(resourceToBeCreated); // 订阅模型属性变化事件,实时验证名称 ((INotifyPropertyChanged)resourceToBeCreated).PropertyChanged += OnModelPropertyChanged; } private void OnModelPropertyChanged(object sender, PropertyChangedEventArgs e) { if (e.PropertyName == nameof(ResourceModel.Name)) { ValidateResourceName(); } } private void ValidateResourceName() { var field = _editContext.Field(nameof(ResourceModel.Name)); _editContext.ClearValidationMessages(field); if (resourcesSortedCollection.Any(x => x.Name == resourceToBeCreated.Name)) { _editContext.AddValidationMessage(field, "该资源名称已存在"); } _editContext.NotifyValidationStateChanged(); } }
内容的提问来源于stack exchange,提问作者BobbyB
相关产品推荐
相关产品推荐

