Как справиться с внедрением зависимостей NestJS при расширении класса для службы?

Я пытаюсь предоставить другую услугу на основе значения моего ConfigService.

Проблема, с которой я сталкиваюсь, заключается в том, что внедренная модель мангуста не возвращает никаких значений при выполнении таких методов запроса, как findOne() (результат null) или countDocuments() (результат 0).

Мои классы обслуживания определены следующим образом:

    export class BaseService {
      constructor(@InjectModel('Cat') public readonly catModel: Model<Cat>) {}

      createService(option: string) {
        if (option === 'OTHER') {
          return new OtherService(this.catModel);
        } else if (option === 'ANOTHER') {
          return new AnotherService(this.catModel);
        } else {
          return new BaseService(this.catModel);
        }
      }

      async findOne(id: string): Promise<Cat> {
        return await this.catModel.findOne({_id: id});
      }

      async count(): Promise<number> {
        return await this.catModel.countDocuments();
      }

      testClass() {
        console.log('BASE SERVICE CLASS USED');
      }
    }

    @Injectable()
    export class OtherService extends BaseService {
      constructor(@InjectModel('Cat') public readonly catModel: Model<Cat>) {
        super(catModel);
      }

       testClass() {
        console.log('OTHER SERVICE CLASS USED');
      }
    }

    @Injectable()
    export class AnotherService extends BaseService {
      constructor(@InjectModel('Cat') public readonly catModel: Model<Cat>) {
        super(catModel);
      }
      testClass() {
        console.log('ANOTHER SERVICE CLASS USED');
      }
    }

Это позволяет мне получать правильные услуги от моего провайдера (testClass() выводит ожидаемую строку). Мой провайдер выглядит так:

    export const catProviders = [
      {
        provide: 'CatModelToken',
        useFactory: (connection: Connection) => connection.model('CAT', CatSchema),
        inject: ['DbConnectionToken'],
      },
      {
        provide: 'BaseService',
        useFactory: (ConfigService: ConfigService, connection: Connection) => {
          const options = ConfigService.get('SERVICE_TYPE');
          let model = connection.model('CAT', CatSchema);
          return new BaseService(model).createService(options);
      },
      inject: [ConfigService, 'CatModelToken', 'DbConnectionToken'],
      }
    ];

Итак, мой вопрос состоит из двух частей:

  • Есть ли лучший способ справиться с созданием правильного класса и избежать создания экземпляра BaseService с единственной целью - вызвать createService()?
  • Как правильно внедрить модель мангуста во вновь созданный сервис?

Я также не могу использовать пример useClass из документации, так как мне нужно ввести ConfigService.


person cameronliam    schedule 14.12.2018    source источник


Ответы (1)


Вы можете решить эту проблему, используя заводской подход, попробуйте следующее:

Интерфейс для определения «формы» ваших услуг:

export interface IDatabaseService {
    findOne(id: string): Promise<Cat>;
    count(): Promise<number>;
    testClass(): void;
}

BaseService должен реализовать этот интерфейс:

export class BaseService implements IDatabaseService {

    constructor(@InjectModel('Cat') public readonly catModel: Model<Cat>) {}

    async findOne(id: string): Promise<Cat> {
        return await this.catModel.findOne({_id: id});
    }

    async count(): Promise<number> {
        return await this.catModel.countDocuments();
    }

    testClass() {
        console.log('BASE SERVICE CLASS USED');
    }
}

Динамические сервисы не внедряются, поэтому они не используют декоратор @Injectable():

export class OtherService extends BaseService {

    constructor(@InjectModel('Cat') public readonly catModel: Model<Cat>) {
        super(catModel);
    }

    testClass() {
        console.log('OTHER SERVICE CLASS USED');
    }
}

export class AnotherService extends BaseService {

    constructor(@InjectModel('Cat') public readonly catModel: Model<Cat>) {
        super(catModel);
    }

    testClass() {
        console.log('ANOTHER SERVICE CLASS USED');
    }
}

Фабричный класс - это то, что вводится:

@Injectable()
export class DatabaseServiceFactory {

    constructor(@InjectModel('Cat') private readonly catModel: Model<Cat>) {}

    createService(name: string) : IDatabaseService {
        switch(name) {
            case 'other': return new OtherService(this.catModel);
            case 'another': return new AnotherService(this.catModel);
            default: throw new Error(`No service has been implemented for the name "${name}"`);
        }
    }
}
export const catProviders = [
    {
        provide: 'CatModelToken',
        useFactory: (connection: Connection) => connection.model('CAT', CatSchema),
        inject: ['DbConnectionToken'],
    },
    {
        provide: 'BaseService',
        useFactory: (ConfigService: ConfigService, connection: Connection, dbFactory: DatabaseServiceFactory) => {

            const options = ConfigService.get('SERVICE_TYPE');
            let model = connection.model('CAT', CatSchema);

            //return new BaseService(model).createService(options);
            return dbFactory.createService(options);
        },
        inject: [
            ConfigService,
            'CatModelToken',
            'DbConnectionToken',
            DatabaseServiceFactory
        ],
    }
];
person nerdy beast    schedule 04.11.2019
comment
Хотя эта ссылка может дать ответ на вопрос, лучше включить сюда основные части ответа и предоставить ссылку для справки. Ответы, содержащие только ссылки, могут стать недействительными, если ссылка на страницу изменится. - Из отзыва - person Rob; 05.11.2019