【问题标题】:Proper way to manually instantiate Nest.js providers手动实例化 Nest.js 提供程序的正确方法
【发布时间】:2021-09-22 05:19:29
【问题描述】:

我想我可能误解了 Nest.js 的 IoC 容器,或者可能是整个 DI。

我有一个类 JSONDatabase,我想根据一些配置值(可以是 JSON 或 SQL)来实例化自己。

我的DatabaseService 提供者:

  constructor(common: CommonService, logger: LoggerService) {

    // eslint-disable-next-line prettier/prettier
    const databaseType: DatabaseType = common.serverConfig.dbType as DatabaseType;

    if (databaseType === DatabaseType.JSON) {
      this.loadDatabase<JSONDatabase>(new JSONDatabase());
    } else if (databaseType === DatabaseType.SQL) {
      this.loadDatabase<SQLDatabase>(new SQLDatabase());
    } else {
      logger.error('Unknown database type.');
    }
  }

我的JSONDatabase 班级:

export class JSONDatabase implements IDatabase {
  dbType = DatabaseType.JSON;
  constructor(logger: LoggerService, io: IOService) {
    logger.log(`Doing something...`)
  }
}

但是,这样做的问题是,如果我希望我的JSONDatabase 能够利用注入,即。它需要IOServiceLoggerService,我需要从DatabaseService 构造函数中添加参数,而不是通过Nest 的IoC 容器注入它们。

Expected 2 arguments, but got 0 [ts(2554)]
json.database.ts(7, 15): An argument for 'logger' was not provided.

这是正确的方法吗?我觉得手动传递这些引用是不正确的,我应该使用 Nest 的自定义提供程序,但是,我并不真正了解有关此主题的 Nest 文档。我本质上希望能够new JSONDatabase() 而不必将引用传递给构造函数,并让 Nest.js IoC 容器已经注入现有的单例(运行时依赖注入?)。

我在这里的想法可能完全偏离了基础,但我并没有太多地使用 Nest,所以我主要是凭直觉工作。任何帮助表示赞赏。

【问题讨论】:

    标签: nestjs


    【解决方案1】:

    您现在遇到的问题是因为您在调用 new JSONDatabase() 时手动实例化 JSONDatabase,而不是利用 NestJS 提供的 DI。由于构造函数需要 2 个参数(LoggerService 和 IOService)并且您没有提供任何参数,因此它会失败并显示消息

    Expected 2 arguments, but got 0 [ts(2554)]

    我认为根据您的用例,您可以尝试几个不同的选项

    1. 如果您在启动时获取配置并在应用程序生命周期中设置一次数据库,则可以使用具有 useFactory 语法的自定义提供程序。
    const providers = [
      {
        provide: DatabaseService,
        useFactory: (logger: LoggerService, io: IOService, config: YourConfigService): IDatabase => {
          if (config.databaseType === DatabaseType.JSON) {
              return new JSONDatabase(logger, io);
          } else if (databaseType === DatabaseType.SQL) {
              return new SQLDatabase(logger, io);
          } else {
            logger.error('Unknown database type.');
          }
        },
        inject: [LoggerService, IOService, YourConfigService]
      },
    ];
    
    @Module({
      providers,
      exports: providers
    })
    export class YourModule {}
    

    如果你有LoggerServiceIOServiceYourConfigurationService@Injectable() 注释,NestJS 会将它们注入到 useFactory 上下文中。在那里您可以检查 databaseType 并手动实例化正确的 IDatabase 实现。这种方法的缺点是您不能在运行时轻松更改数据库。 (这可能适用于您的用例)

    1. 您可以使用策略/工厂模式来获得基于类型的正确实现。假设您有一个基于特定参数保存到不同数据库的方法。
    @Injectable()
    export class SomeService {
      constructor(private readonly databaseFactory: DatabaseFactory){}
    
      method(objectToSave: Object, type: DatabaseType) {
        databaseFactory.getService(type).save(objectToSave);
      }
    }
    
    
    @Injectable()
    export class DatabaseFactory {
    
      constructor(private readonly moduleRef: ModuleRef) {}
    
      getService(type: DatabaseType): IDatabase {
        this.moduleRef.get(`${type}Database`);
      }
    }
    

    上面代码的思路是,根据数据库类型,从 NestJS 作用域中获取正确的单例。这样,如果您愿意,可以很容易地添加一个新数据库——只需添加一个新类型和它的实现。 (而且您的代码可以同时处理多个数据库!)

    1. 我也相信您可以简单地将已经注入的LoggerServiceIOService 传递给您手动创建的DatabasesService(您需要添加IOService 作为DatabaseServce 的依赖项
    @Injectable()
    export class DatabaseService {
      constructor(common: CommonService, logger: LoggerService, ioService: IOService) {
    
        // eslint-disable-next-line prettier/prettier
        const databaseType: DatabaseType = common.serverConfig.dbType as DatabaseType;
    
        if (databaseType === DatabaseType.JSON) {
          this.loadDatabase<JSONDatabase>(new JSONDatabase(logger, ioService));
        } else if (databaseType === DatabaseType.SQL) {
          this.loadDatabase<SQLDatabase>(new SQLDatabase(logger, ioService));
        } else {
          logger.error('Unknown database type.');
        }
      }
    }
    

    【讨论】:

    • 哦,哇,超级有用的答案,谢谢!实际上,我最终选择了一个抽象的 DatabaseService 类,然后根据配置参数和动态模块将它们实现为 JSON 或 SQL,这似乎类似于 #1。我实际上可能会切换到您的#2,因为它对我的代码库来说似乎更合理。非常感谢!
    猜你喜欢
    • 2013-03-27
    • 2020-01-17
    • 2018-10-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多