Autofac 如何根据上下文同时使用默认和非默认构造函数
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 >();
现在我想使用默认构造函数解析 Lp。当我尝试像这样解决 Lp 时:
var c = builder.Build();
using( var scope = c.BeginLifetimeScope() )
{
scope.Resolve<Lp>();
}
我收到一条错误消息,指出 none 类型 P
的构造函数可以使用可用的服务和参数调用。嗯,是的,这是真的。
显然,由于类型 P
已经被注册,Autofac 认为它应该能够创建一个 P
并使用它来创建 Lp
而不是认识到它没有创建没有 Guid
的 P
的方法,因此使用默认的 Lp
ctor。如果我不注册P
,解析成功。如果我将默认构造函数添加到 P
,解析成功(除了它使用非默认 Lp
构造函数)。
诊断不是很有趣,但看起来像:
{ 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
构造函数并了解它不能使用没有 P
参数的非默认 Lp
构造函数,或者
B) 以其他方式(例如 lambda 注册?)发挥 Autofac 的魔力,让 Func<Lp>
和 Func<P, Lp>
都起作用?我已经简要地查看了使用 ctor 选择,但似乎每次解析类型时都会强制使用相同的 ctor。
其他信息 - .Net Framework 4.8、VS2019、Windows10、WPF。这是一个简化的示例,演示了我的(更复杂的)应用程序代码中发生的事情。
更新 - 这是代码味道吗?我仍在努力解决这个问题。
用例是 P 是不可变的。 Lp 是 P 的“可编辑”版本。当需要新 P 时,我使用默认构造函数创建一个新 Lp,编辑 属性 值,然后使用 Lp.CreateP( ).如果 P 已经存在并且需要编辑,我使用 Lp( P i_p ) 创建一个新的 Lp,编辑 属性 值,然后使用 [=61= 将原来的 P 替换为新的 P ]()。这对我来说似乎很合理,但也许我遗漏了什么。
这并不是一个常见的用例。绝大多数用法是:
- Select the constructor that should always be used(要么使用带有
P
的构造函数,要么不使用,但不是“因上下文而异”);或者
- 使用DI中最可用的东西可以填充的构造函数(这是Autofac的默认行为)
这个用例更像是...“我想使用默认的 Autofac 行为 _except 在这个嵌套范围内,我需要覆盖使用哪个构造函数。”我建议分析需要这样做的设计,因为它看起来像代码味道。
但是,您最好的办法是在子生命周期范围内重新注册 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>();
}
我有两个类喜欢:
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 >();
现在我想使用默认构造函数解析 Lp。当我尝试像这样解决 Lp 时:
var c = builder.Build();
using( var scope = c.BeginLifetimeScope() )
{
scope.Resolve<Lp>();
}
我收到一条错误消息,指出 none 类型 P
的构造函数可以使用可用的服务和参数调用。嗯,是的,这是真的。
显然,由于类型 P
已经被注册,Autofac 认为它应该能够创建一个 P
并使用它来创建 Lp
而不是认识到它没有创建没有 Guid
的 P
的方法,因此使用默认的 Lp
ctor。如果我不注册P
,解析成功。如果我将默认构造函数添加到 P
,解析成功(除了它使用非默认 Lp
构造函数)。
诊断不是很有趣,但看起来像:
{ 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
构造函数并了解它不能使用没有 P
参数的非默认 Lp
构造函数,或者
B) 以其他方式(例如 lambda 注册?)发挥 Autofac 的魔力,让 Func<Lp>
和 Func<P, Lp>
都起作用?我已经简要地查看了使用 ctor 选择,但似乎每次解析类型时都会强制使用相同的 ctor。
其他信息 - .Net Framework 4.8、VS2019、Windows10、WPF。这是一个简化的示例,演示了我的(更复杂的)应用程序代码中发生的事情。
更新 - 这是代码味道吗?我仍在努力解决这个问题。
用例是 P 是不可变的。 Lp 是 P 的“可编辑”版本。当需要新 P 时,我使用默认构造函数创建一个新 Lp,编辑 属性 值,然后使用 Lp.CreateP( ).如果 P 已经存在并且需要编辑,我使用 Lp( P i_p ) 创建一个新的 Lp,编辑 属性 值,然后使用 [=61= 将原来的 P 替换为新的 P ]()。这对我来说似乎很合理,但也许我遗漏了什么。
这并不是一个常见的用例。绝大多数用法是:
- Select the constructor that should always be used(要么使用带有
P
的构造函数,要么不使用,但不是“因上下文而异”);或者 - 使用DI中最可用的东西可以填充的构造函数(这是Autofac的默认行为)
这个用例更像是...“我想使用默认的 Autofac 行为 _except 在这个嵌套范围内,我需要覆盖使用哪个构造函数。”我建议分析需要这样做的设计,因为它看起来像代码味道。
但是,您最好的办法是在子生命周期范围内重新注册 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>();
}