装饰器在任何其他混乱 "this" 上下文之前调用方法

Decorator to call a method before any other messes with "this" context

真正的问题

我想我找到了真正的问题,inversify 其他一切正常。 在原来的 post 中,我省略了部分代码,因为我认为它们不是导致问题的原因。

在我的 ICitiesRepo 实现中,我有一个方法 ensureDb 确保初始化一些 typeorm 属性,因为这个方法必须是异步的我不能只调用它构造函数因此应该在任何 CRUD 操作之前调用它,然后我创建了一个装饰器,目的是在 class:

中的任何其他方法之前调用 ensureDb
  export const ensureCall = (method: string) => {
    return (target: any) => {
      for (const prop of Object.getOwnPropertyNames(target.prototype)) {
        if (prop === method || prop === 'constructor') continue;

        const originalMethod = target.prototype[prop];
        if (originalMethod instanceof Function) {
          target.prototype[prop] = async (...args: any[]) => {
            await target.prototype[method]();
            return originalMethod.apply(this, args);
          };
        }
      }
    };
  };

这是用途:

   @ensureCall('ensureDb')
   @injectable()
   class CitiesRepo implements ICitiesRepo {
       @inject('CitiesWriteRepo') private readonly repo: IWriteRepo<City>;
       
       async ensureDb() {
          await this.repo.doSomething();
          // repo is undefined because I messed up with the context
       }
   
       // interface implementation
   }

如果我删除那个装饰器并在其他方法工作之前调用 ensureDb

   const container = getContainer();
   const citiesRepo: ICitiesRepo = container.get('CitiesRepo');
   await citiesRepo.ensureDb();
   const list = await citiesRepo.getAll(); // It works

是否可以解决这个问题或其他更好的方法来实现我想要做的事情?

原版post

我有一个带有通用存储库的项目,但我在使用 inversifyJS 注入存储库对象时遇到了一些问题。结构是这样的:

   interface IWriteRepo<T>  { /* interface members */ }

   @injectable()
   class WriteRepo<City> implements IWriteRepo<City> { /* interface implementation */ }

   interface ICitiesRepo { /* interface members */ }

   @injectable()
   class CitiesRepo implements ICitiesRepo {
       @inject('CitiesWriteRepo') private readonly repo: IWriteRepo<City>;

       // interface implementation
   }

   interface ICitiesService { /* interface members */ }

   @injeactable()
   class CitiesService {
      @inject('CitiesRepo') repo: ICitiesRepo;

       // interface implementation
   }

   // binding
   const container = new Container();
   container.bind<IWriteRepo<City>>('CitiesWriteRepo').to(WriteRepo);
   container.bind<ICitiesRepo>('CitiesRepo').to(CitiesRepo);
   container.bind<ICitiesService>('CitiesService').to(CitiesService);

我正在使用节点应用程序测试代码,当我尝试获取通用存储库 (IWriteRepo<City>) 和 CitiesRepo 我没有任何问题:

   const container = getContainer();
   const citiesRepo: ICitiesRepo = container.get('CitiesRepo'); // OK and prop repo injected correctly
   const citiesWriteRepo: IWriteRepo<City> = container.get('CitiesWriteRepo'); // OK

尝试获取服务时出现问题:

   const container = getContainer();
   const citiesService: ICitiesService = container.get('CitiesService');

服务已正确注入,repo: ICitiesRepo但通用“孙子”repo: IWriteRepo<City>未定义。

知道如何解决吗?

编辑

我仍在努力找出问题所在。 现在我尝试将 class 注入构造函数,但发生了一些奇怪的事情:InversifyJS 正确注入依赖项,我将其分配给 属性 但是当我尝试使用它时它是未定义的。

   interface ICitiesRepo { /* interface members */ }

   @injectable()
   class CitiesRepo implements ICitiesRepo {
       private readonly repo: IWriteRepo<City>;

       constructor(@inject('CitiesWriteRepo') repo: IWriteRepo<City>) {
          console.log(repo); // result: WriteRepo {}
          this.repo = repo;
          console.log(this.repo); // result: WriteRepo {}
       }

       getAll() {
          console.log(this.repo); // result: undefined
       }

       // interface implementation
   }

了。你得到 undefined 因为你的装饰器中的 this 是未定义的,应该指的是你的目标的实例化实例。将该定义更改为常规函数调用而不是箭头函数应该可以解决问题。

export const ensureCall = (method: string) => {
  return (target: any) => {
    for (const prop of Object.getOwnPropertyNames(target.prototype)) {
      if (prop === method || prop === 'constructor') continue;

      const originalMethod = target.prototype[prop];
      if (originalMethod instanceof Function) {
        // Regular function declaration
        target.prototype[prop] = async function(...args: any[]) {
          await target.prototype[method]();
          // Now `this` refers to an instantiated instance
          return originalMethod.apply(this, args);
        };
      }
    }
  };
};