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

如何根据对象使用场景,有条件设置Contact类属性为必填?

针对Contact类上下文差异化必填规则的实现方案

这是个非常典型的场景——同一个基础数据模型在不同业务上下文里需要遵守不同的校验规则,我给你整理了几个实用的实现思路,你可以根据自己项目的技术栈和复杂度来选择:

方案1:自定义数据注解+上下文感知验证

如果不想改动现有类的继承结构,可以通过自定义验证特性结合验证上下文来实现差异化校验:

首先,给Contact类添加自定义的条件必填特性:

public class Contact
{
    [ConditionallyRequired(RequiredContexts = new[] { "Registration" })]
    public string FirstName { get; set; }
    
    [ConditionallyRequired(RequiredContexts = new[] { "Registration" })]
    public string LastName { get; set; }
    
    public string Email { get; set; }
}

// 自定义条件必填特性
public class ConditionallyRequiredAttribute : ValidationAttribute
{
    public string[] RequiredContexts { get; set; }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        // 从验证上下文的Items中获取当前的上下文标识
        if (validationContext.Items.TryGetValue("ValidationContext", out var contextObj) && 
            contextObj is string context && 
            RequiredContexts.Contains(context))
        {
            if (value == null || string.IsNullOrWhiteSpace(value.ToString()))
            {
                return new ValidationResult($"The {validationContext.DisplayName} field is required in this context.");
            }
        }
        return ValidationResult.Success;
    }
}

然后在验证Registration的时候,传入对应的上下文标识:

var registration = new Registration();
var validationContext = new ValidationContext(registration)
{
    Items = { { "ValidationContext", "Registration" } }
};
var validationResults = new List<ValidationResult>();
bool isValid = Validator.TryValidateObject(registration, validationContext, validationResults, validateAllProperties: true);

验证ChildContact的时候,不传这个上下文或者传其他标识,就不会触发必填校验。

优点:不用修改原有类的结构,对现有代码侵入小;缺点:需要手动管理验证上下文,校验逻辑分散在特性里。

方案2:拆分Contact类为基础+子类(符合单一职责)

如果希望类的职责更清晰,可以把Contact拆分为基础类和带必填规则的子类:

// 基础Contact类,无必填约束
public class BaseContact
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Email { get; set; }
}

// 用于Registration的必填Contact类
public class RequiredContact : BaseContact
{
    [Required]
    public override string FirstName { get; set; }
    
    [Required]
    public override string LastName { get; set; }
}

// 修改父类的属性类型
public class Registration
{
    public RequiredContact ContactDetails { get; set; }
    public List<ChildContact> ChildContacts { get; set; }
}

public class ChildContact
{
    public BaseContact Contact { get; set; }
    public int NoOfStudents { get; set; }
}

这样Registration里的ContactDetails天然就有FirstName和LastName的必填校验,而ChildContact里的Contact则没有强制约束。

优点:类的职责清晰,校验规则和类本身绑定,无需额外上下文处理;缺点:需要新增类,对现有代码有一定的重构成本。

方案3:使用FluentValidation(灵活的第三方校验库)

如果项目可以引入第三方库,FluentValidation是个非常好的选择,它可以完全脱离实体类本身,给不同的父类配置差异化的校验规则:

首先安装NuGet包FluentValidation,然后编写对应的验证器:

// Registration的验证器
public class RegistrationValidator : AbstractValidator<Registration>
{
    public RegistrationValidator()
    {
        RuleFor(r => r.ContactDetails.FirstName)
            .NotEmpty().WithMessage("FirstName is required for main contact");
        RuleFor(r => r.ContactDetails.LastName)
            .NotEmpty().WithMessage("LastName is required for main contact");
        
        // 可以继续配置其他规则
        RuleForEach(r => r.ChildContacts).SetValidator(new ChildContactValidator());
    }
}

// ChildContact的验证器
public class ChildContactValidator : AbstractValidator<ChildContact>
{
    public ChildContactValidator()
    {
        // 这里不对FirstName和LastName做必填约束
        RuleFor(c => c.NoOfStudents).GreaterThan(0);
    }
}

使用的时候直接创建验证器实例即可:

var validator = new RegistrationValidator();
var result = validator.Validate(registration);
bool isValid = result.IsValid;

优点:校验逻辑完全和实体类分离,配置灵活,支持复杂的校验场景;缺点:需要引入第三方库,增加项目依赖。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 16:12:30