我正在使用Autofac来处理应用程序中的依赖注入。为了不必显式执行每个注册,我首先使用RegisterAssemblyTypes().AsImplementedInterfaces()
,然后只处理需要特殊配置的那些。
我有一个名为 IToolStripPopulator
的接口,它有几个不同的实现,其中一些正在使用中,但有些不再使用(因为我正在尝试 SOLIDly 工作并遵守 OCP,如果我需要不同的功能,我通常不会更改它们,而是让它们单独创建新的,满足我现在需要的功能)。因此,我需要以两种方式覆盖接口的自动注册:
- 一个实现是注入到我的主要逻辑中,并充当其他一些实现的装饰器。我自然希望通过
As<IToolStripPopulator>()
注册并完成它,因为这是我的应用程序功能当前依赖的实现。 - 这些其他实现是"内部"填充器,仅用于该装饰器类。要注入它们,有两种方法:
- 将外部实现的构造函数与特定类型显式连接起来;这不是很好,也因为该构造函数必须采用我现在正在使用的内部填充器的确切数量,这不符合 OCP 标准。
- 让外部实现
IEnumerable<IToolStripPopulator>
;这是我想做的,但这也是我卡住的地方。
我不能让 Autofac 自行解决该IEnumerable
,因为它只会解决接口的所有实现,甚至是我不再使用的实现以及我不想要的"外部"(这可能会导致解析期间的无限循环)。
所以我想做的是这样的:
// the inner populators
builder.RegisterType<BrowsersMenuPopulator>().Named<IToolStripPopulator>("inner");
builder.RegisterType<ThreadsafeConnectionMenuPopulator>().Named<IToolStripPopulator>("inner");
// the decorator implementation
builder.RegisterType<BrowserAndConnectionMenuPopulator>().As<IToolStripPopulator>().WithParameter( ? );
但是此时无法实际访问现有注册。
....在这里,我又尝试了一件事,我实际上并没有想到会起作用,但它确实做到了。
builder.Register(c => c.ResolveNamed<IEnumerable<IToolStripPopulator>>("inner")).As<IEnumerable<IToolStripPopulator>>();
正如我所期望的那样,不会尝试解析名为"inner"的IEnumerable<IToolStripPopulator>
注册,而是IEnumerable
名为"inner"的IToolStripPopulator
注册 - 完全符合我的需要。
然后,这将正确解决并注入到装饰器实现中,只需注册该As<IToolStripPopulator>()
而无需进一步配置。
从形式方面来看,我实际上更愿意能够说明直接通过装饰器注册解析的名称,因为这会更直观和灵活(如果我需要解析几个IEnumerable<IToolStripPopulator>
怎么办?),但在许多情况下,这应该足够了。
@codinghorror的"橡皮鸭解决问题"的概念统治着我的每一个工作日......
直接指定我知道的唯一方法是直接在您的注册码中"new-up":
//decorator impl
builder.Register(c => new BrowserAndConnectionMenuPopulator(c.ResolveNamed<IEnumerable<IToolStripPopulator>>("inner"),...)).As<IToolStripPopulator>();
这样做的好处是可以直接在注册中指定确切的必需依赖项,但不利于在重新配置BrowserAndConnectionMenuPopulator
依赖项时失去灵活性。 为了完整起见,我将其包括在内,但老实说,我更喜欢您发现的解决方案。