Załóżmy, że mam dwa moduły, które eksportują BService i CService, gdzie obie te usługi rozszerzają AService

Kod wygląda więc tak:

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 {}

Ale kluczem jest to, że nie mogę użyć REQUEST (aby wstrzyknąć go bezpośrednio do useFactory) z @nestjs/core, ponieważ używam tej usługi w zadaniach cron i z wywołaniem API

Nie sądzę też, żeby wzorzec Factory był tam przydatny, mam na myśli, że zadziała, ale chcę to zrobić poprawnie

Myślałem o wstrzykiwaniu opartym na właściwościach.

Ale nie jestem pewien, jak go użyć w moim przypadku

4
hejkerooo 5 grudzień 2019, 15:02
Sprawdź moją odpowiedź poniżej, aby sprawdzić, czy pasuje do Twoich potrzeb. Brzmi to jak dobre miejsce na użycie wzorca strategii. stackoverflow.com/a/69502460/8148483
 – 
Francisco Garcia
10 październik 2021, 03:10

2 odpowiedzi

Jeśli właściwość jest statyczna (np. zmienna środowiskowa), możesz użyć niestandardowego dostawcy, aby wybrać odpowiednią instancję. Jeśli jednak właściwość jest w jakiś sposób dynamiczna, nie można polegać wyłącznie na wstrzykiwaniu zależności gniazda, ponieważ tworzy on wystąpienie dostawcy podczas uruchamiania (z wyjątkiem zakresu REQUEST, który nie jest opcją dla Ciebie).

Właściwość statyczna

Utwórz dostawcę niestandardowego, który tworzy instancję potrzebnej implementacji na podstawie właściwości statycznej (np. zmiennej środowiskowej ).

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

Właściwość dynamiczna z zakresem żądania

Załóżmy, że mamy dwie różne implementacje usługi:

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

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

Gdy suma zmiennych count obu jest parzysta, należy użyć BService; CService kiedy jest to dziwne. W tym celu tworzymy niestandardowego dostawcę z zakresem żądania.

{
  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],
},

Jeśli nasz kontroler wstrzyknie teraz token MyService (@Inject('MyService')) i udostępni swoją metodę run za pośrednictwem punktu końcowego, zwróci B C B . ...

Właściwość dynamiczna z domyślnym zakresem

Ponieważ chcemy użyć zakresu domyślnego (Singleton!), nie można użyć statycznej instancji wstrzykiwania zależności gniazda. Zamiast tego możesz użyć wzorca delegata, aby wybrać żądane wystąpienie w klasie głównej (AService w twoim przykładzie).

Świadcz wszystkie usługi w takim stanie, w jakim są:

providers: [AService, BService, CService]

Dynamicznie zdecyduj w AService, której implementacji użyć:

@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();
    }
  }
}
3
Kim Kern 5 grudzień 2019, 16:18
Żadne z tych rozwiązań nie spełni moich wymagań, czy masz gdzieś działający przykład z docs.nestjs. com/providers#property-based-injection ?
 – 
hejkerooo
5 grudzień 2019, 16:55
Wstrzykiwanie oparte na właściwościach to po prostu inny sposób wstrzykiwania zależności. Zamiast używać konstruktora, używasz zmiennej instancji. Nie rozumiem, jak miałoby to być przydatne w twoim przypadku. Nest po prostu nie oferuje dynamicznego tworzenia instancji, którego szukasz. Jestem prawie pewien, że masz tylko trzy wymienione przeze mnie opcje.
 – 
Kim Kern
5 grudzień 2019, 16:57

Moim zdaniem podejście fabryczne jest dokładnie tym, czego potrzebujesz. Opisałeś, że potrzebujesz innej usługi w oparciu o kontekst, co jest świetne dla podejścia fabrycznego. Spróbujmy tego:

Utwórz fabrykę do wstrzykiwań:

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}"`);
        }
    }
}

Teraz zaimportuj tę fabrykę do modułu aplikacji:

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

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

Teraz Twoja usługa aplikacji otrzyma fabrykę jako zależność, która utworzy odpowiednią usługę na podstawie kontekstu:

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();
    }
}
6
nerdy beast 7 grudzień 2019, 01:53