根据参数

时间:2019-12-05 12:02:31

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

假设我有两个模块,分别导出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 {}

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

我也不认为Factory模式在那里有用,我的意思是它可以工作,但我想正确地进行操作

我在考虑property based injection

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

2 个答案:

答案 0 :(得分:1)

如果属性是静态的(例如环境变量),则可以使用自定义提供程序来选择适当的实例。但是,如果该属性处于某种动态状态,则您不能完全依赖nest的依赖项注入,因为它会在启动时实例化提供程序(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 {{1 }} ...

具有默认范围的动态属性

由于我们要使用默认范围(Singleton!),因此不能使用嵌套的依赖项注入的静态实例化。相反,您可以使用委托模式在根类(示例中为B)中选择所需的实例。

按原样提供所有服务:

AService

在您的providers: [AService, BService, CService] 中动态决定要使用的实现方式:

AService

答案 1 :(得分:0)

我认为,工厂方法正是您所需要的。您描述了您需要基于上下文的其他服务,这对于工厂方法非常有用。让我们尝试一下:

创建可注射工厂:

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();
    }
}