Simple Injector 中的异步工厂

Async factory in Simple Injector

我正在配置 Quartz 库以使用 Simple Injector,但我无法正确注册它。问题是 GetScheduler() 是异步的,这段代码注册的是 Task<IScheduler> 而不是 IScheduler

 container.Register(async () =>
 {
      return await container.GetInstance<ISchedulerFactory>().GetScheduler();
 });

如何在异步的Simple Injector中注册工厂方法?当然,我可以使用 TaskResult 等待结果,但也许还有其他方法?

虽然可以注册并注入一个Task<ISceduler>,但是Simple Injector不支持异步工厂方法的注册,因为它的GetInstance方法是同步的

事实上,没有 DI 容器支持这个,也不应该。对象组合应该是 fast and reliable 并且在对象组合期间不应发生 I/O 操作。

在对象组合期间进行 I/O(因此是异步的)操作 运行 会导致操作变得缓慢、不可靠,并使测试对象构造变得更加困难(因为外部 I/O 资源必须在此期间可用)。

相反,异步操作应该移动到对象组合之前或对象组合之后。 Before Object Composition 是指在应用程序启动期间,即一次性启动初始化,而在Object Composition 之后发生的异步操作是由对构建的对象图上的组件的调用触发的。

您的情况的正确解决方案取决于几个因素。

如果您的应用程序只需要一个 IScheduler,则可以在应用程序启动时调用一次 factory.GetScheduler 并将 IScheduler 注册为 Singleton在容器中。 关于进行异步启动初始化。

但是如果 IScheduler 不能是单例,这意味着您使用 IScheduler 的代码需要变成异步的(它可能已经是)。这可能意味着您需要将 ISchedulerFactory 注入到需要使用 IScheduler 的组件中。这样你就可以等待 GetScheduler 方法。

另一个常见的选择是创建我们正在使用的抽象的虚拟代理实现,在本例中为 IScheduler。然而,这在 Quartz 的情况下不起作用,因为

  1. IScheduler 接口有很多成员,为此创建虚拟代理非常麻烦。
  2. 它要求 IScheduler 的所有成员都是异步的,但也有不少不是异步的,例如 IsStarted。因此,创建特定于应用程序的抽象更有意义。

这意味着将 IScheduler 的使用隐藏在异步特定于应用程序的抽象之后。这种抽象将隐藏使用调度程序的复杂性,并且通过使其方法异步,您可以允许通过实现这种新抽象来延迟创建调度程序。此实现将是一个隐藏调度程序复杂性的适配器。