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

Question: Scoped Containers and "default" connection #51

Open
MWhite-22 opened this issue Jun 29, 2021 · 8 comments
Open

Question: Scoped Containers and "default" connection #51

MWhite-22 opened this issue Jun 29, 2021 · 8 comments

Comments

@MWhite-22
Copy link

Trying to follow the below guideline from #39:

import { createConnection, useContainer } from 'typeorm';
import { Container } from 'typedi';

/** Tell TypeORM to use the TypeDI container to resolve it's dependencies. */
useContainer(Container);

/** Create a connection and start using TypeORM. */
createConnection({
  /* <connection options> */
}).catch(error => {
  console.error(`Couldn't connect to the database!`);
  console.error(error);
});

Originally posted by @NoNameProvided in #39 (comment)

and I get the new serviceNotFound: MaybeConstructable error. So I try using the Container exported from typeorm-typedi-extensions, and I get the "cannot get connection default" error.

Any guidance here on how to properly use scoped containers with TypeORM & TypeDI?

@MWhite-22
Copy link
Author

Full reproduction repo HERE

@MWhite-22
Copy link
Author

Ok after some fun debugging last night I found the issue.

The current TypeDI integration sets the ConnectionManager service for use by TypeORM, but doesnt set it as a global type. When we are in a scoped container, TypeDI goes through the below steps:

  1. Try to resolve @InjectRepository. As a custom injector, it uses the handler registered in the typeDI container below:
//...
       typedi_1.Container.registerHandler({
            object: object,
            index: index,
            propertyName: propertyName,
            value: containerInstance => getRepositoryHelper(connectionName, repositoryType, entityType, containerInstance),
        });
  1. Get repository helper now goes into the container instance and tries to get the TypeORM.ConnectionManager
function getRepositoryHelper(connectionName, repositoryType, entityType, containerInstance) {
    const connectionManager = containerInstance.get(typeorm_1.ConnectionManager);
    if (!connectionManager.has(connectionName)) {
        throw new manager_not_found_error_1.ConnectionNotFoundError(connectionName);
    }
  1. Our Container Instance (scoped), grabs the global container, finds the service in the global container, and also checks if the service is already in the scoped container. Here we do find the ConnectionManager in the global container and not in the scoped container, but its .global setting is not true. As such, we DO NOT return the globalService as is (//*1) which would include the required connections values. Instead, we create a cloned service (//*2), set the value to EMPTY, instantiate a new type and use that as the value (//*3). This creates a cloned ConnectionManager that is new, so it has no connections in it.
get(identifier) {
        const globalContainer = container_class_1.Container.of(undefined);
        const globalService = globalContainer.findService(identifier);
        const scopedService = this.findService(identifier);
        if (globalService && globalService.global === true)
            return this.getServiceValue(globalService);   //*1
        if (scopedService)
            return this.getServiceValue(scopedService);
        if (globalService && this !== globalContainer) {   //*2
            const clonedService = { ...globalService };
            clonedService.value = empty_const_1.EMPTY_VALUE;
            this.set(clonedService);
            const value = this.getServiceValue(clonedService);   //*3
            this.set({ ...clonedService, value });
            return value;
        }
        if (globalService)
            return this.getServiceValue(globalService);
        throw new service_not_found_error_1.ServiceNotFoundError(identifier);
    }

Changing the initial TypeDI Container.set to set ConnectionManager as global: true fixes all of this as it would then return that global service as found in //*1

@NoNameProvided @pleerock
Creating a PR to fix the above. Please let me know if there is a use case for non-global ConnectionManager that I am not correctly anticipating.

@MWhite-22
Copy link
Author

PR #53

@MWhite-22
Copy link
Author

@NoNameProvided @pleerock, any input on this?

@b0nbon1
Copy link

b0nbon1 commented Nov 19, 2021

@Xzas22 any work around on this? I'm facing the same issue

@MWhite-22
Copy link
Author

My work around was unfortunately to drop TypeORM until this got traction...

@MWhite-22
Copy link
Author

Gonna go ahead and drop another BUMP here as I think now more than every this package needs some attention. I did submit a PR with this change. See #53

@kevin-mitchell
Copy link

out of curiosity, rather than dropping TypeORM altogether did you consider dropping this package / the TypeDI integration and just manually resolving your repositories? That's where I'm thinking about going currently. TypeDI seems pretty stable, and TypeORM seems at least actively used.

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

No branches or pull requests

3 participants