将类型传递给依赖注入容器
pass type to dependency injection container
我在不使用 ORM 工具的情况下支持多个数据库。对于这个例子,我将展示一个 Postgres 和 MSSQL 的例子 UserQueries
我有一个接口 IDataBase
,多个数据库实现了这个接口。
@injectable()
export class MssqlDatabase implements IDatabase {
public async connect(): Promise<void> {
}
public async disconnect(): Promise<void> {
}
}
此外,我有 IUserQueries
定义查询的界面
export interface IUserQueries {
fetchUsers(): Promise<QueryResult>;
}
QueryResult
是自定义 class 以确保每个数据库查询 returns 与我的 API 相同的数据对象。我的 API 从目标接口调用特定查询。如果 mssql
是配置文件中的标识符,我的应用程序应该创建到 MSSQL 数据库的连接。但是我的 API 文件不应该关心并且不知道这一点(显然,这就是接口的作用)。
一个基本的例子是
@injectable()
export class UserRepository implements IUserRepository {
public userQueries: IUserQueries;
constructor(@inject(IoCTypes.IUserQueries) userQueries: IUserQueries) {
this.userQueries = userQueries;
}
}
现在事情变得棘手了。我的 DI 容器:
const container: Container = new Container();
container.bind<IUserQueries>(IoCTypes.IUserQueries).to(/* what? */);
export { container };
DI 容器不知道选择哪个查询文件。它可能是 MSSQLUserQueries
或 PostgresUserQueries
。我怎么解决这个问题?启动服务器时 select 可以进行正确的查询 (app.ts) 但据我所知,这个 DI 容器完全独立于应用程序并且只是作为一个配置文件,所以我不应该传递一个抽象 UserQueries
class.
如果我理解正确,你的问题是关于在运行时选择某个接口(IUserQueries
)的具体实现(MSSQLUserQueries
、PostgresUserQueries
)之一——基于一些配置。最简单的方法是使用工厂,它知道配置和 returns IUserQueries
.
的正确实现
container.bind<interfaces.Factory<IUserQueries>>(IoCTypes.IUserQueriesFactory).toFactory<IUserQueries>((context: interfaces.Context) => {
return () => {
const config = context.container.get('configuration');
//if config.isPostgresql return PostgresUserQueries
//if config.isMssql return MSSQLUserQueries
};
});
你可以使用工厂模式:
interface IUserQueryFactory {
get(): IUserQueries;
get(type: string): IUserQueries;
}
class UserQueryFactory implements IUserQueryFactory {
get(): IUserQueries {
const defaultValue = config.database; // or whatever returns your "mssql" from config
return this.get(defaultValue);
}
get(type: string): IUserQueries {
switch (type) {
case "mssql":
return new MSSQLUserQueries();
case "postgresql":
return new PostgresUserQueries();
default:
return null;
}
}
}
const container: Container = new Container();
container.bind<IUserQueryFactory>(IoCTypes.IUserQueryFactory).to(UserQueryFactory);
container.bind<IUserQueries>(IoCTypes.IUserQueries).toDynamicValue((context: interfaces.Context) => { return context.container.get<IUserQueryFactory>(IoCTypes.IUserQueryFactory).get(); });
export { container };
我不确定我是否正确理解了整个语法,因为这没有经过测试,但我想你明白了:
IUserQueryFactory
returns IUserQueries
基于输入类型或 returns 默认值(在配置中定义)如果类型不是提供者
IUserQueries
的默认实现基本上是通过 IUserQueryFactory
实现的,这意味着创建 IUserQueries
的整个源代码都在一个地方并且易于维护。
我在不使用 ORM 工具的情况下支持多个数据库。对于这个例子,我将展示一个 Postgres 和 MSSQL 的例子 UserQueries
我有一个接口 IDataBase
,多个数据库实现了这个接口。
@injectable()
export class MssqlDatabase implements IDatabase {
public async connect(): Promise<void> {
}
public async disconnect(): Promise<void> {
}
}
此外,我有 IUserQueries
定义查询的界面
export interface IUserQueries {
fetchUsers(): Promise<QueryResult>;
}
QueryResult
是自定义 class 以确保每个数据库查询 returns 与我的 API 相同的数据对象。我的 API 从目标接口调用特定查询。如果 mssql
是配置文件中的标识符,我的应用程序应该创建到 MSSQL 数据库的连接。但是我的 API 文件不应该关心并且不知道这一点(显然,这就是接口的作用)。
一个基本的例子是
@injectable()
export class UserRepository implements IUserRepository {
public userQueries: IUserQueries;
constructor(@inject(IoCTypes.IUserQueries) userQueries: IUserQueries) {
this.userQueries = userQueries;
}
}
现在事情变得棘手了。我的 DI 容器:
const container: Container = new Container();
container.bind<IUserQueries>(IoCTypes.IUserQueries).to(/* what? */);
export { container };
DI 容器不知道选择哪个查询文件。它可能是 MSSQLUserQueries
或 PostgresUserQueries
。我怎么解决这个问题?启动服务器时 select 可以进行正确的查询 (app.ts) 但据我所知,这个 DI 容器完全独立于应用程序并且只是作为一个配置文件,所以我不应该传递一个抽象 UserQueries
class.
如果我理解正确,你的问题是关于在运行时选择某个接口(IUserQueries
)的具体实现(MSSQLUserQueries
、PostgresUserQueries
)之一——基于一些配置。最简单的方法是使用工厂,它知道配置和 returns IUserQueries
.
container.bind<interfaces.Factory<IUserQueries>>(IoCTypes.IUserQueriesFactory).toFactory<IUserQueries>((context: interfaces.Context) => {
return () => {
const config = context.container.get('configuration');
//if config.isPostgresql return PostgresUserQueries
//if config.isMssql return MSSQLUserQueries
};
});
你可以使用工厂模式:
interface IUserQueryFactory {
get(): IUserQueries;
get(type: string): IUserQueries;
}
class UserQueryFactory implements IUserQueryFactory {
get(): IUserQueries {
const defaultValue = config.database; // or whatever returns your "mssql" from config
return this.get(defaultValue);
}
get(type: string): IUserQueries {
switch (type) {
case "mssql":
return new MSSQLUserQueries();
case "postgresql":
return new PostgresUserQueries();
default:
return null;
}
}
}
const container: Container = new Container();
container.bind<IUserQueryFactory>(IoCTypes.IUserQueryFactory).to(UserQueryFactory);
container.bind<IUserQueries>(IoCTypes.IUserQueries).toDynamicValue((context: interfaces.Context) => { return context.container.get<IUserQueryFactory>(IoCTypes.IUserQueryFactory).get(); });
export { container };
我不确定我是否正确理解了整个语法,因为这没有经过测试,但我想你明白了:
IUserQueryFactory
returnsIUserQueries
基于输入类型或 returns 默认值(在配置中定义)如果类型不是提供者IUserQueries
的默认实现基本上是通过IUserQueryFactory
实现的,这意味着创建IUserQueries
的整个源代码都在一个地方并且易于维护。