Inversifyjs: Thousand resolves inside factory: how to avoid?

Created on 20 Dec 2016  路  2Comments  路  Source: inversify/InversifyJS

My program traverse over the AST tree and for some node call factory which returns some class.

// customNode factory
    bind<ICustomNode>(ServiceIdentifiers['Factory<ICustomNode>'])
        .toFactory<ICustomNode>((context: interfaces.Context) => {
            return (customNodeName: CustomNodes) => {
                return context.container.getNamed<ICustomNode>(
                    ServiceIdentifiers.ICustomNode,
                    customNodeName
                );
            };
        });

At total, i have many thousands calls to factory, each call and resolve takes about 0.1-0.2ms, but in summary i getting slowdown about 5-6s.

I can't cache factory objects, they should be unique, and now i seeing only way to fix performance - manually create objects inside factory, and return them.

in this case here any way to get constructor from the container by binding name or tag?
I can do something like

 bind<ICustomNode>(ServiceIdentifiers['Factory<ICustomNode>'])
        .toFactory<ICustomNode>((context: interfaces.Context) => {
            let cachedOptions: IOptions;

            return (customNodeName: CustomNodes) => {
                if (!cachedOptions) {
                    cachedOptions = context.container.get<IOptions>('IOptions');
                }

                return new container.getNamedConstructor(customNodeName)(cachedOptions);
            };
        });

question

All 2 comments

I'm not sure about fully understanding what do you need but I will try. I think you are asking for a new method getNamedConstructor ?

If that is the case, that method is not needed because you can do the following:

First use toConstructor to declare some bindings:

container.bind<X>("X").toConstructor(X).whenTargetNamed(customNodeNameA);
container.bind<X>("X").toConstructor(X).whenTargetNamed(customNodeNameB);

Then use getNamed to get the constuctor:

 bind<ICustomNode>(ServiceIdentifiers['Factory<ICustomNode>'])
        .toFactory<ICustomNode>((context: interfaces.Context) => {
            let cachedOptions: IOptions;

            return (customNodeName: CustomNodes) => {
                if (!cachedOptions) {
                    cachedOptions = context.container.get<IOptions>('IOptions');
                }
                let Constructor = container.getNamed<X>("X", customNodeName);
                return new Constructor(cachedOptions);
            };
        });

Thank you, i did it, with toConstructor i still should add resolved constructors to the cache (because resolve time still about 0.15ms, but with constructors i can use cache and overall time of factory call now about 0.005-0.01ms

// customNode factory
bind<ICustomNode>(ServiceIdentifiers['Factory<ICustomNode>'])
    .toFactory<ICustomNode>((context: interfaces.Context) => {
        const cache: Map <CustomNodes, interfaces.Newable<ICustomNode>> = new Map();

        let cachedOptions: IOptions;

        return (customNodeName: CustomNodes) => {
            if (!cachedOptions) {
                cachedOptions = context.container.get<IOptions>(ServiceIdentifiers.IOptions);
            }

            if (cache.has(customNodeName)) {
                return new (<interfaces.Newable<ICustomNode>>cache.get(customNodeName));
            }

            const constructor: interfaces.Newable<ICustomNode> = context.container
                .getNamed<interfaces.Newable<ICustomNode>>(
                    ServiceIdentifiers.ICustomNode,
                    customNodeName
                );

            cache.set(customNodeName, constructor);

            return new constructor(cachedOptions);
        };
    });
Was this page helpful?
0 / 5 - 0 ratings