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

如何实现跨多个类依赖属性场景下的ModelState验证?

实现方案

自定义跨类条件校验可以通过继承ValidationAttribute,从校验上下文获取根对象来实现,以下是可直接复用的代码:

方案1:固定规则的专用校验特性

直接针对你的业务场景实现专用特性,代码如下:

using System.ComponentModel.DataAnnotations;

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class IsRequiredOnlyWhenAIsNotNullAttribute : ValidationAttribute
{
    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        // 从校验上下文获取根级Parent实例(.NET Core 3.0+ 直接支持RootObject属性
        var parent = validationContext.RootObject as Parent;
        // 不在Parent校验上下文下直接跳过,当前校验规则不生效
        if (parent == null)
        {
            return ValidationResult.Success;
        }
        // Child1为空或A属性无值时,无需校验B
        if (parent.C1 == null || string.IsNullOrWhiteSpace(parent.C1.A))
        {
            return ValidationResult.Success;
        }
        // A有值时校验B是否为空
        if (string.IsNullOrWhiteSpace(value?.ToString()))
        {
            return new ValidationResult(ErrorMessage ?? "当A属性有值时,B属性不能为空");
        }
        return ValidationResult.Success;
    }
}

方案2:通用跨属性路径校验特性

如果有大量同类规则的校验需求,可以实现通用特性支持配置依赖属性路径,无需重复定义特性:

using System.ComponentModel.DataAnnotations;
using System.Reflection;

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class RequiredWhenPropertyHasValueAttribute : ValidationAttribute
{
    private readonly string _dependentPropertyPath;

    public RequiredWhenPropertyHasValueAttribute(string dependentPropertyPath)
    {
        _dependentPropertyPath = dependentPropertyPath;
    }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        var rootObject = validationContext.RootObject;
        // 按路径解析依赖属性的值
        var dependentValue = GetValueByPath(rootObject, _dependentPropertyPath);
        
        // 依赖属性无值时跳过校验
        if (dependentValue == null || string.IsNullOrWhiteSpace(dependentValue.ToString()))
        {
            return ValidationResult.Success;
        }

        // 依赖属性有值时校验当前属性是否为空
        if (string.IsNullOrWhiteSpace(value?.ToString()))
        {
            return new ValidationResult(ErrorMessage ?? $"{validationContext.DisplayName}为必填项");
        }
        return ValidationResult.Success;
    }

    // 按点分隔的属性路径从对象中取值
    private object GetValueByPath(object obj, string path)
    {
        foreach (var propName in path.Split('.'))
        {
            if (obj == null) return null;
            PropertyInfo prop = obj.GetType().GetProperty(propName, BindingFlags.Public | BindingFlags.Instance);
            if (prop == null) return null;
            obj = prop.GetValue(obj);
        }
        return obj;
    }
}

使用方式如下:

public class Child2 {
  [RequiredWhenPropertyHasValue("C1.A", ErrorMessage = "当A属性有值时B不能为空")]
  public string B { get; set; }
}

校验触发说明

  • ASP.NET Core 控制器自动模型校验场景下直接生效,框架默认会递归校验子对象属性
  • 手动触发校验时,调用代码如下:
var parent = new Parent();
// 填充parent对象赋值逻辑
var validationContext = new ValidationContext(parent);
var validationResults = new List<ValidationResult>();
// 注意最后一个参数设为true开启全属性递归校验
Validator.TryValidateObject(parent, validationContext, validationResults, validateAllProperties: true);
  • 低于.NET Core 3.0的版本没有RootObject属性,可以手动将根对象赋值到validationContext.Items字典中传入,在校验逻辑中从Items取根对象即可。

内容的提问来源于stack exchange,提问作者Benjamin U.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 00:57:04