Controlling the scope of the dependencies

InversifyJS uses transient scope by default but you can also use singleton and request scope:

container.bind<Shuriken>("Shuriken").to(Shuriken).inTransientScope(); // Default
container.bind<Shuriken>("Shuriken").to(Shuriken).inSingletonScope();
container.bind<Shuriken>("Shuriken").to(Shuriken).inRequestScope();

About inSingletonScope

There are many available kinds of bindings:

interface BindingToSyntax<T> {
    to(constructor: { new (...args: any[]): T; }): BindingInWhenOnSyntax<T>;
    toSelf(): BindingInWhenOnSyntax<T>;
    toConstantValue(value: T): BindingWhenOnSyntax<T>;
    toDynamicValue(func: (context: Context) => T): BindingWhenOnSyntax<T>;
    toConstructor<T2>(constructor: Newable<T2>): BindingWhenOnSyntax<T>;
    toFactory<T2>(factory: FactoryCreator<T2>): BindingWhenOnSyntax<T>;
    toFunction(func: T): BindingWhenOnSyntax<T>;
    toAutoFactory<T2>(serviceIdentifier: ServiceIdentifier<T2>): BindingWhenOnSyntax<T>;
    toProvider<T2>(provider: ProviderCreator<T2>): BindingWhenOnSyntax<T>;
}

In terms of how scope behaves we can group these types of bindings in two main groups:

  • Bindings that will inject an object
  • Bindings that will inject a function

Bindings that will inject an object

In this group are included the following types of binding:

interface BindingToSyntax<T> {
    to(constructor: { new (...args: any[]): T; }): BindingInWhenOnSyntax<T>;
    toSelf(): BindingInWhenOnSyntax<T>;
    toConstantValue(value: T): BindingWhenOnSyntax<T>;
    toDynamicValue(func: (context: Context) => T): BindingInWhenOnSyntax<T>;
}

The inTransientScope is used by default and we can select the scope of this types of binding, except for toConstantValue which will always use inSingletonScope.

When we invoke container.get for the first time and we are using to, toSelf or toDynamicValue the InversifyJS container will try to generate an object instance or value using a constructor or the dynamic value factory. If the scope has been set to inSingletonScope the value is cached. The second time we invoke container.get for the same resource ID, and if inSingletonScope has been selected, InversifyJS will try to get the value from the cache.

Note that a class can have some dependencies and a dynamic value can access other types via the current context. These dependencies may or may not be a singleton independently of the selected scope of their parent object in their respective composition tree,

Bindings that will inject a function

In this group are included the following types of binding:

interface BindingToSyntax<T> {
    toConstructor<T2>(constructor: Newable<T2>): BindingWhenOnSyntax<T>;
    toFactory<T2>(factory: FactoryCreator<T2>): BindingWhenOnSyntax<T>;
    toFunction(func: T): BindingWhenOnSyntax<T>;
    toAutoFactory<T2>(serviceIdentifier: ServiceIdentifier<T2>): BindingWhenOnSyntax<T>;
    toProvider<T2>(provider: ProviderCreator<T2>): BindingWhenOnSyntax<T>;
}

We cannot select the scope of this types of binding because the value to be injected (a factory function) is always a singleton. However, the factory internal implementation may or may not return a singleton.

For example, the following binding will inject a factory which will always be a singleton.

container.bind<interfaces.Factory<Katana>>("Factory<Katana>").toAutoFactory<Katana>("Katana");

However, the value returned by the factory may or may not be a singleton:

container.bind<Katana>("Katana").to(Katana).inTransientScope();
// or
container.bind<Katana>("Katana").to(Katana).inSingletonScope();

About inRequestScope

When we use inRequestScope we are using a special kind of singleton.

  • The inSingletonScope creates a singleton that will last for the entire life cycle of a type binding. This means that the inSingletonScope can be cleared up from memory when we unbind a type binding using container.unbind.

  • The inRequestScope creates a singleton that will last for the entire life cycle of one call to the container.get, container.getTagged or container.getNamed methods. Each call to one of this methods will resolve a root dependency and all its sub-dependencies. Internally, a dependency graph known as the "resolution plan" is created by InversifyJS. The inRequestScope scope will use one single instance for objects that appear multiple times in the resolution plan. This reduces the number of required resolutions and it can be used as a performance optimization in some cases.

A 'Request scope' is not bound to a 'http request', but to a 'container request', which is a container.get or container.resolve call.

Suppose you have dependencies like this:

A -> B -> R
-> C -> R

So A has a dependency on B and C, and B and C both have a dependency on R.

Using only transient scope, if you get A from the container, B and C will both receive a different instance of R. If you bind R to RequestScope, B and C will both receive a reference to the same instance, as in a.b.r === a.c.r

If you resolve A again in the container, this will create a new instance of R, just like it will create new instances of A, B and C.

results matching ""

    No results matching ""