如何解决FluentValidation校验循环引用模型时的循环依赖问题
FluentValidation循环依赖问题解决方案
问题原因
报错属于DI容器层面的循环依赖异常:ObjectAValidator构造函数依赖IValidator<ObjectB>,ObjectBValidator构造函数又依赖IValidator<ObjectA>,容器构造实例时无法打破循环,因此抛出异常。即使解决DI层面的问题,若业务对象本身存在互相引用的情况,校验时还会出现无限递归导致栈溢出,需要同步处理。
解决方案
方案1:使用Lazy延迟加载依赖
.NET 原生DI容器支持自动解析Lazy<T>类型,依赖项不会在校验器构造时实例化,只有首次使用时才会解析,可直接打破构造环节的循环依赖:
public class ObjectAValidator : AbstractValidator<ObjectA> { public ObjectAValidator(Lazy<IValidator<ObjectB>> bValidator) { RuleFor(c => c.Name).NotNull(); RuleFor(c => c.AnotherObject) .NotNull() .SetValidator(bValidator.Value); } } public class ObjectBValidator : AbstractValidator<ObjectB> { public ObjectBValidator(Lazy<IValidator<ObjectA>> aValidator) { RuleFor(c => c.Name).NotNull(); RuleFor(c => c.AnotherObject) .NotNull() .SetValidator(aValidator.Value); } }
该方案无需修改原有服务注册逻辑,原有调用代码保持不变即可生效。
方案2:使用SetValidator服务工厂重载延迟获取
FluentValidation的SetValidator支持传入委托,在校验触发时才从当前上下文的服务容器中获取校验器实例,无需在校验器构造时注入依赖:
public class ObjectAValidator : AbstractValidator<ObjectA> { public ObjectAValidator() { RuleFor(c => c.Name).NotNull(); RuleFor(c => c.AnotherObject) .NotNull() .SetValidator((ctx, _) => ctx.RequestServices.GetRequiredService<IValidator<ObjectB>>()); } } public class ObjectBValidator : AbstractValidator<ObjectB> { public ObjectBValidator() { RuleFor(c => c.Name).NotNull(); RuleFor(c => c.AnotherObject) .NotNull() .SetValidator((ctx, _) => ctx.RequestServices.GetRequiredService<IValidator<ObjectA>>()); } }
该方案直接移除了构造函数的互相依赖,从根源避免DI循环问题。
额外注意事项
解决DI循环依赖后,若业务对象本身存在A→B→A的循环引用,校验会触发无限递归导致栈溢出,需要在校验规则中增加深度限制:
// 示例:限制ObjectA的嵌套校验最多2层 RuleFor(c => c.AnotherObject) .NotNull() .When(a => { int depth = 0; object current = a; while (current != null && depth < 2) { depth++; current = current switch { ObjectA aObj => aObj.AnotherObject, ObjectB bObj => bObj.AnotherObject, _ => null }; } return depth < 2; }) .SetValidator(xxx);
内容的提问来源于stack exchange,提问作者Bruno Warmling
相关产品推荐
相关产品推荐

