激活 Autofac .NET Core Web API 时检测到循环组件依赖项

Posted

技术标签:

【中文标题】激活 Autofac .NET Core Web API 时检测到循环组件依赖项【英文标题】:Circular component dependency detected while activating Autofac .NET Core Web API 【发布时间】:2021-11-29 06:44:54 【问题描述】:

IAfterSaleService 接口和 AfterSaleService 类有问题,我对这些 cs 文件使用了 autofac IoC。但是,虽然我制作的构造函数注入适用于某些接口,但售后服务不适用于某些构造函数注入。例如, 虽然 IAfterService 在 AfterServiceController 的构造函数中起作用,但在同目录下的其他服务(如 OrderService.cs...)的构造函数中不起作用

我收到以下异常响应

Autofac.Core.DependencyResolutionException:激活 Retail.Business.Concretes.AfterSaleService -> Retail.Business.Concretes.OrderService 时引发异常。 ---> Autofac.Core.DependencyResolutionException:检测到循环组件依赖:Retail.Business.Concretes.AfterSaleService -> Retail.Business.Concretes.OrderService -> Retail.Business.Concretes.AfterSaleService。

使用以下堆栈跟踪:

   at Autofac.Core.Resolving.Middleware.CircularDependencyDetectorMiddleware.Execute(ResolveRequestContext context, Action`1 next)
   at Autofac.Core.Resolving.Pipeline.ResolvePipelineBuilder.<>c__DisplayClass14_0.<BuildPipeline>b__1(ResolveRequestContext ctxt)
   at Autofac.Core.Pipeline.ResolvePipeline.Invoke(ResolveRequestContext ctxt)
   at Autofac.Core.Resolving.ResolveOperation.GetOrCreateInstance(ISharingLifetimeScope currentOperationScope, ResolveRequest request)
   at Autofac.Core.Resolving.Pipeline.DefaultResolveRequestContext.ResolveComponent(ResolveRequest request)
   at Autofac.Core.Activators.Reflection.AutowiringParameter.<>c__DisplayClass0_0.<CanSupplyValue>b__0()
   at Autofac.Core.Activators.Reflection.BoundConstructor.Instantiate()
   at Autofac.Core.Activators.Reflection.ReflectionActivator.ActivateInstance(IComponentContext context, IEnumerable`1 parameters)
   at Autofac.Core.Activators.Reflection.ReflectionActivator.<ConfigurePipeline>b__11_0(ResolveRequestContext ctxt, Action`1 next)
   at Autofac.Core.Resolving.Middleware.DelegateMiddleware.Execute(ResolveRequestContext context, Action`1 next)
   at Autofac.Core.Resolving.Pipeline.ResolvePipelineBuilder.<>c__DisplayClass14_0.<BuildPipeline>b__1(ResolveRequestContext ctxt)
   at Autofac.Core.Resolving.Middleware.DisposalTrackingMiddleware.Execute(ResolveRequestContext context, Action`1 next)
   at Autofac.Core.Resolving.Pipeline.ResolvePipelineBuilder.<>c__DisplayClass14_0.<BuildPipeline>b__1(ResolveRequestContext ctxt)
   at Autofac.Core.Resolving.Middleware.ActivatorErrorHandlingMiddleware.Execute(ResolveRequestContext context, Action`1 next)
   --- End of inner exception stack trace ---
   at Autofac.Core.Resolving.Middleware.ActivatorErrorHandlingMiddleware.Execute(ResolveRequestContext context, Action`1 next)
   at Autofac.Core.Resolving.Pipeline.ResolvePipelineBuilder.<>c__DisplayClass14_0.<BuildPipeline>b__1(ResolveRequestContext ctxt)
   at Autofac.Core.Pipeline.ResolvePipeline.Invoke(ResolveRequestContext ctxt)
   at Autofac.Core.Resolving.Middleware.RegistrationPipelineInvokeMiddleware.Execute(ResolveRequestContext context, Action`1 next)
   at Autofac.Core.Resolving.Pipeline.ResolvePipelineBuilder.<>c__DisplayClass14_0.<BuildPipeline>b__1(ResolveRequestContext ctxt)
   at Autofac.Core.Resolving.Middleware.SharingMiddleware.<>c__DisplayClass5_0.<Execute>b__0()
   at Autofac.Core.Lifetime.LifetimeScope.CreateSharedInstance(Guid id, Func`1 creator)
   at Autofac.Core.Lifetime.LifetimeScope.CreateSharedInstance(Guid primaryId, Nullable`1 qualifyingId, Func`1 creator)
   at Autofac.Core.Resolving.Middleware.SharingMiddleware.Execute(ResolveRequestContext context, Action`1 next)
   at Autofac.Core.Resolving.Pipeline.ResolvePipelineBuilder.<>c__DisplayClass14_0.<BuildPipeline>b__1(ResolveRequestContext ctxt)
   at Autofac.Core.Resolving.Pipeline.ResolvePipelineBuilder.<>c__DisplayClass14_0.<BuildPipeline>b__1(ResolveRequestContext ctxt)
   at Autofac.Core.Resolving.Middleware.CircularDependencyDetectorMiddleware.Execute(ResolveRequestContext context, Action`1 next)
   at Autofac.Core.Resolving.Pipeline.ResolvePipelineBuilder.<>c__DisplayClass14_0.<BuildPipeline>b__1(ResolveRequestContext ctxt)
   at Autofac.Core.Pipeline.ResolvePipeline.Invoke(ResolveRequestContext ctxt)
   at Autofac.Core.Resolving.ResolveOperation.GetOrCreateInstance(ISharingLifetimeScope currentOperationScope, ResolveRequest request)
   at Autofac.Core.Resolving.ResolveOperation.ExecuteOperation(ResolveRequest request)
   at Autofac.Core.Resolving.ResolveOperation.Execute(ResolveRequest request)
   at Autofac.Core.Lifetime.LifetimeScope.ResolveComponent(ResolveRequest request)
   at Autofac.ResolutionExtensions.TryResolveService(IComponentContext context, Service service, IEnumerable`1 parameters, Object& instance)
   at Autofac.ResolutionExtensions.ResolveOptionalService(IComponentContext context, Service service, IEnumerable`1 parameters)
   at Autofac.ResolutionExtensions.ResolveOptional(IComponentContext context, Type serviceType, IEnumerable`1 parameters)
   at Autofac.ResolutionExtensions.ResolveOptional(IComponentContext context, Type serviceType)
   at Autofac.Extensions.DependencyInjection.AutofacServiceProvider.GetService(Type serviceType)
   at Microsoft.Extensions.DependencyInjection.ActivatorUtilities.GetService(IServiceProvider sp, Type type, Type requiredBy, Boolean isDefaultParameterRequired)
   at lambda_method429(Closure , IServiceProvider , Object[] )
   at Microsoft.AspNetCore.Mvc.Controllers.ControllerActivatorProvider.<>c__DisplayClass4_0.<CreateActivator>b__0(ControllerContext controllerContext)
   at Microsoft.AspNetCore.Mvc.Controllers.ControllerFactoryProvider.<>c__DisplayClass5_0.<CreateControllerFactory>g__CreateController|0(ControllerContext controllerContext)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeInnerFilterAsync()
--- End of stack trace from previous location ---
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|19_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
   at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
   at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
   at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext)
   at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider)
   at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)   

我的 IoC 如下。我说IAfterService在以下服务的构造函数中不起作用

public class ContainerModule : Module

    protected override void Load(ContainerBuilder builder)
    

        builder.RegisterType<AfterSaleService>().As<IAfterSaleService>().SingleInstance();
        builder.RegisterType<EfAfterSaleDal>().As<IAfterSaleDal>().SingleInstance();

        builder.RegisterType<ProductService>().As<IProductService>().SingleInstance();
        builder.RegisterType<EfProductDal>().As<IProductDal>().SingleInstance();

        builder.RegisterType<CustomerService>().As<ICustomerService>().SingleInstance();
        builder.RegisterType<EfCustomerDal>().As<ICustomerDal>().SingleInstance();

        builder.RegisterType<OrderService>().As<IOrderService>().SingleInstance();
        builder.RegisterType<EfOrderDal>().As<IOrderDal>().SingleInstance();

        builder.RegisterType<EfOrderDetailDal>().As<IOrderDetailDal>().SingleInstance();
        builder.RegisterType<OrderDetailService>().As<IOrderDetailService>()
            .SingleInstance();
    

但它在控制器的构造函数中起作用

private IAfterSaleService _afterSaleService;

public AfterSalesController(IAfterSaleService afterSaleService)

    _afterSaleService = afterSaleService;

并且它在 AfterService.cs 的服务相同目录下不起作用,我希望它在 OrderService.cs 中起作用,因为我需要它 注意:其他服务(如 ICustomerService、IProductService...)仅在 OrderService 的构造函数中起作用 IAfterSaleService 不起作用

    public class OrderService : IOrderService
    
        private IOrderDal _orderDal;
        private ICustomerService _customerService;
        private IProductService _productService;
        private IOrderDetailService _orderDetailService;
        private IAfterSaleService _afterSaleService;

        public OrderService(
            IOrderDal orderDal,
            ICustomerService customerService,
            IProductService productService,
            IOrderDetailService orderDetailService,
            IAfterSaleService afterSaleService)
        
            _orderDal = orderDal;
            _customerService = customerService;
            _productService = productService;
            _orderDetailService = orderDetailService;
            _afterSaleService = afterSaleService;
        
    

AfterService.cs

    public class AfterSaleService : IAfterSaleService
    
        private IAfterSaleDal _afterSaleDal;
        private IOrderService _orderService;

        public AfterSaleService(
            IAfterSaleDal afterSaleDal, IOrderService orderService)
        
            _afterSaleDal = afterSaleDal;
            _orderService = orderService;
        
    

【问题讨论】:

IAfterSaleService 实现的 ctor 的代码是? 请在问题中添加代码。 我为 IAfterSaleService 实现的 ctor 添加了代码 在section 6.3 的DIPP&P 中,Mark Seemann 和我认为循环依赖通常是由Single Responsibility Principle 违规引起的。在看到OrderService 的名称和依赖数量之后,我相信您的循环依赖也是由违反 SRP 引起的。您可能需要考虑将 OrderService 拆分为多个较小的服务。 【参考方案1】:

错误很明显:

检测到循环组件依赖

您的AfterSaleService 依赖于IOrderServiceOrderService 依赖于IAfterSaleService,由于显而易见的原因,这将使DI 容器在构建过程中无法解决依赖关系。就我个人而言,我尽量不注入相同“级别”的依赖项,即存储库不接受存储库、服务 - 服务和控制器不依赖于其他控制器,所以我的建议是重构你的应用程序,这样你就不需要这样的注入(可能会引入更多层或将通用代码移动到静态帮助程序或其他东西)。

如果由于工作量大而无法重写 ATM,您现在可以使用解决该问题的方法 - 注入与 Func 相同级别的依赖项。 Autofac 提供开箱即用的Func 分辨率(即,如果您注册IService,则无需任何额外注册即可解析Func&lt;IService&gt;),因此您可以执行以下操作:

public OrderService(..., Func<IAfterSaleService> afterSaleServiceFactory)

    .... 
    _afterSaleService = new Lazy<IAfterSaleService>(afterSaleServiceFactory);

public AfterSaleService(IAfterSaleDal afterSaleDal, 
     Func<IOrderService> orderServiceFactory)

    _afterSaleDal = afterSaleDal;
    _orderService = new Lazy<IAfterSaleService>(orderServiceFactory);

并使用延迟解析的依赖项 - _afterSaleService .Value, _orderService.Value

UPD

实际上,Autofac 似乎也开箱即用 supports Lazy,所以这可以更容易:

public OrderService(..., Lazy<IAfterSaleService> afterSaleServiceFactory)

    .... 
    _afterSaleService = afterSaleService;

【讨论】:

好的,非常感谢。我理解我的错误。我会改变我的设计。但我不明白工作是如何运作的 @notcontrol 您注入工厂(func)而不是依赖项本身,因此在您需要使用它时解决(即当您需要 _afterSaleService 时调用 _afterSaleServiceFactory 或初始化惰性值),而不是在构建服务时。不过,您仍然可能对这种设计有疑问(即,如果 A 中的某些方法需要调用 B,而 B 需要调用 A,而 B 需要调用 B ...您最终会得到 SO),但根据我的经验,通常它不会发生。

以上是关于激活 Autofac .NET Core Web API 时检测到循环组件依赖项的主要内容,如果未能解决你的问题,请参考以下文章

ASP.NET Core Web 应用程序系列- 在ASP.NET Core中使用Autofac替换自带DI进行构造函数和属性的批量依赖注入(MVC当中应用)

ASP.NET Core Web 应用程序系列- 在ASP.NET Core中使用Autofac替换自带DI进行构造函数和属性的批量依赖注入(MVC当中应用)

.NET Core下自带容器IServiceCollection以及AutoFac以及AutoFac中AOP简介

[Asp.Net Core]Autofac整合.NET5 MVC

[Asp.Net Core]Autofac整合.NET5 MVC

.Net Core AutoFac 使用方法讲解大全,具体详细使用知识总结