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

C# FluentValidation(11.4.0)泛型验证:多类型Resource属性规则配置

问题分析与解决方案

首先要纠正你代码中的一个关键错误:你的泛型验证类继承目标写错了。当前代码public class ResourceDtoValidator<TResource> : AbstractValidator<ResourceDtoValidator<TResource>>是在验证验证器自身,而不是验证包含Resource属性的DTO类型。正确的继承应该是针对包含TResource类型Resource属性的DTO,比如假设你的DTO定义是:

public class ResourceDto<TResource>
{
    public TResource Resource { get; set; }
    // 其他业务属性
}

那么验证类应该修正为:

public class ResourceDtoValidator<TResource> : AbstractValidator<ResourceDto<TResource>>
{
    // 验证规则在这里实现
}

接下来针对不同TResource类型(HumanResource/HardwareResource/OtherResource)的验证需求,提供三种可行方案:

方案一:在泛型验证器内按类型分支设置规则

这种方式最直接,在同一个验证器中通过类型判断为不同资源类型设置专属规则:

public class ResourceDtoValidator<TResource> : AbstractValidator<ResourceDto<TResource>>
{
    public ResourceDtoValidator()
    {
        // 所有资源类型通用的基础规则
        RuleFor(x => x.Resource)
            .NotNull()
            .WithMessage("资源不能为空");

        // HumanResource专属验证规则
        When(x => x.Resource is HumanResource, () =>
        {
            RuleFor(x => ((HumanResource)x.Resource).EmployeeId)
                .NotEmpty()
                .Length(5, 20)
                .WithMessage("员工ID必须是5-20位非空字符串");
            RuleFor(x => ((HumanResource)x.Resource).Department)
                .NotNull()
                .NotEmpty()
                .WithMessage("部门信息不能为空");
        });

        // HardwareResource专属验证规则
        When(x => x.Resource is HardwareResource, () =>
        {
            RuleFor(x => ((HardwareResource)x.Resource).AssetTag)
                .NotEmpty()
                .Matches(@"^HW-\d{6}$")
                .WithMessage("资产标签格式必须为HW-xxxxxx(6位数字)");
            RuleFor(x => ((HardwareResource)x.Resource).PurchaseDate)
                .LessThanOrEqualTo(DateTime.Now)
                .WithMessage("采购日期不能晚于当前时间");
        });

        // OtherResource专属验证规则
        When(x => x.Resource is OtherResource, () =>
        {
            RuleFor(x => ((OtherResource)x.Resource).ResourceType)
                .IsInEnum()
                .WithMessage("资源类型必须是有效的枚举值");
            RuleFor(x => ((OtherResource)x.Resource).Description)
                .MinimumLength(10)
                .WithMessage("资源描述长度不能少于10个字符");
        });
    }
}

方案二:为每种资源类型单独实现验证器,再引入到泛型验证器

这种方式遵循单一职责原则,将不同资源的验证逻辑拆分到独立的验证器中,再在泛型验证器中通过SetValidator引入:

第一步:实现各资源类型的专属验证器

public class HumanResourceValidator : AbstractValidator<HumanResource>
{
    public HumanResourceValidator()
    {
        RuleFor(x => x.EmployeeId).NotEmpty().Length(5,20);
        RuleFor(x => x.Department).NotNull().NotEmpty();
    }
}

public class HardwareResourceValidator : AbstractValidator<HardwareResource>
{
    public HardwareResourceValidator()
    {
        RuleFor(x => x.AssetTag).NotEmpty().Matches(@"^HW-\d{6}$");
        RuleFor(x => x.PurchaseDate).LessThanOrEqualTo(DateTime.Now);
    }
}

public class OtherResourceValidator : AbstractValidator<OtherResource>
{
    public OtherResourceValidator()
    {
        RuleFor(x => x.ResourceType).IsInEnum();
        RuleFor(x => x.Description).MinimumLength(10);
    }
}

第二步:修改泛型验证器引入对应验证器

public class ResourceDtoValidator<TResource> : AbstractValidator<ResourceDto<TResource>>
{
    public ResourceDtoValidator()
    {
        RuleFor(x => x.Resource)
            .NotNull()
            .SetValidator(resource => 
            {
                return resource switch
                {
                    HumanResource => new HumanResourceValidator(),
                    HardwareResource => new HardwareResourceValidator(),
                    OtherResource => new OtherResourceValidator(),
                    _ => throw new InvalidOperationException("不支持的资源类型")
                };
            });
    }
}

方案三:修复扩展方法实现

如果你坚持使用扩展方法,需要为每种资源类型编写针对性的扩展,避免泛型不匹配问题:

第一步:编写资源验证扩展方法

public static class ResourceValidationExtensions
{
    public static IRuleBuilderOptions<T, HumanResource> ValidateHumanResource<T>(this IRuleBuilder<T, HumanResource> ruleBuilder)
    {
        return ruleBuilder
            .NotNull()
            .ChildRules(hr =>
            {
                hr.RuleFor(x => x.EmployeeId).NotEmpty().Length(5,20);
                hr.RuleFor(x => x.Department).NotNull().NotEmpty();
            });
    }

    public static IRuleBuilderOptions<T, HardwareResource> ValidateHardwareResource<T>(this IRuleBuilder<T, HardwareResource> ruleBuilder)
    {
        return ruleBuilder
            .NotNull()
            .ChildRules(hw =>
            {
                hw.RuleFor(x => x.AssetTag).NotEmpty().Matches(@"^HW-\d{6}$");
                hw.RuleFor(x => x.PurchaseDate).LessThanOrEqualTo(DateTime.Now);
            });
    }

    public static IRuleBuilderOptions<T, OtherResource> ValidateOtherResource<T>(this IRuleBuilder<T, OtherResource> ruleBuilder)
    {
        return ruleBuilder
            .NotNull()
            .ChildRules(other =>
            {
                other.RuleFor(x => x.ResourceType).IsInEnum();
                other.RuleFor(x => x.Description).MinimumLength(10);
            });
    }
}

第二步:在泛型验证器中调用扩展方法

你可以选择为每种资源类型单独声明验证器(类型安全,推荐):

public class HumanResourceDtoValidator : ResourceDtoValidator<HumanResource>
{
    public HumanResourceDtoValidator()
    {
        RuleFor(x => x.Resource).ValidateHumanResource();
    }
}

public class HardwareResourceDtoValidator : ResourceDtoValidator<HardwareResource>
{
    public HardwareResourceDtoValidator()
    {
        RuleFor(x => x.Resource).ValidateHardwareResource();
    }
}

或者在通用泛型验证器中通过类型判断调用:

public class ResourceDtoValidator<TResource> : AbstractValidator<ResourceDto<TResource>>
{
    public ResourceDtoValidator()
    {
        if (typeof(TResource) == typeof(HumanResource))
        {
            var typedRule = RuleFor(x => x.Resource) as IRuleBuilder<ResourceDto<TResource>, HumanResource>;
            typedRule?.ValidateHumanResource();
        }
        else if (typeof(TResource) == typeof(HardwareResource))
        {
            var typedRule = RuleFor(x => x.Resource) as IRuleBuilder<ResourceDto<TResource>, HardwareResource>;
            typedRule?.ValidateHardwareResource();
        }
        else if (typeof(TResource) == typeof(OtherResource))
        {
            var typedRule = RuleFor(x => x.Resource) as IRuleBuilder<ResourceDto<TResource>, OtherResource>;
            typedRule?.ValidateOtherResource();
        }
        else
        {
            RuleFor(x => x.Resource).NotNull().WithMessage("不支持的资源类型");
        }
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 12:15:48