InversifyJS icon indicating copy to clipboard operation
InversifyJS copied to clipboard

LazyServiceIdentifer does not work for property injection

Open MeirionHughes opened this issue 5 years ago • 8 comments

Unable to lazily inject circular service dependency via property injection.

Side-note: Identifer -> Identifier

Expected Behavior

Inject without error

Current Behavior

throws error

Steps to Reproduce (for bugs)

import "reflect-metadata";
import { Container, injectable, inject, LazyServiceIdentifer } from "inversify";

const container = new Container();

interface IModuleA {}
interface IModuleB {}

const moduleA = Symbol.for("IModuleA");
const moduleB = Symbol.for("IModuleB");

@injectable()
class ModuleA implements IModuleA {
  @inject(new LazyServiceIdentifer(()=>moduleB))
  b: IModuleB;
}

@injectable()
class ModuleB implements IModuleB{
  @inject(new LazyServiceIdentifer(()=>moduleA))
  a: IModuleA
}

container.bind(moduleA).to(ModuleA).inSingletonScope();
container.bind(moduleB).to(ModuleB).inSingletonScope();

let bar = container.get(moduleA);

console.log(bar);

Context

I'm aware that injecting via the constructor works; however in NInject I was able to do this kind of lazy property injection within a base "Module" class. Subsequent derivations of that "Module" class then didn't have to deal with adding those dependencies manually.

Environment

inversify: "^4.13.0" node-js: 10.1.0

Stack trace

Error: No matching bindings found for serviceIdentifier: [object Object]
    at _validateActiveBindingCount (D:\Code\logger\node_modules\inversify\lib\planning\planner.js:62:23)
    at _getActiveBindings (D:\Code\logger\node_modules\inversify\lib\planning\planner.js:48:5)
    at _createSubRequests (D:\Code\logger\node_modules\inversify\lib\planning\planner.js:91:26)
    at D:\Code\logger\node_modules\inversify\lib\planning\planner.js:115:17
    at Array.forEach (<anonymous>)
    at D:\Code\logger\node_modules\inversify\lib\planning\planner.js:114:26
    at Array.forEach (<anonymous>)
    at _createSubRequests (D:\Code\logger\node_modules\inversify\lib\planning\planner.js:94:20)
    at Object.plan (D:\Code\logger\node_modules\inversify\lib\planning\planner.js:136:9)
    at D:\Code\logger\node_modules\inversify\lib\container\container.js:318:37

MeirionHughes avatar Aug 28 '18 11:08 MeirionHughes

I have tried to reproduce the same case (A injected in B. B injected in A.) with @lazyInject but also getting an error. And also I tried an example with the service identifier. https://github.com/inversify/InversifyJS/issues/865

Halynsky avatar Aug 31 '18 08:08 Halynsky

Any progress on this issue?

bl-packages-publisher avatar Oct 09 '20 14:10 bl-packages-publisher

Any updates ? facing same issue with lazyServiceIdentifier

mohsinamjad avatar Jan 07 '21 07:01 mohsinamjad

I resolved this problem by defining a third class, class C, which has an A and B injected property. This will contain methods which should call each property's methods.

VivienneB avatar Jan 08 '21 13:01 VivienneB

This really really hurts.

dustinlacewell avatar Oct 03 '21 22:10 dustinlacewell

Just had an issue with that. My workaround:

a) Inject Container to container somewhere

import { Container } from 'inversify'

const container = new container({...})

container.bind(Container).toConstantValue(container)

b) Implement custom lazy inject getter decorator based on container.get()

import { interfaces, decorate, Container, METADATA_KEY } from 'inversify'

export function lazyInject(
  serviceIdentifierCallback: () => interfaces.ServiceIdentifier<any>,
): PropertyDecorator {
  return function (target: any, propertyKey) {
    // inject container as this.container if it's not present
    if (!Reflect.getMetadata(METADATA_KEY.TAGGED_PROP, target.constructor)?.container) {
      decorate(inject(Container), target, 'container')
    }

   // define getter
    Object.defineProperty(target, propertyKey, {
      get: function () {
        return this.container.get(serviceIdentifierCallback())
      },
    })
  }
}

c) Usage

@injectable()
export class MyClass {
     @lazyInject(() => SOME_TOKEN) service!: SomeService
}

The caveats are:

  • we're injecting container as container property in each class (need to make sure not to use it for smth else)
  • we're lazy resolving it at runtime (so we loose some debug value from inversify messages, idk about perf)

vadistic avatar Nov 19 '21 11:11 vadistic

Any progress on this issue?

cuidong626 avatar Nov 18 '22 14:11 cuidong626

I don't think anyone is working on Inversify

dustinlacewell avatar Nov 18 '22 16:11 dustinlacewell