【问题标题】:Inject correct service based on parameter根据参数注入正确的服务
【发布时间】:2020-03-30 08:39:11
【问题描述】:

假设我有两个模块正在导出 BServiceCService,这两个服务都扩展了 AService

所以代码如下所示:

abstract class AService {
    public run() {}
}

@Injectable()
export class BService extends AService {}

@Injectable()
export class CService extends AService {}

@Module({
    providers: [BService],
    exports: [BService],
})
export class BModule {}


@Module({
    providers: [CService],
    exports: [CService],
})
export class CModule {}

@Injectable()
class AppService {
    constructor(protected readonly service: AService) {}

    public run(context: string) { // let's assume context may be B or C
        this.service.run();
    }
}


@Module({
    imports: [CModule, BModule],
    providers: [{
        provide: AppService,
        useFactory: () => {
            return new AppService(); // how to use BService/CService depending on the context?
        }
    }]
})
export class AppModule {}

但关键是,我不能使用 @nestjs/core 中的 REQUEST(直接将其注入 useFactory),因为我在 cron 作业和 API 调用中使用此服务

我也不认为Factory 模式在那里有用,我的意思是它会起作用,但我想正确地做到这一点

我在想property based injection

但我不确定如何在我的情况下使用它

【问题讨论】:

标签: javascript node.js typescript dependency-injection nestjs


【解决方案1】:

如果属性是静态的(例如环境变量),您可以使用自定义提供程序来选择合适的实例。但是,如果属性在某种程度上是动态的,则您不能单独依赖嵌套的依赖注入,因为它会在启动时实例化提供程序(REQUEST 范围除外,这不是您的选择)。

静态属性

创建一个custom provider,它基于静态属性(例如环境变量)实例化所需的实现。

{
  provide: AService,
  useClass: process.ENV.useBService ? BService : CService,
}

具有请求范围的动态属性

假设我们有两种不同的服务实现:

@Injectable()
export class BService {
  public count = 0;
  run() {
    this.count++;
    return 'B';
  }
}

@Injectable()
export class CService {
  public count = 0;
  run() {
    this.count++;
    return 'C';
  }
}

当两者的count变量之和为偶数时,应使用BServiceCService 奇怪的时候。为此,我们使用request scope 创建了一个自定义提供程序。

{
  provide: 'MyService',
  scope: Scope.REQUEST,
  useFactory: (bService: BService, cService: CService) => {
    if ((bService.count + cService.count) % 2 === 0) {
      return bService;
    } else {
      return cService;
    }
  },
  inject: [BService, CService],
},

如果我们的控制器现在注入 MyService 令牌 (@Inject('MyService')) 并通过端点公开其 run 方法,它将返回 B C B ...

具有默认范围的动态属性

由于我们要使用默认作用域(Singleton!),所以不能使用nest 的依赖注入的静态实例化。相反,您可以使用委托模式在根类中选择所需的实例(在您的示例中为AService)。

按原样提供所有服务:

providers: [AService, BService, CService]

在您的 AService 中动态决定使用哪个实现:

@Injectable()
export class AService {
  constructor(private bService: BService, private cService: CService) {}

  run(dynamicProperty) {
    if (dynamicProperty === 'BService') {
      return this.bService.run();
    } else {
      return this.cService.run();
    }
  }
}

【讨论】:

  • 这些解决方案都不符合我的要求,你有没有 docs.nestjs.com/providers#property-based-injection 的工作示例?
  • @KrzysztofSzostak 基于属性的注入只是注入依赖项的另一种方式。您使用实例变量而不是使用构造函数。我看不出这对您的情况有何用处。 Nest 只是不提供您正在寻找的动态实例化。我很确定您只有我列出的三个选项。
【解决方案2】:

在我看来,工厂方法正是您所需要的。您描述了您需要基于上下文的不同服务,这非常适合工厂方法。让我们试试这个:

创建可注入工厂:

import { Injectable } from '@nestjs/common';
import { AService } from './AService';
import { BService } from './BService';
import { CService } from './CService';

@Injectable()
export class ServiceFactory {

    public getService(context: string) : AService {

        switch(context) {
            case 'a': return new BService();
            case 'b': return new CService();
            default: throw new Error(`No service defined for the context: "${context}"`);
        }
    }
}

现在将该工厂导入您的应用模块:

import { ServiceFactory } from './ServiceFactory';
import { AService } from './AService';

@Module({
    providers: [AppService, ServiceFactory]
})
export class AppModule {}

现在您的应用服务将获取工厂作为依赖项,该依赖项将根据上下文创建适当的服务:

import { ServiceFactory } from './ServiceFactory';
import { AService } from './AService';

@Injectable()
class AppService {

    constructor(readonly serviceFactory: ServiceFactory) { }

    public run(context: string) {
        const service: AService = this.serviceFactory.getService(context);
        service.run();
    }
}

【讨论】:

    猜你喜欢
    • 2021-11-20
    • 2019-06-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-01-14
    • 2018-11-23
    • 2023-04-04
    • 1970-01-01
    相关资源
    最近更新 更多