Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

toDynamicValue and caching #357

Closed
Offirmo opened this issue Sep 6, 2016 · 7 comments
Closed

toDynamicValue and caching #357

Offirmo opened this issue Sep 6, 2016 · 7 comments

Comments

@Offirmo
Copy link
Contributor

Offirmo commented Sep 6, 2016

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)

@remojansen
Copy link
Member

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...

@Offirmo
Copy link
Contributor Author

Offirmo commented Sep 6, 2016

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

@remojansen
Copy link
Member

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.

@remojansen remojansen self-assigned this Sep 6, 2016
@Offirmo
Copy link
Contributor Author

Offirmo commented Sep 7, 2016

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 !

@remojansen
Copy link
Member

remojansen commented Sep 7, 2016

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();

@remojansen
Copy link
Member

Implemented by #360 I will release this ASAP 😄

remojansen added a commit that referenced this issue Sep 9, 2016
* Implemented #357

* [WIP] #330

* [WIP] #330

* Auto generate dts files

* Updated docs

* [WIP] trying to fix 'Cannot find global type Array'

* Removed tsify
remojansen added a commit that referenced this issue Sep 9, 2016
* Implemented #357

* [WIP] #330

* [WIP] #330

* Auto generate dts files

* Updated docs

* [WIP] trying to fix 'Cannot find global type Array'

* Removed tsify

* Migrated from typings to @types

* Removed typings

* preparing for rc.14
@Offirmo
Copy link
Contributor Author

Offirmo commented Sep 12, 2016

Tested it just now. Works perfectly 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants