Autofac无法在两个构造函数之间进行解析(如果明确更具体)
如果我们将委托注册为组件,则AutowiringParam在解析时将采用与NamedParameter相同的优先级!Autofac无法在两个构造函数之间进行解析(如果明确更具体)
这里有一个归结例如:
public class AParam { }
public class BParam : IParam { }
public interface IParam { }
public interface IAThing { }
public class AThing : IAThing
{
public AThing(AParam aParam) { }
public AThing(BParam anotherParam) { }
}
static void Main(string[] args)
{
IContainer c = (new ContainerBuilder()).Build();
var anotherBuilder = new ContainerBuilder();
anotherBuilder.RegisterType<AThing>().As<IAThing>().InstancePerDependency();
anotherBuilder.Register((context, parm) => new BParam()).As<BParam>().InstancePerDependency();
anotherBuilder.Update(c);
object aParam = new AParam();
//Throws exception, it's unable to decide which constructor to use....
var instance = c.Resolve(typeof(IAThing), new[] {new NamedParameter("aParam", aParam) });
}
在这种情况下,我指定正是我想要的NamedParameter“aParam”,但AutowiritingParameter可以填写在BParam,所以它不知道哪个构造函数可以选择(因为它们的参数长度都相等)。
我该怎么做才能让Autofac优先使用我提到的指定参数的特定构造函数?没有理由为什么我想让我的参数被忽略,如果它使用AutowiringParameter就会是这种情况。
我可以使用“UsingConstructor”,但只要BParam派生自AParam,我们就会回到相同的含糊之处。 在这里,我明确要求具有命名参数的构造函数。 任何想法?
编辑:
使用在构造函数默认参数可以使Autofac忽略用户指定的命名参数完全,没有错误,你不会发现!
public class AThing : IAThing
{
public AThing(AParam aParam) { }
public AThing(BParam anotherParam, bool def = true) { }
}
我不确定你在目前情况下能否真的赢。什么是“明确更具体”给你不是必然 100%更具体 - 一旦你提供了参数,Autofac采用提供的参数以及自动布线参数,并试图确定,给出所有可用的信息,哪些构造函数它可以实现。所有参数相同 - 无论它们来自注册时间(builder.RegisterType<T>().WithParameter(...)
),解析时间(scope.Resolve<T>(...)
)还是自动装配 - 应该调用哪个构造函数?
如果有优先级,比如参数X比参数Y更重要,它实际上可能开始变得非常复杂,并且创建一些难以排除故障的行为。解决时间参数是否优先于注册时间参数?你可以覆盖这种行为?为什么或者为什么不?它变得混乱。
无论如何,这就是为什么它的行为如此并且可能会继续以这种方式进入未来。
但是,你有两个选择:
选项1 - UsingConstructor
:你只需指定在测试中使用的构造是一个需要BParam
如果总是是怎么回事。
builder.RegisterType<AThing>()
.As<IAThing>()
.UsingConstructor(typeof(BParam));
我会建议创建一个自定义IConstructorSelector
潜在能力,但你没有得到入参数那里,只是一个构造函数列表可以根据已提供的一组参数满足 。没有足够的信息继续下去。有关更多信息,请参阅此答案的结尾。
选项2 - Lambda注册:如果我是你,这可能是我会做的。它基于example in the docs here。它并不像一个班轮实现,但它可以让你你想要的效果:这是不是漂亮
// This is the "default" behavior registration for when
// no parameters are provided. Note it's named, though, so
// the actual default registration for IAThing will be the
// lambda.
builder.RegisterType<AThing>().Named<IAThing>("default-thing");
// This is what will run when you Resolve<IAThing>()
builder.Register((ctx, p) => {
var aorb = p
.OfType<NamedParameter>()
.Where(n => n.Name == "aParam")
.FirstOrDefault();
if (aorb != null)
{
// You passed the parameter so use it.
return new AThing((BParam)aorb.Value);
}
else
{
// Use the default reflection-based registration, above.
return ctx.ResolveNamed<IAThing>("default-thing", p);
}
}).As<IAThing>();
,但它能够完成任务。如果你有很多这样的内容,你可以将其中的一部分包装到一个扩展方法中,该方法生成默认的注册ID,处理if/else逻辑等等。
我认为这可能是有价值的,以使更多的功能构造选择,以便UsingConstructor
可以做得更多。为此,我opened up this issue for you看看,如果这将是一个有趣的增强。
我认为明显有更高的“特异性”,因为我想明确使用NamedParameter进行解析。如果我没有,我会不会感到惊讶,如果我得到一个异常(或者在这种情况下不是真的,因为唯一的构造函数是BParam)。自动填充已经试图抓住最具体的构造函数(通过更高的长度),但在这种情况下,具有两个自动布线参数的长度为2的构造函数将赢得我的构造函数,并带有一个在解析调用中明确命名的参数。 我想在CSS选择器中使用相同的特殊性规则,我不希望它神奇地工作。但一个想法 – GettnDer
我认为这可以用任何方式来论证,但我的解释的重点是解释它为什么是这样,而不是开始一个侧面讨论。我希望我的回答有帮助。 –
它的确如此,不幸的是,这个应用程序是一个支持很多潜在的野兽并且做出巨大修改的野兽。老版本的Autofac曾经工作过,有人更新过Autofac,我只好忍受支持这个更新的子弹,我不会这样做自己。 我回复了你的票,非常感谢你的时间 – GettnDer
我注意到你的'Resolve'使用'typeof(IAThing)'但不是具体类'AThing'你试图指定构造函数吗?另外,[this](http://docs.autofac.org/en/latest/register/registration.html#selection-of-an-implementation-by-parameter-value)能为您提供任何帮助吗?它还说“如果创建实例的委托被声明并且使用委托工厂,则可以实现清洁,类型安全的语法。”您可能可以通过代理和委托工厂解决此问题。 – ErikE
嗨,Erik, 一般来说,我们使用Autofac进行依赖注入,所以它可能不是我们正在试图解决的AThing(可能是测试模型),但绝对是带有命名参数“aParam”的构造函数。 我不明白为什么Autofac使用“AutowiringParameter”作为构造函数向“aParam”构造函数赋值相同的“特异性”(以下将其用于使用旧版本的Autofac):/它的某种忽略我想要一个特定的参数名称(键入的参数结果相同的问题btw) 感谢您的回复 – GettnDer
这是您的应用程序组件具有多个构造函数的反模式,详细解释[这里](https:// www。 cuttingedge.it/blogs/steven/pivot/entry.php?id=97)。 – Steven