我可以使用 instanceCreator 上下文(aka )注册单个注册项Func<T>
,但 RegisterAll 似乎没有相同的限额。
TL;DR - 找到接受的答案并查看更新 2(或跳至此问题的更新 3)
这就是我想要做的:
container.RegisterAll<IFileWatcher>(
new List<Func<IFileWatcher>>
{
() => new FileWatcher(
@".\Triggers\TriggerWatch\SomeTrigger.txt",
container.GetInstance<IFileSystem>()),
() => new FileWatcher(
@".\Triggers\TriggerWatch\SomeOtherTrigger.txt",
container.GetInstance<IFileSystem>())
});
我尝试根据先前的 Stack Overflow 答案为多个注册添加扩展,但似乎最后一个获胜:
public static class SimpleInjectorExtensions
{
public static void RegisterAll<TService>(this Container container,
IEnumerable<Func<TService>> instanceCreators)
where TService : class
{
foreach (var instanceCreator in instanceCreators)
{
container.RegisterSingle(typeof(TService),instanceCreator);
}
container.RegisterAll<TService>(typeof (TService));
}
}
我也很好奇为什么首先需要RegisterAll
存在。这是我使用过的 5 个依赖注入容器中的第一个。其他的只是允许您针对服务注册多种类型,然后通过调用Resolve<IEnumerable<TService>>
(autofac) 或GetAllInstances<TService>
(SimpleInjector 和 Ninject) 将它们全部加载。
更新
为了更清楚起见,我正在尝试构建一个项目列表,我可以将其传递给处理每个单独项目的复合材料。它遇到了与上述相同的问题,因为它属于一组任务,这些任务都被注册为基于调度、触发器和事件 (Rx) 运行。暂时删除所有寄存器并删除其他一些内容:
container.Register<ITask>(() => new FileWatchTask(
container.GetInstance<IFileSystem>(),
container.GetInstance<IMessageSubscriptionManagerService>(),
configuration,
container.GetAllInstances<IFileWatcher>()));
你可以看到我正在抓取之前注册的文件观察者的所有实例。
我需要知道的是这个问题的简单解决方法以及何时实施(或者如果没有,为什么不会实施)。鉴于 Simple Injector 设计的当前限制,我也将接受这是不可能的。我不能接受的是我需要改变和调整我的架构以满足工具的限制。
更新 2
让我们谈谈 OCP(Open Closed Principle aka the O in SOLID)以及我在 SimpleInjector 在某些情况下如何打破这一特定原则的印象。
开闭原则就是这样,对扩展开放,对修改关闭。这意味着您可以在不更改其源代码的情况下更改实体的行为。
现在让我们转向一个相关的例子:
var tasks = container.GetAllInstances<ITask>();
foreach (var task in tasks.OrEmptyListIfNull())
{
//registers the task with the scheduler, Rx Event Messaging, or another trigger of some sort
task.Initialize();
}
注意那是多么干净。为了能够做到这一点,我需要能够注册接口的所有实例:
container.RegisterAll<ITask>(
new List<Func<ITask>>{
() => new FileWatchTask(container.GetInstance<IFileSystem>(),container.GetInstance<IMessageSubscriptionManagerService>(),configuration,container.GetAllInstances<IFileWatcher>()),
() => new DefaultFtpTask(container.GetInstance<IFtpClient>(),container.GetInstance<IFileSystem>()),
() => new DefaultImportFilesTask(container.GetInstance<IFileSystem>())
}
);
对?所以这里的教训是,这很好并且符合 OCP。我可以通过添加或删除已注册的项目来更改任务运行器的行为。打开扩展,关闭修改。
现在让我们专注于尝试按照以下答案中建议的方式进行操作(在第二次更新之前,最终回答了这个问题),作者给人的印象是更好的设计。
让我们从维护者提到的良好注册设计开始。我得到的观点是我必须牺牲我的代码以某种方式使 ITask 更灵活地使用 SimpleInjector:
container.Register<ITask<SomeGeneric1>(() => new FileWatchTask(container.GetInstance<IFileSystem>(),container.GetInstance<IMessageSubscriptionManagerService>(),configuration,container.GetAllInstances<IFileWatcher>()));
container.Register<ITask<SomeGeneric2>(() => new DefaultFtpTask(container.GetInstance<IFtpClient>(),container.GetInstance<IFileSystem>()));
container.Register<ITask<SomeGeneric3>(() => new DefaultImportFilesTask(container.GetInstance<IFileSystem>()));
现在让我们看看这如何改变我们的设计:
var task1 = container.GetInstances<ITask<SomeGeneric1>();
task1.Initialize();
var task2 = container.GetInstances<ITask<SomeGeneric2>();
task2.Initialize();
var task3 = container.GetInstances<ITask<SomeGeneric3>();
task3.Initialize();
哎哟。您可以看到每次我从容器注册中添加或删除项目时,我现在还需要更新另一段代码。一个更改的两个修改位置,我打破了多个设计问题。
你可能会说我为什么要向容器索要这个?好吧,这是在启动领域,但如果我不是,让我们探索一下。
所以我将使用构造函数注入来说明为什么这是不好的。首先让我们将我的示例视为构造注入。
public class SomeClass {
public SomeClass(IEnumerable<ITask> tasks){}
}
干净整洁。
现在,让我们切换回我对已接受答案观点的理解(同样在更新 2 之前):
public class SomeClass {
public SomeClass(ITask<Generic1> task1,
ITask<Generic2> task2,
ITask<Generic3> task3
) {}
}
哎哟。每次我必须编辑多个代码区域时,我们甚至都不要开始了解这个设计有多糟糕。
这里有什么教训?我不是世界上最聪明的人。我维护(或尝试维护:))多个框架,并且我不会假装我比其他人了解更多或更好。我的设计感可能是有偏差的,或者我可能会以某种我还没有想到的未知方式限制其他人。我敢肯定,作者在提供设计建议时是善意的,但在某些情况下,它可能会让人讨厌(而且有点居高临下),尤其是对于我们这些知道自己在做什么的人来说。
更新 3
因此,维护者在更新 2 中回答了这个问题。我试图使用 RegisterAll,因为我没有想到我可以使用Register<IEnumerable<T>>
(不幸的是文档没有指出这一点)。现在看起来很明显,但是当人们从其他 IoC 框架中跳出来时,他们背负着一些包袱,可能会错过设计中的这种令人敬畏的简化!我错过了,我的腰带下还有 4 个其他 DI 容器。希望他将其添加到文档中或更好地调用它。