Nest предоставляет класс ModuleRef
для навигации по внутреннему списку провайдеров и получения ссылки на любой провайдер, используя его инъекционный токен в качестве ключа поиска. Класс ModuleRef
также предоставляет способ динамического инстанцирования как статических, так и динамических провайдеров. ModuleRef
может быть инжектирован в класс обычным способом:
cats.service.ts
@Injectable()
export class CatsService {
constructor(private moduleRef: ModuleRef) {}
}
Класс
ModuleRef
импортируется из пакета@nestjs/core
.
Получение экземпляров
Экземпляр ModuleRef
(далее мы будем называть его ссылка на модуль) имеет метод get()
. Этот метод извлекает провайдер, контроллер или инжектируемый модуль (например, guard, interceptor и т.д.), который существует (был инстанцирован) в текущем модуле, используя его инжектируемый токен/имя класса.
cats.service.ts
@Injectable()
export class CatsService implements OnModuleInit {
private service: Service;
constructor(private moduleRef: ModuleRef) {}
onModuleInit() {
this.service = this.moduleRef.get(Service);
}
}
Вы не можете получить скопированных провайдеров (переходных или скопированных по запросу) с помощью метода
get()
.
Чтобы получить провайдера из глобального контекста (например, если провайдер был внедрен в другой модуль), передайте параметр { strict: false }
в качестве второго аргумента к get()
.
this.moduleRef.get(Service, { strict: false });
Разрешение scoped провайдеров
Для динамического разрешения scoped провайдеров (переходного или примененного на запрос) используйте метод resolve()
, передавая в качестве аргумента инъекционный токен провайдера.
cats.service.ts
@Injectable()
export class CatsService implements OnModuleInit {
private transientService: TransientService;
constructor(private moduleRef: ModuleRef) {}
async onModuleInit() {
this.transientService = await this.moduleRef.resolve(TransientService);
}
}
Метод resolve()
возвращает уникальный экземпляр провайдера из его собственного поддерева DI-контейнера. Каждое поддерево имеет уникальный идентификатор контекста. Таким образом, если вы вызовете этот метод несколько раз и сравните ссылки на экземпляры, вы увидите, что они не равны.
cats.service.ts
@Injectable()
export class CatsService implements OnModuleInit {
constructor(private moduleRef: ModuleRef) {}
async onModuleInit() {
const transientServices = await Promise.all([
this.moduleRef.resolve(TransientService),
this.moduleRef.resolve(TransientService),
]);
console.log(transientServices[0] === transientServices[1]); // false
}
}
Чтобы генерировать один экземпляр при нескольких вызовах resolve()
и гарантировать, что они используют одно и то же сгенерированное поддерево контейнера DI, вы можете передать идентификатор контекста в метод resolve()
. Для создания идентификатора контекста используйте класс ContextIdFactory
. Этот класс предоставляет метод create()
, который возвращает соответствующий уникальный идентификатор.
cats.service.ts
@Injectable()
export class CatsService implements OnModuleInit {
constructor(private moduleRef: ModuleRef) {}
async onModuleInit() {
const contextId = ContextIdFactory.create();
const transientServices = await Promise.all([
this.moduleRef.resolve(TransientService, contextId),
this.moduleRef.resolve(TransientService, contextId),
]);
console.log(transientServices[0] === transientServices[1]); // true
}
}
Класс
ContextIdFactory
импортируется из пакета@nestjs/core
.
Регистрация провайдера REQUEST
.
Идентификаторы контекста, созданные вручную (с помощью ContextIdFactory.create()
), представляют поддеревья DI, в которых провайдер REQUEST
является undefined
, поскольку они не инстанцируются и не управляются системой инъекции зависимостей Nest.
Чтобы зарегистрировать пользовательский объект REQUEST
для созданного вручную поддерева DI, используйте метод ModuleRef#registerRequestByContextId()
, как показано ниже:
const contextId = ContextIdFactory.create();
this.moduleRef.registerRequestByContextId(/* YOUR_REQUEST_OBJECT */, contextId);
Получение текущего поддерева
Иногда вам может понадобиться разрешить экземпляр провайдера, скопированного на запрос, в контексте запроса. Допустим, CatsService
является request-scoped, и вы хотите разрешить экземпляр CatsRepository
, который также помечен как request-scoped provider. Чтобы совместно использовать одно и то же поддерево DI-контейнера, вы должны получить текущий идентификатор контекста, а не генерировать новый (например, с помощью функции ContextIdFactory.create()
, как показано выше). Чтобы получить текущий идентификатор контекста, начните с инъекции объекта запроса с помощью декоратора @Inject()
.
cats.service.ts
@Injectable()
export class CatsService {
constructor(
@Inject(REQUEST) private request: Record<string, unknown>,
) {}
}
Теперь используйте метод getByRequest()
класса ContextIdFactory
для создания идентификатора контекста на основе объекта запроса и передайте его в вызов resolve()
:
const contextId = ContextIdFactory.getByRequest(this.request);
const catsRepository = await this.moduleRef.resolve(CatsRepository, contextId);
Динамическое создание пользовательских классов
Для динамического создания класса, который не был ранее зарегистрирован в качестве провайдера, используйте метод create()
.
cats.service.ts
@Injectable()
export class CatsService implements OnModuleInit {
private catsFactory: CatsFactory;
constructor(private moduleRef: ModuleRef) {}
async onModuleInit() {
this.catsFactory = await this.moduleRef.create(CatsFactory);
}
}
Эта техника позволяет условно инстанцировать различные классы вне контейнера фреймворка.
0 комментариев