NestJS:动态调用各种服务进行批处理

NestJS: dynamically call various services for batch processing

我有一些服务 类 如下:

//Cat Service:
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, getManager } from 'typeorm';
import { CatRepo } from '../tables/catrepo.entity';
import { CatInterface } from './cat.interface';

@Injectable()
export class CatService {
  constructor(
    @InjectRepository(CatRepo)
    private catRepo: Repository<CatRepo>,
    
  ) {}

  async customFindAll(offset:number, limit: number): Promise<CatRepo[]> {
    const entityManager = getManager();
    const catRows = await entityManager.query(
      `
      SELECT * FROM CATREPO
      ${offset ? ` OFFSET ${offset} ROWS ` : ''}
      ${limit ? `FETCH NEXT ${limit} ROWS ONLY` : ''}
      `,
    );
    return catRows;
  }

  formResponse(cats: CatRepo[]): CatInterface[] {
   const catsResults: CatInterface[] = [];
   .
   //form cat response etc.
   .

   //then return 
   return catsResults;
  }
}

//Pet Service:
import { Injectable } from '@nestjs/common';
import { getManager } from 'typeorm';
import { PetInterface } from './pet.interface';


@Injectable()
export class PetService {

async customFindAll(offset:number, limit: number) {
    const entityManager = getManager();
    const petRows = await entityManager.query(
      `
        JOIN ON TABLES......
        ${offset ? ` OFFSET ${offset} ROWS ` : ''}
        ${limit ? `FETCH NEXT ${limit} ROWS ONLY` : ''}
        `,
    );

    //returns list of objects
    return petRows;
  }

  formResponse(pets): PetInteface[] {
   const petsResults: PetInteface[] = [];
   .
   . //form pet response etc.
   .

   //then return 
   return petsResults;
  }

 }

我是 运行 一个使用这两个服务的 cron BatchService,随后将数据保存到各自的批处理文件中。

我从 BatchService 调用 CatService 和 PetService 如下:

/Start the Batch job for Cats.
if(resource === "Cat") {
//Call Cat Service
result = await this.catService.findAllWithOffest(startFrom, fetchRows);          
finalResult = this.catService.formResponse(result);
}
//Start the batch job for Pets.
if(resource === "Pet") {
//Call Pet Service
result = await this.petService.findAllWithOffest(startFrom, fetchRows);          
finalResult = this.petService.formResponse(result);
}

但是,我想动态地使用这些服务而不是上述服务。 为了实现CatService和PetService现在extends AbstractService...

export abstract class AbstractService {
    public batchForResource(startFrom, fetchRows) {}
}

//The new CatService is as follows:

export class CatService extends AbstractService{
  constructor(
    @InjectRepository(CatRepo)
    private catRepo: Repository<CatRepo>,
    
  ) {}
  .
  .
  .
  }

//the new PetService is:
export class PetService extends AbstractService{
  constructor(
  ) {super()}
.
.
.
}

//the BatchService...

public getService(context: string) : AbstractService {
  switch(context) {
      case 'Cat': return new CatService();
      case 'Pet': return new PetService();
      default: throw new Error(`No service found for: "${context}"`);
  }
}

但是在 CatService 中我遇到了编译错误...(应为 1 个参数但得到的是 0)。在 CatService 中传递的参数应该是什么。

此外,更大的问题是这是否可以通过使用 NestJS 来实现 useValue/useFactory...如果可以,该怎么做?

可以 可能使用 useFactory 来动态检索您的依赖项,但有一些陷阱。

  • 您必须使服务的生命周期成为瞬态的,因为 NestJS 依赖项默认注册为单例。否则,无论后续调用的上下文如何,每次都会注入相同的第一个服务。

  • 您的上下文必须来自另一个注入的依赖项 - ExecutionContextRequest 或类似动态的东西,或者您自己注册的东西。

备选

作为替代方案,您可以实施“servicelocator/factory”模式。您的 BatchService 已经完成了一半。不是您的服务创建 CatServicePetService 的实例,而是根据上下文注入它,而只是 return 注入的服务。像这样:

@Injectable()
export class BatchService {
    constructor(
        private readonly catService: CatService,
        private readonly petService: PetService
    )

    public getService(context: string) : AbstractService {
        switch(context) {
            case 'Cat': return this.catService;
            case 'Pet': return this.petService;
            default: throw new Error(`No service found for: "${context}"`);
        }
    }
}

替代方法比使用 useFactory 更灵活,因为您的上下文不限于 DI 容器中可用的内容。不利的一面是,它确实向调用代码公开了一些(通常不需要的)基础结构细节,但这是您必须做出的权衡。