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
相关产品推荐
相关产品推荐

