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

自定义RequiredParentAttribute:获取父对象名称优化验证提示

问题描述

我开发了一个继承自RequiredAttribute的自定义验证特性RequiredParentAttribute,代码如下:

// 自定义特性
public class RequiredParentAttribute: RequiredAttribute
{
    public RequiredParentAttribute()
    {
        ErrorMessageResourceType = typeof(ResourceFW);
        ErrorMessageResourceName = nameof(ResourceFW.RequiredParentError);
    }

    public override string FormatErrorMessage(string name)
    {
        return string.Format(CultureInfo.CurrentCulture, ErrorMessageString, name, getParentName());
    }

    private string getParentName()
    {
        return "parentName;";
    }
}

现有Order类包含两个Address类型的属性:

public class Order
{
    public Address BillingAddress {get;set;}
    public Address ShippingAddress {get;set;}
}

public class Address
{
    [RequiredParentAttribute]
    public string City {get;set;}
    [RequiredParentAttribute]
    public string ZipCode {get;set;}
}

当前验证时,错误提示无法区分字段所属的父对象:

City is required
ZipCode is required
City is required
ZipCode is required

期望的提示格式为:

City in Billing Address is required
ZipCode in Billing Address is required
City in Shipping Address is required
ZipCode in Shipping Address is required

由于Address类会被多个实体复用,需要通用的解决方案。尝试自定义特性实现时无法获取父对象信息,且了解到.NET无法直接知晓特性调用者,特此求助。

解决方案

要实现通用的父对象名称识别,可利用.NET验证系统的ValidationContext上下文信息,通过父级验证上下文提取属性名称并转换成友好格式,具体实现如下:

1. 修改自定义特性,获取父级属性信息

针对.NET 6+版本,可直接使用ValidationContext.ParentContext属性提取父级属性名称,转换为友好格式后保存,用于后续错误消息格式化:

using System.Globalization;
using System.Text.RegularExpressions;
using System.ComponentModel.DataAnnotations;

public class RequiredParentAttribute : RequiredAttribute
{
    private string _parentFriendlyName;

    public RequiredParentAttribute()
    {
        ErrorMessageResourceType = typeof(ResourceFW);
        // 确保资源文件错误消息格式为: "{0} in {1} is required"
        ErrorMessageResourceName = nameof(ResourceFW.RequiredParentError);
    }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        if (validationContext.ParentContext != null)
        {
            var parentPropertyName = validationContext.ParentContext.DisplayName;
            // 将驼峰命名转换为友好格式(如BillingAddress → Billing Address)
            _parentFriendlyName = Regex.Replace(parentPropertyName, "(?<!^)([A-Z])", " $1");
        }
        else
        {
            // 无父级时的兜底文本
            _parentFriendlyName = "unknown parent";
        }

        return base.IsValid(value, validationContext);
    }

    public override string FormatErrorMessage(string name)
    {
        return string.Format(CultureInfo.CurrentCulture, ErrorMessageString, name, _parentFriendlyName);
    }
}

2. 配置资源文件错误消息格式

在ResourceFW资源文件中,RequiredParentError的内容需设置为:

{0} in {1} is required

3. .NET 5及以下版本兼容方案

若使用.NET 5或更早版本,ValidationContext无ParentContext属性,可通过ValidationContext.Items获取完整属性路径,拆分后提取父级名称:

protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
    if (validationContext.Items.TryGetValue("Validation.PropertyPath", out var pathObj) 
        && pathObj is string fullPropertyPath)
    {
        var pathParts = fullPropertyPath.Split('.');
        if (pathParts.Length >= 2)
        {
            // 提取父级属性名并转换为友好格式
            var parentPropertyName = pathParts[pathParts.Length - 2];
            _parentFriendlyName = Regex.Replace(parentPropertyName, "(?<!^)([A-Z])", " $1");
        }
    }
    else
    {
        _parentFriendlyName = "unknown parent";
    }

    return base.IsValid(value, validationContext);
}

效果说明

使用上述修改后的特性,验证Order对象时,错误提示将自动区分父对象,完全符合预期格式,且无需修改Address或其他使用Address的实体类,具备通用性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 04:02:43