Inversifyjs: toDynamicValue and caching

Created on 6 Sep 2016  路  7Comments  路  Source: inversify/InversifyJS

Expected Behavior

I converted my useless model factories to dynamic values (.toDynamicValue) as suggested in #351. I expect the bound resources to be cached.

Current Behavior

The bound resources are not cached. Underlying factory is called each get() (and other factories in cascade)

bind<AdventureArchetypeModel>(RSRCIDS.model)
    .toDynamicValue((context: interfaces.Context) => factory({
        schema: context.kernel.get<IJsonSchemaExtended>(RSRCIDS.schema)
    }))

Possible Solution

If it's by design (well... dynamic value), then using dynamic values may not be a proper solution to #351

Context

Still trying to do functional programming, using pure functions and not using classes (no state + considered harmful in javascript)

enhancement minor

Most helpful comment

Implemented by https://github.com/inversify/InversifyJS/pull/360 I will release this ASAP 馃槃

All 7 comments

I need to think more about the possible implementation of this. I will confirm soon If I can implement something to sove this.

For the moment you can use a closure to create a "singleton dynamic value":

bind<AdventureArchetypeModel>(RSRCIDS.model).toDynamicValue((function() {

    var cache = null;

    return (context: interfaces.Context) => {

        if (cache !== null) {
             return cache;
        } else {
            return factory({
                schema: context.kernel.get<IJsonSchemaExtended>(RSRCIDS.schema)
            });
        }

    };

})());

Note that I haven't actually tested this but my guess is that it will work...

A solution could be to create a new type of binding as I proposed in #351. The "dynamic value" semantic has its own meaning.

What we are missing is support for inSingletonScope in toDynamicValue bindings:

kernel.bind<AdventureArchetypeModel>(RSRCIDS.model)
      .toDynamicValue((context: interfaces.Context) => {
          return factory({ schema: context.kernel.get<IJsonSchemaExtended>(RSRCIDS.schema) });
      }).inSingletonScope();

I will add support for this ASAP.

Great ! So the caching would be configurable, even better.

But I'd like you to elaborate about this "singleton scope". I guess it means that for a given kernel state, the same resource identifier will return the same resource (singleton).

Do you confirm ? Can you elaborate on the caching mechanism ? Is there a documentation entry for this feature ?

As usual, I'm impressed at your reactivity !

I have improved the page about scope in the wiki:

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 a 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>;
}

We can select the scope of this types of binding with the exception of the toConstantValue which will always be a singleton.

When we invoke kernel.get for the first time and we are using to, toSelf or toDynamicValue the InversifyJS kernel 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 kernel.get, 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 many not be a singleton independently of the selected scope of their parent object in their respective composition tree,

Bindings that will inject an 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.

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

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

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

Implemented by https://github.com/inversify/InversifyJS/pull/360 I will release this ASAP 馃槃

Tested it just now. Works perfectly :+1:

Was this page helpful?
0 / 5 - 0 ratings

Related issues

rashtao picture rashtao  路  3Comments

remojansen picture remojansen  路  4Comments

jshearer picture jshearer  路  4Comments

matthewjh picture matthewjh  路  3Comments

remojansen picture remojansen  路  3Comments