diff --git a/CHANGELOG.md b/CHANGELOG.md index bc486003..2567c89f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed ### Fixed +- Fixed container to properly resolve async `.toService` bindings. ## [6.1.4] diff --git a/src/syntax/binding_to_syntax.ts b/src/syntax/binding_to_syntax.ts index e4017290..1016e1d2 100644 --- a/src/syntax/binding_to_syntax.ts +++ b/src/syntax/binding_to_syntax.ts @@ -116,9 +116,14 @@ class BindingToSyntax implements interfaces.BindingToSyntax { } public toService(service: interfaces.ServiceIdentifier): void { - this.toDynamicValue((context: interfaces.Context) => - context.container.get(service), - ); + this.toDynamicValue((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); + } + }); } } diff --git a/test/bugs/issue_1518.test.ts b/test/bugs/issue_1518.test.ts index 5ce5d50d..8dc7baa3 100644 --- a/test/bugs/issue_1518.test.ts +++ b/test/bugs/issue_1518.test.ts @@ -2,7 +2,7 @@ import { expect } from 'chai'; import { Container } from '../../src/inversify'; -describe('Issue 1515', () => { +describe('Issue 1518', () => { it('should not throw on deactivating undefined singleton values', () => { const container: Container = new Container(); const symbol: symbol = Symbol.for('foo'); diff --git a/test/bugs/issue_1564.test.ts b/test/bugs/issue_1564.test.ts new file mode 100644 index 00000000..eb1d1285 --- /dev/null +++ b/test/bugs/issue_1564.test.ts @@ -0,0 +1,46 @@ +import { expect } from 'chai'; +import { describe, it } from 'mocha'; + +import { Container, inject, injectable } from '../../src/inversify'; + +describe('Issue 1564', () => { + it('should not throw on getting async services bound using "toService"', async () => { + @injectable() + class Database { + constructor() { + console.log('new Database'); + } + } + + @injectable() + class Service1 { + constructor(@inject(Database) public database: Database) { + console.log('new Service1'); + } + } + + @injectable() + class Service2 { + constructor(@inject(Service1) public service1: Service1) { + console.log('new Service2'); + } + } + + const container: Container = new Container({ defaultScope: 'Request' }); + + container.bind(Database).toDynamicValue(async () => { + console.log('connecting to db...'); + return new Database(); + }); + + container.bind(Service1).toSelf(); + container.bind(Service2).toSelf(); + + container.bind('services').toService(Service1); + container.bind('services').toService(Service2); + + const result: unknown[] = await container.getAllAsync('services'); + + expect(result).to.have.length(2); + }); +});