我正在开发一个小型库,并使用Simple Injector进行我的DI。
类库有一个访问点(我猜的某种服务),它是public
,它有一些internal
服务和存储库。
我看到Simple Injector不支持使用内部构造函数的构造函数注入。 例如,我的产品服务如下:
internal class ProductService : IProductService
{
private IProductRepository _productRepository;
internal ProductService(IProductRepository repository)
{
if (repository == null) throw new ArgumentNullException("repository");
_productRepository = repository;
}
}
我的设置:
container.Register<IProductService, ProductService>();
container.Register<IProductRepository>(() => new ProductRepository());
当我运行代码时,我得到以下异常:
For the container to be able to create ProductService, it should contain exactly one public constructor, but it has 0.
我的问题:
1)注入内部类在架构/设计方面是否有效的具体原因是什么?
2)如何实现这种行为(使用不应公开的类的依赖注入)并且是否需要?
答案 0 :(得分:5)
Simple Injector试图给你一个合理的默认值。默认情况下,由于具有multiple constructors is an anti-pattern,因此仅限于使用单个公共构造函数的自动布线类型。默认的Injector只注入公共构造函数,因为Simple Injector能够安全地调用类型的构造函数,所以它必须是公共的。例如,当应用程序在(部分信任)沙箱中运行时,Simple Injector将无法调用内部构造函数,虽然可以完全信任地调用内部构造函数,但创建此类型的速度较慢。为了提高性能,最好将类型和构造函数保持公开。
除了这些技术限制外,在正常情况下,组件及其构造函数将是公共的,因为您通常总是需要访问该组件的外部使用者。此类消费者的示例是您的单元测试项目和composition root project。
因此,合理的默认值是“一个公共构造函数”,但类型本身不必是公共的,尽管解析内部类型会更慢并且可能并不总是在沙箱中工作。换句话说,当您没有在沙箱(例如Silverlight或Windows Phone)中运行时, Simple Injector将能够解析内部类型,只要它们有一个< em> public 构造函数。
但是,如果您确实需要或希望构造函数是内部的,则可以通过实现和注册自定义IConstructorResolutionBehavior
来覆盖构造函数解析行为。这是一个例子:
public class InternalConstructorResolutionBehavior : IConstructorResolutionBehavior
{
private IConstructorResolutionBehavior original;
public InternalConstructorResolutionBehavior(Container container) {
this.original = container.Options.ConstructorResolutionBehavior;
}
public ConstructorInfo GetConstructor(Type implementationType) {
if (!implementationType.GetConstructors().Any()) {
var internalCtors = implementationType.GetConstructors(
BindingFlags.Instance | BindingFlags.NonPublic)
.Where(c => !c.IsPrivate)
.ToArray();
if (internalCtors.Length == 1) return internalCtors.First();
}
return this.original.GetConstructor(implementationType);
}
}
此自定义构造函数解析行为可以按如下方式注册:
var container = new Container();
container.Options.ConstructorResolutionBehavior =
new InternalConstructorResolutionBehavior(container);
// Normal registrations here