繁体   English   中英

Autofac如何根据上下文使用默认和非默认ctor

[英]Autofac how to use both default and non-default ctor based on context

我有两个类,例如:

public class P 
{
    public P( Guid g )  {}
}
public class Lp
{
    public Lp() {}
    public Lp( P p ) {}
}   

我将它们都注册到 Autofac 6.2.0 中,例如:

builder.RegisterType< P >();
builder.RegisterType< Lp >();

现在我想使用默认 ctor 解析 Lp。 当我尝试像这样解决 Lp 时:

var c = builder.Build();
using( var scope = c.BeginLifetimeScope() )
{
   scope.Resolve<Lp>();
}

我收到一个错误,即无法使用可用的服务和参数调用为类型P找到的任何构造函数。 嗯,是的,这是真的。

显然,由于类型P已注册,Autofac 认为它应该能够创建P并使用它来创建Lp ,而不是认识到它无法在没有Guid的情况下创建P并因此使用默认的Lp ctor。 如果我不注册P ,则解析成功。 如果我将默认 ctor 添加到P ,则解析成功(除了它使用非默认Lp ctor)。

诊断不是很有趣,但它看起来像:

{   Resolve Request Starting   {
    Service: CtsMasterTs.Positions.Lp
    Component: CtsMasterTs.Positions.Lp

    Pipeline:
    -> CircularDependencyDetectorMiddleware
      -> ScopeSelectionMiddleware
        -> SharingMiddleware
          -> RegistrationPipelineInvokeMiddleware
            -> ActivatorErrorHandlingMiddleware
              -> DisposalTrackingMiddleware
                -> Lp (ReflectionActivator)
                  Resolve Request Starting
                  {
                    Service: CtsMasterTs.Positions.P
                    Component: CtsMasterTs.Positions.P

                    Pipeline:
                    -> CircularDependencyDetectorMiddleware
                      -> ScopeSelectionMiddleware
                        -> SharingMiddleware
                          -> RegistrationPipelineInvokeMiddleware
                            -> ActivatorErrorHandlingMiddleware
                              -> DisposalTrackingMiddleware
                                -> P (ReflectionActivator)
                                X- P (ReflectionActivator)
                              X- DisposalTrackingMiddleware
                            X- ActivatorErrorHandlingMiddleware
                          X- RegistrationPipelineInvokeMiddleware
                        X- SharingMiddleware
                      X- ScopeSelectionMiddleware
                    X- CircularDependencyDetectorMiddleware
                  }
                  Resolve Request FAILED
                    Autofac.Core.DependencyResolutionException: None of the constructors found with
'Autofac.Core.Activators.Reflection.DefaultConstructorFinder' on type
'CtsMasterTs.Positions.P' can be invoked with the available services
and parameters:
                    Cannot resolve parameter 'System.Guid g' of constructor 'Void .ctor(System.Guid)'.
                       at Autofac.Core.Activators.Reflection.ReflectionActivator.GetAllBindings(ConstructorBinder[]
availableConstructors, IComponentContext context, IEnumerable`1
parameters) in
/_/src/Autofac/Core/Activators/Reflection/ReflectionActivator.cs:line
175
                       at Autofac.Core.Activators.Reflection.ReflectionActivator.ActivateInstance(IComponentContext
context, IEnumerable`1 parameters) in
/_/src/Autofac/Core/Activators/Reflection/ReflectionActivator.cs:line
134
                       at Autofac.Core.Activators.Reflection.ReflectionActivator.<ConfigurePipeline>b__11_0(ResolveRequestContext
ctxt, Action`1 next) in
/_/src/Autofac/Core/Activators/Reflection/ReflectionActivator.cs:line
104
                       at Autofac.Core.Resolving.Pipeline.ResolvePipelineBuilder.<>c__DisplayClass14_0.<BuildPipeline>b__1(ResolveRequestContext
ctxt) in
/_/src/Autofac/Core/Resolving/Pipeline/ResolvePipelineBuilder.cs:line
262
                       at Autofac.Core.Resolving.Middleware.DisposalTrackingMiddleware.Execute(ResolveRequestContext
context, Action`1 next) in
/_/src/Autofac/Core/Resolving/Middleware/DisposalTrackingMiddleware.cs:line
32
                       at Autofac.Core.Resolving.Pipeline.ResolvePipelineBuilder.<>c__DisplayClass14_0.<BuildPipeline>b__1(ResolveRequestContext
ctxt) in
/_/src/Autofac/Core/Resolving/Pipeline/ResolvePipelineBuilder.cs:line
262
                       at Autofac.Core.Resolving.Middleware.ActivatorErrorHandlingMiddleware.Execute(ResolveRequestContext
context, Action`1 next) in
/_/src/Autofac/Core/Resolving/Middleware/ActivatorErrorHandlingMiddleware.cs:line
36
                X- Lp (ReflectionActivator)
              X- DisposalTrackingMiddleware
            X- ActivatorErrorHandlingMiddleware
          X- RegistrationPipelineInvokeMiddleware
        X- SharingMiddleware
      X- ScopeSelectionMiddleware
    X- CircularDependencyDetectorMiddleware   }   Resolve Request FAILED: Nested Resolve Failed } Operation FAILED  
Autofac.Core.DependencyResolutionException: An exception was thrown
while activating CtsMasterTs.Positions.Lp -> CtsMasterTs.Positions.P.
---> Autofac.Core.DependencyResolutionException: None of the constructors found with
'Autofac.Core.Activators.Reflection.DefaultConstructorFinder' on type
'CtsMasterTs.Positions.P' can be invoked with the available services
and parameters:   Cannot resolve parameter 'System.Guid g' of
constructor 'Void .ctor(System.Guid)'.
     at Autofac.Core.Activators.Reflection.ReflectionActivator.GetAllBindings(ConstructorBinder[]
availableConstructors, IComponentContext context, IEnumerable`1
parameters) in
/_/src/Autofac/Core/Activators/Reflection/ReflectionActivator.cs:line
175
     at Autofac.Core.Activators.Reflection.ReflectionActivator.ActivateInstance(IComponentContext
context, IEnumerable`1 parameters) in
/_/src/Autofac/Core/Activators/Reflection/ReflectionActivator.cs:line
134
     at Autofac.Core.Activators.Reflection.ReflectionActivator.<ConfigurePipeline>b__11_0(ResolveRequestContext
ctxt, Action`1 next) in
/_/src/Autofac/Core/Activators/Reflection/ReflectionActivator.cs:line
104
     at Autofac.Core.Resolving.Pipeline.ResolvePipelineBuilder.<>c__DisplayClass14_0.<BuildPipeline>b__1(ResolveRequestContext
ctxt) in
/_/src/Autofac/Core/Resolving/Pipeline/ResolvePipelineBuilder.cs:line
262
     at Autofac.Core.Resolving.Middleware.DisposalTrackingMiddleware.Execute(ResolveRequestContext
context, Action`1 next) in
/_/src/Autofac/Core/Resolving/Middleware/DisposalTrackingMiddleware.cs:line
32
     at Autofac.Core.Resolving.Pipeline.ResolvePipelineBuilder.<>c__DisplayClass14_0.<BuildPipeline>b__1(ResolveRequestContext
ctxt) in
/_/src/Autofac/Core/Resolving/Pipeline/ResolvePipelineBuilder.cs:line
262
     at Autofac.Core.Resolving.Middleware.ActivatorErrorHandlingMiddleware.Execute(ResolveRequestContext
context, Action`1 next) in
/_/src/Autofac/Core/Resolving/Middleware/ActivatorErrorHandlingMiddleware.cs:line
36
     --- End of inner exception stack trace ---
     at Autofac.Core.Resolving.Middleware.ActivatorErrorHandlingMiddleware.Execute(ResolveRequestContext
context, Action`1 next) in
/_/src/Autofac/Core/Resolving/Middleware/ActivatorErrorHandlingMiddleware.cs:line
48
     at Autofac.Core.Resolving.Pipeline.ResolvePipelineBuilder.<>c__DisplayClass14_0.<BuildPipeline>b__1(ResolveRequestContext
ctxt) in
/_/src/Autofac/Core/Resolving/Pipeline/ResolvePipelineBuilder.cs:line
262
     at Autofac.Core.Resolving.Pipeline.ResolvePipelineBuilder.<>c__DisplayClass14_0.<BuildPipeline>b__1(ResolveRequestContext
ctxt) in
/_/src/Autofac/Core/Resolving/Pipeline/ResolvePipelineBuilder.cs:line
262
     at Autofac.Core.Resolving.Middleware.SharingMiddleware.Execute(ResolveRequestContext
context, Action`1 next) in
/_/src/Autofac/Core/Resolving/Middleware/SharingMiddleware.cs:line 58
     at Autofac.Core.Resolving.Pipeline.ResolvePipelineBuilder.<>c__DisplayClass14_0.<BuildPipeline>b__1(ResolveRequestContext
ctxt) in
/_/src/Autofac/Core/Resolving/Pipeline/ResolvePipelineBuilder.cs:line
262
     at Autofac.Core.Resolving.Pipeline.ResolvePipelineBuilder.<>c__DisplayClass14_0.<BuildPipeline>b__1(ResolveRequestContext
ctxt) in
/_/src/Autofac/Core/Resolving/Pipeline/ResolvePipelineBuilder.cs:line
262
     at Autofac.Core.Resolving.Middleware.CircularDependencyDetectorMiddleware.Execute(ResolveRequestContext
context, Action`1 next) in
/_/src/Autofac/Core/Resolving/Middleware/CircularDependencyDetectorMiddleware.cs:line
94
     at Autofac.Core.Resolving.Pipeline.ResolvePipelineBuilder.<>c__DisplayClass14_0.<BuildPipeline>b__1(ResolveRequestContext
ctxt) in
/_/src/Autofac/Core/Resolving/Pipeline/ResolvePipelineBuilder.cs:line
262
     at Autofac.Core.Resolving.ResolveOperation.GetOrCreateInstance(ISharingLifetimeScope
currentOperationScope, ResolveRequest request) in
/_/src/Autofac/Core/Resolving/ResolveOperation.cs:line 150
     at Autofac.Core.Resolving.ResolveOperation.ExecuteOperation(ResolveRequest
request) in /_/src/Autofac/Core/Resolving/ResolveOperation.cs:line 182

这对我来说是个问题,因为我需要能够在有或没有P的情况下创建Lp 在代码的其他地方,我使用注入的Func<Lp>Func<P, Lp>作为工厂。 我还使用注入的Func<Guid, P>来创建P对象。

所以我的问题是 - 我该怎么做:

A) 让 Autofac 查看P ctor 并了解它不能在没有P参数的情况下使用非默认Lp ctor,或者

B)以其他方式工作一些 Autofac 魔法(例如 lambda 注册?)以允许Func<Lp>Func<P, Lp>两者都工作? 我已经简要介绍了使用 ctor 选择,但似乎每次解析类型时都会强制使用相同的 ctor。

其他信息-.Net Framework 4.8、VS2019、Windows 10、WPF。 这是一个简化的示例,演示了我的(更复杂的)应用程序代码中发生了什么。

更新 - 这是代码异味吗? 我仍在努力解决这个问题。

用例是 P 是不可变的。 Lp 是 P 的“可编辑”版本。当需要新的 P 时,我使用默认 ctor 创建一个新的 Lp,编辑属性值,然后使用 Lp.CreateP() 从它创建一个 P。 如果 P 已经存在并且需要编辑,我使用 Lp(P i_p) 创建一个新的 Lp,编辑属性值,然后使用 Lp.CreateP() 将原始 P 替换为新的 P。 这对我来说似乎很合理,但也许我错过了一些东西。

这不是一个真正的常见用例。 绝大多数的用法是:

  • Select 应该始终使用的构造函数(使用带有P的构造函数或不使用,但不是“因上下文而异”); 或者
  • 使用可以由 DI 中最可用的东西填充的构造函数(这是 Autofac 的默认行为)

这个用例更像是......“我想在这个嵌套的 scope 中使用默认的 Autofac 行为 _except,我需要覆盖使用哪个构造函数。” 我建议分析需要这样做的设计,因为它看起来像代码味道。

但是,您可以做的最好的事情是在子生命周期 scope 中重新注册Lp并强制您想要的构造函数。 解决方案将根据覆盖发生,并且应该执行您想要的操作。

var builder = new ContainerBuilder();
builder.RegisterType<P>();
builder.RegisterType<Lp>();
var container = builder.Build();

// This will use the `Lp(P)` constructor - most matched
var first = container.Resolve<Lp>();

using(var scope = container.BeginLifetimeScope(b =>
  {
    b.RegisterType<Lp>().UsingConstructor(Array.Empty<Type>());
  })
{
  // This should use the default no-parameter constructor.
  var second = scope.Resolve<Lp>();
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM