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

如何解决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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.23 21:15:05