Blazor中Fluent Validator适配动态Min/Max并在组件值更新时触发验证
解决方案:Blazor Server 中动态参数的 FluentValidation 实时验证
针对你提出的两个核心问题,以下提供两种可行方案,优先推荐组件内集成验证的方式,更适配动态参数场景:
方案1:组件内集成 FluentValidation(推荐)
将验证逻辑直接封装在 DoubleNumericOnly 组件内部,自动识别当前实例的动态 Min/Max 参数,且在值更新时立即触发验证,无需依赖父级模型规则。
组件完整代码
@using Blazored.FluentValidation @using FluentValidation @using FluentValidation.Validators @implements IDisposable <input type="text" @onchange="OnValueChanged" value="@Data" /> @if (CurrentEditContext != null) { <ValidationMessage For="@_fieldIdentifier" /> } @code { [Parameter] public double Data { get; set; } [Parameter] public EventCallback<double> DataChanged { get; set; } [Parameter] public double? Min { get; set; } [Parameter] public double? Max { get; set; } [CascadingParameter] private EditContext? CurrentEditContext { get; set; } private ValidationMessageStore? _messageStore; private IValidator<double>? _validator; private FieldIdentifier _fieldIdentifier; protected override void OnInitialized() { if (CurrentEditContext == null) throw new InvalidOperationException("DoubleNumericOnly 组件必须在 EditForm 内部使用"); _fieldIdentifier = CurrentEditContext.Field(nameof(Data)); _messageStore = new ValidationMessageStore(CurrentEditContext); _validator = new InlineValidator<double>(); UpdateValidationRules(); CurrentEditContext.OnFieldChanged += HandleFieldChanged; } protected override void OnParametersSet() { UpdateValidationRules(); ValidateCurrentValue(); } private void UpdateValidationRules() { _validator?.Rules.Clear(); if (Min.HasValue) { _validator?.RuleFor(x => x) .GreaterThanOrEqualTo(Min.Value) .WithMessage($"数值不能小于 {Min.Value}"); } if (Max.HasValue) { _validator?.RuleFor(x => x) .LessThanOrEqualTo(Max.Value) .WithMessage($"数值不能大于 {Max.Value}"); } } private async Task OnValueChanged(ChangeEventArgs e) { if (double.TryParse(e.Value?.ToString(), out var newValue)) { await DataChanged.InvokeAsync(newValue); ValidateCurrentValue(); } } private void ValidateCurrentValue() { if (_validator == null || CurrentEditContext == null || _messageStore == null) return; _messageStore.Clear(_fieldIdentifier); var validationContext = new ValidationContext<double>(Data); var validationResult = _validator.Validate(validationContext); foreach (var error in validationResult.Errors) { _messageStore.Add(_fieldIdentifier, error.ErrorMessage); } CurrentEditContext.NotifyValidationStateChanged(); } private void HandleFieldChanged(object? sender, FieldChangedEventArgs e) { if (e.FieldIdentifier.Equals(_fieldIdentifier)) { ValidateCurrentValue(); } } public void Dispose() { if (CurrentEditContext != null) { CurrentEditContext.OnFieldChanged -= HandleFieldChanged; } } }
核心特性
- 验证逻辑与组件绑定,复用性强,每个实例自动适配自身的
Min/Max参数 - 值更新时立即触发验证,无需等待表单提交
- 验证消息直接显示在组件下方,用户反馈更直观
- 自动响应
Min/Max参数的动态变更
方案2:父级传递动态参数到 FluentValidation 上下文
若需在父级统一管理验证规则,可通过 ValidationContext 的 RootContextData 传递每个属性的动态 Min/Max 参数。
步骤1:模型验证器
public class MyModelValidator : AbstractValidator<MyModel> { public MyModelValidator() { RuleFor(x => x.DAmount) .Custom((value, context) => { if (context.RootContextData.TryGetValue("DAmount_Min", out var minObj) && minObj is double min && value < min) { context.AddFailure($"DAmount 不能小于 {min}"); } if (context.RootContextData.TryGetValue("DAmount_Max", out var maxObj) && maxObj is double max && value > max) { context.AddFailure($"DAmount 不能大于 {max}"); } }); RuleFor(x => x.AnotherDAmount) .Custom((value, context) => { if (context.RootContextData.TryGetValue("AnotherDAmount_Min", out var minObj) && minObj is double min && value < min) { context.AddFailure($"AnotherDAmount 不能小于 {min}"); } if (context.RootContextData.TryGetValue("AnotherDAmount_Max", out var maxObj) && maxObj is double max && value > max) { context.AddFailure($"AnotherDAmount 不能大于 {max}"); } }); } }
步骤2:父页面代码
<EditForm Model="@MyModel" OnValidSubmit="HandleSubmit"> <Blazored.FluentValidation.FluentValidationValidator @ref="_validator" /> <DoubleNumericOnly @bind-Data="@MyModel.DAmount" Min="50" Max="4000" DataExpression="() => MyModel.DAmount" /> <ValidationMessage For="() => MyModel.DAmount" /> <DoubleNumericOnly @bind-Data="@MyModel.AnotherDAmount" Min="100" Max="400" DataExpression="() => MyModel.AnotherDAmount" /> <ValidationMessage For="() => MyModel.AnotherDAmount" /> <button type="submit">提交</button> </EditForm> @code { private MyModel MyModel = new(); private FluentValidationValidator? _validator; protected override void OnAfterRender(bool firstRender) { if (firstRender && _validator?.EditContext != null) { // 为每个属性绑定动态参数 _validator.EditContext.RootContextData["DAmount_Min"] = 50; _validator.EditContext.RootContextData["DAmount_Max"] = 4000; _validator.EditContext.RootContextData["AnotherDAmount_Min"] = 100; _validator.EditContext.RootContextData["AnotherDAmount_Max"] = 400; // 触发初始验证 _validator.EditContext.Validate(); } } private void HandleSubmit() { // 提交逻辑 } }
组件补充(实时验证触发)
在 DoubleNumericOnly 组件中添加以下代码,实现值更新时触发验证:
[CascadingParameter] private EditContext? CurrentEditContext { get; set; } [Parameter] public Expression<Func<double>>? DataExpression { get; set; } private FieldIdentifier? _fieldIdentifier; protected override void OnInitialized() { if (DataExpression != null && CurrentEditContext != null) { _fieldIdentifier = FieldIdentifier.Create(DataExpression); } } private async Task OnValueChanged(ChangeEventArgs e) { if (double.TryParse(e.Value?.ToString(), out var newValue)) { await DataChanged.InvokeAsync(newValue); if (_fieldIdentifier != null && CurrentEditContext != null) { CurrentEditContext.ValidateField(_fieldIdentifier.Value); } } }
内容的提问来源于stack exchange,提问作者bruceiow
相关产品推荐
相关产品推荐

