diff --git a/CHANGELOG.md b/CHANGELOG.md index e41c9d62..388682ae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - Fixed container to properly resolve async `.toService` bindings. +- Fixed `.toService` binding to properly disable caching any values. ## [6.1.4] diff --git a/src/syntax/binding_to_syntax.ts b/src/syntax/binding_to_syntax.ts index 1016e1d2..cb4123c9 100644 --- a/src/syntax/binding_to_syntax.ts +++ b/src/syntax/binding_to_syntax.ts @@ -116,14 +116,28 @@ class BindingToSyntax implements interfaces.BindingToSyntax { } public toService(service: interfaces.ServiceIdentifier): void { - this.toDynamicValue((context: interfaces.Context): T | Promise => { + this._binding.type = BindingTypeEnum.DynamicValue; + + // Service bindings should never ever be cached. This is just a workaround to achieve that. A better design should replace this approach. + Object.defineProperty(this._binding, 'cache', { + configurable: true, + enumerable: true, + get() { + return null; + }, + set(_value: unknown) {}, + }); + this._binding.dynamicValue = ( + context: interfaces.Context, + ): T | Promise => { try { return context.container.get(service); } catch (_error: unknown) { // This is a performance degradation in this edge case, we do need to improve the internal resolution architecture in order to solve this properly. return context.container.getAsync(service); } - }); + }; + this._binding.implementationType = null; } } diff --git a/test/bugs/issue_1416.test.ts b/test/bugs/issue_1416.test.ts new file mode 100644 index 00000000..d6603e28 --- /dev/null +++ b/test/bugs/issue_1416.test.ts @@ -0,0 +1,42 @@ +import { describe, it } from 'mocha'; +import sinon from 'sinon'; + +import { Container, injectable, preDestroy } from '../../src/inversify'; + +describe('Issue 1416', () => { + it('should allow providing default values on optional bindings', async () => { + @injectable() + class Test1 { + public stub: sinon.SinonStub = sinon.stub(); + + @preDestroy() + public destroy() { + this.stub(); + } + } + + @injectable() + class Test2 { + public destroy(): void {} + } + + @injectable() + class Test3 { + public destroy(): void {} + } + + const container: Container = new Container({ defaultScope: 'Singleton' }); + + container.bind(Test1).toSelf(); + container.bind(Test2).toService(Test1); + container.bind(Test3).toService(Test1); + + const test1: Test1 = container.get(Test1); + container.get(Test2); + container.get(Test3); + + container.unbindAll(); + + sinon.assert.calledOnce(test1.stub); + }); +}); diff --git a/wiki/tagged_bindings.md b/wiki/tagged_bindings.md index d205b403..3dd9a7fa 100644 --- a/wiki/tagged_bindings.md +++ b/wiki/tagged_bindings.md @@ -1,7 +1,7 @@ # Tagged bindings We can use tagged bindings to fix `AMBIGUOUS_MATCH` errors when two or more -concretions have been bound to an abstraction. Notice how the constructor +concretions have been bound to an abstraction. Notice how the constructor arguments of the `Ninja` class have been annotated using the `@tagged` decorator: ```ts