You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET 6 Blazor Server自定义数据验证注解异常排查求助

问题场景

在.NET 6 Blazor Server项目的EditForm中,有三个属性要求至少填写其中一个且不能为空白字符。为此自定义了RequiredIf数据注解,其IsValid方法代码如下:

protected override ValidationResult? IsValid(object? value, ValidationContext validationContext)
{
    if (value != null)
    {
        return ValidationResult.Success;
    }

    int numberOfInvalidProperties = 0;

    object instance = validationContext.ObjectInstance;
    Type containerType = instance.GetType();
    Type type = instance.GetType();

    for (int i = 0; i < this.PropertyNames.Length; i++)
    {
        PropertyInfo? otherProperty = validationContext.ObjectType.GetProperty(this.PropertyNames[i]);

        if (otherProperty == null)
        {
            return new ValidationResult(string.Format(CultureInfo.CurrentCulture, "Could not find a property named '{0}'.", this.PropertyNames[i]));
        }

        object? propertyValue = otherProperty.GetValue(validationContext.ObjectInstance);

        if (propertyValue == this.DesiredValues[i])
        {
            numberOfInvalidProperties++;
        }
        else if (otherProperty?.PropertyType == typeof(string))
        {
            // treat empty strings as null
            if (this.DesiredValues[i] == null && string.IsNullOrWhiteSpace((string?)propertyValue))
            {
                numberOfInvalidProperties++;
            }
        }
    }

    if (numberOfInvalidProperties == this.PropertyNames.Length)
    {
        return new ValidationResult(this.ErrorMessage, new[] { validationContext.MemberName }!);
    }
    else
    {
        return ValidationResult.Success;
    }
}

模型的三个属性配置如下:

[RequiredIf(new string[] { "OrderNumber", "InternalOrderNumber" }, new object?[] { null, null }, ErrorMessage = "At least one of these is required")]
public string CardStatementId { get; set; }

[RequiredIf(new string[] { "OrderNumber", "CardStatementId" }, new object?[] { null, null }, ErrorMessage = "At least one of these is required")]
public string InternalOrderNumber { get; set; }

[RequiredIf(new string[] { "CardStatementId", "InternalOrderNumber" }, new object?[] { null, null }, ErrorMessage = "At least one of these is required")]
public string OrderNumber { get; set; }

提交空表单时三个属性都显示错误,但存在两个问题:

  1. 填写任意一个属性的有效值后,仅该属性的错误提示消失,另外两个属性的错误提示仍存在(不阻止表单提交);
  2. 代码中已处理空白字符串的校验逻辑,且进入了numberOfInvalidProperties == this.PropertyNames.Length分支,但未显示错误提示。

问题分析与解决

问题1:部分属性错误提示未自动消失

这是因为Blazor Server的EditForm默认仅触发当前被编辑属性的验证逻辑,其他属性的验证状态不会自动更新。当你填写其中一个属性后,只有该属性的RequiredIf验证被重新调用并返回成功,另外两个属性的错误状态仍停留在之前的校验结果。

解决方法:

  • 方式一:给每个输入组件添加ValueChanged事件,手动触发全模型验证。比如给InputText添加:
    <InputText @bind-Value="Model.CardStatementId" ValueChanged="@(() => ValidateModel())" />
    
    然后在组件中实现验证方法:
    private async Task ValidateModel()
    {
        var validationContext = new ValidationContext(Model);
        ValidationResults.Clear();
        Validator.TryValidateObject(Model, validationContext, ValidationResults, validateAllProperties: true);
        await EditContext.ValidateAsync();
        StateHasChanged();
    }
    
  • 方式二:改用对象级验证(实现IValidatableObject接口)替代属性级自定义注解,这样每次验证都会扫描整个模型,所有属性的错误提示会同步更新。

问题2:空白字符串未触发错误提示

IsValid方法开头的判断逻辑存在漏洞:如果当前属性是空白字符串(如""或全空格),value是一个非null的字符串对象,会直接返回ValidationResult.Success,跳过后续的其他属性检查逻辑。

需要先将当前属性的空白字符串视为null处理,修改开头的判断:

// 先处理当前属性的空白字符串
if (value is string strValue)
{
    if (string.IsNullOrWhiteSpace(strValue))
    {
        value = null;
    }
}

if (value != null)
{
    return ValidationResult.Success;
}

这样空白字符串会被当作null处理,进而进入后续的其他属性检查逻辑,符合“不能为空/空白字符”的要求。

另外注意:模型中字符串属性默认初始化为空字符串""而非null,上述修改已经覆盖了这种场景,无需额外修改模型构造函数。

内容的提问来源于stack exchange,提问作者osswmi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 16:35:18