Lifecycle sopes with factory provider
Is your feature request related to a problem? Please describe.
Currently you cant use lifecycle scopes with useFactory provider. Transient is supported by default and Singleton can be achieved with the instacneCachingFactory wrapper, but there is no way to do ContainerScoped or ResolutionScoped.
Is there a way to get ContainerScoped working with factories? Should we maybe support Lifecycle.Singleton instead of the instanceCachingFactory and add support for scopes isntead?
Factory called
- always -
Transient(default) - once -
Singleton - once per container -
ContainerScoped - once per resolution -
ResolutionScoped
This would result in a more powerful and consistent registration api in my opinion.
The predicateAwareClassFactory could also be scrapped basically, since the same result can be achieved without it;
container.register('PREDICATE_AWARE_FACTORY',{
useFactory: (c) => {
return c.resolve(Bar).useHttp ? c.resolve( FooHttps) : c.resolve( FooHttp)
}
})
Any thoughts on this.
+1
I would like to see this as well - it would make dynamic external configuration much easier.
import { ThirdPartyClient } from 'third-party-client`;
@singleton()
class Configuration {
public readonly optionX = 'xxx';
public readonly optionY = 'yyy';
}
@scoped(Lifecycle.ContainerScoped)
class State {
public readonly option: 'X' | 'Y';
}
container.Register(ThirdPartyClient, {
useFactory: c => {
const state = c.resolve(State);
const config = c.resolve(Configuration);
const option = configuration[`option${state.option}`];
return new ThirdPartyClient({ option })
},
lifecycle: Lifecycle.ContainerScoped
);
// later per request
const scopedContainer1 = container.createChildContainer();
const client1a = container.Resolve(ThirdPartyClient);
const client1b = container.Resolve(ThirdPartyClient);
assert(client1a === client1b);
const scopedContainer2 = container.createChildContainer();
const client2 = container.Resolve(ThirdPartyClient);
assert(client1a === client2);
I think this could be implemented with a custom factory type and a weak map
Ok I hacked this together.
import { FactoryFunction, DependencyContainer } from "tsyringe";
export default function instancePerContainerCachingFactory<T>(
factoryFunc: FactoryFunction<T>
): FactoryFunction<T> {
const cache = new WeakMap<DependencyContainer, T>();
return (dependencyContainer: DependencyContainer) => {
let instance = cache.get(dependencyContainer);
if (instance == undefined) {
instance = factoryFunc(dependencyContainer);
cache.set(dependencyContainer, instance);
}
return instance;
};
}
Thanks @daniel-white
However, for my use-case I need to be able to clear the singletons with container.clearInstances(). I ended up using this instead:
import { DependencyContainer, FactoryFunction } from "tsyringe";
export default function instancePerContainerCachingFactory<T>(
factoryFunc: FactoryFunction<T>,
): FactoryFunction<T> {
let token = Symbol();
return (dependencyContainer: DependencyContainer) => {
if (dependencyContainer.isRegistered(token)) {
return dependencyContainer.resolve(token);
} else {
const instance = factoryFunc(dependencyContainer);
dependencyContainer.registerInstance(token, instance);
return instance;
}
};
}