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 依赖项默认注册为单例。否则,无论后续调用的上下文如何,每次都会注入相同的第一个服务。
您的上下文必须来自另一个注入的依赖项 - ExecutionContext
、Request
或类似动态的东西,或者您自己注册的东西。
备选
作为替代方案,您可以实施“servicelocator/factory”模式。您的 BatchService
已经完成了一半。不是您的服务创建 CatService
和 PetService
的实例,而是根据上下文注入它,而只是 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 容器中可用的内容。不利的一面是,它确实向调用代码公开了一些(通常不需要的)基础结构细节,但这是您必须做出的权衡。
我有一些服务 类 如下:
//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 依赖项默认注册为单例。否则,无论后续调用的上下文如何,每次都会注入相同的第一个服务。
您的上下文必须来自另一个注入的依赖项 -
ExecutionContext
、Request
或类似动态的东西,或者您自己注册的东西。
备选
作为替代方案,您可以实施“servicelocator/factory”模式。您的 BatchService
已经完成了一半。不是您的服务创建 CatService
和 PetService
的实例,而是根据上下文注入它,而只是 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 容器中可用的内容。不利的一面是,它确实向调用代码公开了一些(通常不需要的)基础结构细节,但这是您必须做出的权衡。