【问题标题】:How to dynamically inject providers in NestJs如何在 NestJs 中动态注入提供程序
【发布时间】:2021-11-07 16:29:11
【问题描述】:

我的团队正在尝试确定一种动态 DI 的方法,这样就可以有一个批量插入点,而无需拼写 + 导入每个模块。

@Module({
  ...
  providers: [
    WorkerService,
    WorkerResolver,
    Worker2Service,
    Worker2Resolver........
  ]
})

想要实现

var allModules = ... // logic here to include all my resolvers, or all my services

@Module({
  ...
  providers: [
    ...allModules
  ]
})

【问题讨论】:

  • 我能想到的注册每个 provider 的唯一简单方法是读取导出提供程序的所有文件(利用 convention over configuration 模型) 你想注册
  • @MicaelLevi 是的,这就是我们所困的地方,DI 发生在编译时,如果我尝试读取文件,它会报告 dist/ 目录中不存在任何内容。
  • 我认为你可以通过使用动态模块来规避这个问题

标签: node.js dependency-injection nestjs metaprogramming


【解决方案1】:

您可以使用glob 包动态查找模块,然后使用NestJs 动态模块功能动态加载它们。

假设您的所有工作文件都存储在名为workers 并扩展名为.worker.ts 的目录中:

    @Module({})
    export class WorkerModule {
      static forRootAsync(): DynamicModule {
        return {
          module: WorkerModule ,
          imports: [WorkerCoreModule.forRootAsync()],
        };
      }
    }
export class WorkerCoreModule {

  static async forRootAsync(): Promise<DynamicModule> {
    // Feel free to change path if your structure is different
    const workersPath = glob.sync('src/**/workers/*.worker.ts');
    const workersRelativePathWithoutExt = modelsPath
      // Replace src, because you are probably running the code
      // from dist folder
      .map((path) => path.replace('src/', './../'))
      .map((path) => path.replace('.ts', ''));
    const workerProviders: Provider<any>[] = [];
    const importedModules = await Promise.all(
      workersRelativePathWithoutExt.map((path) => import(path)),
    );
    importedModules.forEach((modules) => {
      // Might be different if you are using default export instead
      const worker = modules[Object.keys(modules)[0]];
      workerProviders.push({
        provide: worker.name, 
        useValue: worker,
      });
    });

    return {
      module: WorkerCoreModule,
      providers: [...workerProviders],
      // You can omit exports if providers are meant to be used
      // only in this module
      exports: [...workerProviders],
    };
  }
}

现在假设你有一个简单的工作类,路径为src/anyModule/workers/simple-worker.ts,你可以这样使用它:

class WrokersService {
  constructor(@Inject('SimpleWorker') simpleWroker: SimpleWorker) {}
  .
  .
  .
}

如果你想省略 @Inject('SimpleWorker') 并自动注入像 NestJs 服务这样的模块,那么你需要对 WorkerCoreModule 进行这些更改:

workerProviders.push({
  provide: worker,
});

但是,为了使其正常工作,您需要确保您的 workers 类使用 @injectable() 装饰。

【讨论】:

  • 我认为为此您仍然需要将 SimpleWorker 注入 WorkerService。此外,如果我正确理解您的方法,则必须假设文件都从 typescript 转换为 1 到 1 到 javscript。实际上,我们的应用程序被捆绑到一个文件中。所以解决方案需要在编译时发生。
  • 你在使用 Webpack 吗?它如何在一个文件中结束?
  • 是的,我们正在将其编译成类似于客户端应用程序的 main.js
猜你喜欢
  • 2021-11-17
  • 2020-04-28
  • 2019-05-09
  • 2022-09-23
  • 2021-04-23
  • 2020-12-05
  • 2020-06-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多