当前位置: 首页 > wzjs >正文

做网站浏览器标签一般放哪什么是软件开发工具

做网站浏览器标签一般放哪,什么是软件开发工具,代码导入wordpress,食品企业网站建设方案概述 TypeORM 可以通过 TypeORM 中的 forRootAsync 方法读取点位文件中的配置信息,随后它会自动加载驱动,以便与各类数据库进行对接Prisma 部分依托于 PrismaClient,无需手动安装数据库驱动。我们直接扩展 PrismaClient 就能得到一个 Prisma…

概述

  • TypeORM 可以通过 TypeORM 中的 forRootAsync 方法读取点位文件中的配置信息,随后它会自动加载驱动,以便与各类数据库进行对接
  • Prisma 部分依托于 PrismaClient,无需手动安装数据库驱动。我们直接扩展 PrismaClient 就能得到一个 PrismaService,进而实现对数据库的读写操作
  • 至于 Mongoose 与 上面两个类似
  • 我们的目标是让项目既能与单库对接,也能应对多库场景

关于多租户

  • 以用户进入系统为例,NestJS 会读取当前用户的租户信息
  • 每个用户都携带不同的标签,不同的标签用于区分读取不同的数据库
  • 根据用户标签读取不同数据库的配置信息从而获取不同数据库的实例来操作对应的数据库
  • 这时候,就需要依赖 动态模块 (Dynamic Module)
    • 动态模块提供一种API, 将一个模块导入到另外一个模块
    • 并且定义该模块属性的时候可以 影响到 它的属性和行为
    • 动态模块 可以让人非常方便的自定义模块,并且注册和配置它
    • 在注册模块的过程中,对其配置,传入参数,注册逻辑进行定制
    • 这是分层化编程的思想, AOP 编程
  • 单库读取相对简单,而通用框架通常会涵盖单库功能
  • 所以,我们着重针对多库场景进行数据库设计
  • 要把程序封装起来,通过依赖注入的方式读取配置来定义 url

Prisma 的多租户系统集成

  • 之前,我们集成Prisma的时候,是用了一套配置,也就是一个数据库的配置
  • 在相关service里面,如下
    import { Injectable, OnModuleInit } from '@nestjs/common';
    import { PrismaClient } from '@prisma/client';@Injectable()
    export class PrismaService extends PrismaClient implements OnModuleInit {async onModuleInit() {await this.$connect();}
    }
    
    • 上面,PrismaService 继承了 PrismaClient 类,并实现了 OnModuleInit 这个生命周期方法
    • 该方法内部会调用 Prisma$connect 方法来连接对应的数据库
  • 如果应用场景需要通过 Prisma 操作多个数据库,就不能使用同一套配置
  • 因为不同用户可能属于不同租户,所以要以租户为单元,让不同用户读取不同的数据库配置
  • 这就引出了一个问题:Prisma 如何动态接收配置?
  • 参考: accessing-environment-variables-from-the-schema
    • 点击第一个 Data sources 跳转到 programmatically-override-a-datasource-url
  • 就是用程序的方式,动态的写这个 url, 也就是把 datasouce url 拆分成不同字段,这样的目的就是把 url 拆分成 host, user, password 等字段分开管理
  • 生成不同的 PrismaClient 存放到 map 中, 后续,当不同的 用户 过来之后,通过 map 定位到 不同的 PrismaClient 从而操作不同数据库
  • 普通的写法只能让 Prisma 读取配置文件中的一个数据库,如果要实现动态配置,需要参考 之前 TypeOrmModule.forRootAsync 的方法 传递给 prisma.module.ts,并通过依赖注入的方式将 ConfigService 注入其中
  • 接收到数据后,我们需要将其拼接成 Prisma 要求的结构,这就需要了解 connectionURLs 的格式,通常前面是数据库类型、用户密码、主机和端口,后面可能还有查询参数,参考:connection-urls

如何实现动态模块

  • 我们知道 TypeOrmModule.forRootTypeOrmModule.forRootAsync 都是 Dynamic modules 即动态模块
  • 我们探索其源码,如下
    import { DynamicModule } from '@nestjs/common';
    import { DataSource, DataSourceOptions } from 'typeorm';
    import { EntityClassOrSchema } from './interfaces/entity-class-or-schema.type';
    import { TypeOrmModuleAsyncOptions, TypeOrmModuleOptions } from './interfaces/typeorm-options.interface';
    export declare class TypeOrmModule {static forRoot(options?: TypeOrmModuleOptions): DynamicModule;static forFeature(entities?: EntityClassOrSchema[], dataSource?: DataSource | DataSourceOptions | string): DynamicModule;static forRootAsync(options: TypeOrmModuleAsyncOptions): DynamicModule;
    }
    
  • forRootAsync 能接收参数并返回一个 DynamicModule,这是 NestJS 中的重要概念
  • 这样,通过 注入 一个 configService 实例可以读取不同的配置, 不需要通过函数式编程 new 出来 多个 configService 实例导致性能问题
  • 关于动态模块,参考官网:dynamic-modules
  • 按照这个文档上的 modules#dynamic-modules
    import { Module, DynamicModule } from '@nestjs/common';
    import { createDatabaseProviders } from './database.providers';
    import { Connection } from './connection.provider';@Module({providers: [Connection],exports: [Connection],
    })
    export class DatabaseModule {static forRoot(entities = [], options?): DynamicModule {const providers = createDatabaseProviders(options, entities);return {module: DatabaseModule,providers: providers,exports: providers,};}
    }
    
  • 定义一个全新的动态模块,这个模块可以接受参数,它返回的也是一个模块结构
  • 它的作用是:让程序员方便的自定义模块,并且注册和配置它,也就是在注册模块的过程中,对其配置,传入参数,注册逻辑等进行定制,这就是AOP编程思想

1 )官方示例

import { Module } from '@nestjs/common';
import { UsersService } from './users.service';@Module({providers: [UsersService],exports: [UsersService],
})
export class UsersModule {}
  • 这里有一个 UserModule, 里面定义了 UsersService

  • 下面又定义了一个 AuthModule 模块,里面使用了 UsersModule

    import { Module } from '@nestjs/common';
    import { AuthService } from './auth.service';
    import { UsersModule } from '../users/users.module';@Module({imports: [UsersModule],providers: [AuthService],exports: [AuthService],
    })
    export class AuthModule {}
    
  • 进入 AuthService 中, 里面使用了 UsersService 的实例

    import { Injectable } from '@nestjs/common';
    import { UsersService } from '../users/users.service';@Injectable()
    export class AuthService {constructor(private usersService: UsersService) {}/*Implementation that makes use of this.usersService*/
    }
    
  • 这里做了3件事

    • 实例化 UsersModule, 传递导入 UsersModule的本身的其他模块,包含解析Providers依赖项,相当于初始化了 UsersService的实例,并且导出出来,这个实例被放入了 DI 系统中
    • 实例化 AuthModule, 使 UsersModule的导出服务实例可用于 AuthModule 中的组件,类似于参数的形式来注入和调用
    • 在 AuthModule 中注入 UsersModule 的实例

2 )动态模块的使用场景

  • 通过静态模块的绑定,消费模块时无法影响主机模块的配置。
  • 比如,我们有一个通用模块,这个模块要在不同场景中表现出不同的情况
  • 我们需要可定制化地传递 databaseURL,就像系统中的插件概念
  • 动态模块提供了一种 API,可以将一个模块导入另一个模块,并在定义该模块属性时影响其属性和行为
  • 与静态模块相比,动态模块多了定义模块属性和改变模块行为的功能
  • 动态模块并不是说是动态注册的,而是在注册的过程中,通过参数的方式影响其注册的行为

3 )另外的官方示例

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { ConfigModule } from './config/config.module';@Module({imports: [ConfigModule],controllers: [AppController],providers: [AppService],
})
export class AppModule {}
  • 我们之前使用 ConfigService 是写死的,后续我们可以如上定制下此类ConfigModule

  • 下面开始调用 ConfigModule

    import { Module } from '@nestjs/common';
    import { AppController } from './app.controller';
    import { AppService } from './app.service';
    import { ConfigModule } from './config/config.module';@Module({imports: [ConfigModule.register({ folder: './config' })],controllers: [AppController],providers: [AppService],
    })
    export class AppModule {}
    
  • 这里调用了 ConfigModule.register 方法,我们看下这个 register 方法的初步面貌 (下面仅为过渡演示代码)

    import { DynamicModule, Module } from '@nestjs/common';
    import { ConfigService } from './config.service';@Module({})
    export class ConfigModule {static register(): DynamicModule {return {module: ConfigModule,providers: [ConfigService],exports: [ConfigService],};}
    }
    
  • 这里 register 是一个静态方法,返回的是 DynamicModule 类型

  • 内部 register 逻辑在 ConfigService 部分初步面貌如下 (下面仅为过渡演示代码)

    import { Injectable } from '@nestjs/common';
    import * as dotenv from 'dotenv';
    import * as fs from 'fs';
    import * as path from 'path';
    import { EnvConfig } from './interfaces';@Injectable()
    export class ConfigService {private readonly envConfig: EnvConfig;constructor() {const options = { folder: './config' };const filePath = `${process.env.NODE_ENV || 'development'}.env`;const envFile = path.resolve(__dirname, '../../', options.folder, filePath);this.envConfig = dotenv.parse(fs.readFileSync(envFile));}get(key: string): string {return this.envConfig[key];}
    }
    
  • 完整的 ConfigModule 及其 register 如下

    import { DynamicModule, Module } from '@nestjs/common';
    import { ConfigService } from './config.service';@Module({})
    export class ConfigModule {static register(options: Record<string, any>): DynamicModule {return {module: ConfigModule,providers: [{provide: 'CONFIG_OPTIONS',useValue: options,},ConfigService,],exports: [ConfigService],};}
    }
    
  • 完整的 ConfigService 如下

    import * as dotenv from 'dotenv';
    import * as fs from 'fs';
    import * as path from 'path';
    import { Injectable, Inject } from '@nestjs/common';
    import { EnvConfig } from './interfaces';@Injectable()
    export class ConfigService {private readonly envConfig: EnvConfig;constructor(@Inject('CONFIG_OPTIONS') private options: Record<string, any>) {const filePath = `${process.env.NODE_ENV || 'development'}.env`;const envFile = path.resolve(__dirname, '../../', options.folder, filePath);this.envConfig = dotenv.parse(fs.readFileSync(envFile));}get(key: string): string {return this.envConfig[key];}
    }
    
  • 这里用了 @Inject 的装饰器,是为了让 ConfigModule 中的 ConfigService 接收到 上面 register 方法传递进来的 options 参数,利用了 providers 的写法

  • 上面的 providers 写法是在 nestjs 的 DI 容器里,全局给了 options 的参数,参数的名称为里面 provide 定义的 CONFIG_OPTIONS 字符串,后面,我们应该将这个字符串抽离成常量,如下

    export const CONFIG_OPTIONS = 'CONFIG_OPTIONS';
    
  • 这样,就不会出错,容易统一修改

  • 后续使用的时候,就向在上面的 ConfigService 的 constructor 中,通过 @Inject 依赖注入的方式,注入到私有的变量中去, 下面就正常使用了


文章转载自:

http://b7iAX34F.Ljzss.cn
http://I1U0mV69.Ljzss.cn
http://QRn0LhF5.Ljzss.cn
http://tcVgawAG.Ljzss.cn
http://QtijWdIl.Ljzss.cn
http://0cxQQqCe.Ljzss.cn
http://Hoq0ZgkQ.Ljzss.cn
http://JUkBjFcj.Ljzss.cn
http://8zBUSi7T.Ljzss.cn
http://ZydfFaPC.Ljzss.cn
http://9pCS1xsk.Ljzss.cn
http://s5a0DPAh.Ljzss.cn
http://5IhQ8Dgl.Ljzss.cn
http://cdmSVvut.Ljzss.cn
http://LDG9ZYro.Ljzss.cn
http://F4u59xvK.Ljzss.cn
http://qXmMF3zD.Ljzss.cn
http://NUwuvPvX.Ljzss.cn
http://2JdtjERM.Ljzss.cn
http://JKPA2Myb.Ljzss.cn
http://oKn1F41I.Ljzss.cn
http://xwcm7T0p.Ljzss.cn
http://ZjXtlRsP.Ljzss.cn
http://v0VftolV.Ljzss.cn
http://pwphWucg.Ljzss.cn
http://sNjTwZIu.Ljzss.cn
http://ICmuEwgA.Ljzss.cn
http://z5Uk7oIQ.Ljzss.cn
http://virT5Yg6.Ljzss.cn
http://CoxyV1ye.Ljzss.cn
http://www.dtcms.com/wzjs/699423.html

相关文章:

  • 亿码酷网站建设网易企业邮箱输入完整的邮箱地址怎么填写
  • 网站建设咸阳湖北聚四方建设有限公司网站
  • 做免费漫画网站有风险吗宁波网络公司招聘信息
  • 富阳网站建设报价免费动图制作app
  • 无锡建设网站wordpress代码执行漏洞
  • 系统网站开发网页设制作与网站建设宝典 pdf
  • 北京高端企业网站建设大同工业园区招聘信息
  • 上海史特做网站多少钱win2003怎么做网站
  • 营口规划建设局网站做视频网站怎么盈利模式
  • 如何设置公司网站创建商城
  • 宣传旅游网站建设河南郑州新闻
  • 中国建设银行官网站住房公积金c++软件开发需要学什么
  • 做宠物的网站微网站什么意思
  • 麻涌镇网站仿做做网站需要机吗
  • 手机营销型网站制作江西省seo
  • 网站客户留言高校文明校园建设专题网站
  • 腾讯云快速建站百度指数怎么提升
  • 开发个网站开票名称是什么意思怎样在微信中做网站
  • 佛山模板网站建设泰安做网站优化
  • 关于建设门户网站的通知企业网站建设规划设计任务书
  • 推荐外贸网站建设的公司公司网站asp源码
  • 关于h5的网站模板wordpress文本块
  • 洛阳市住房和城乡建设网站wordpress个人网站模板
  • 做网站重要标签图库素材网站模板
  • 品牌网站 响应式网站视频营销成功的案例
  • 泉州比较好的网站开发建设公司seo搜索引擎优化就业指导
  • 太原网站建设推广服务wordpress学习教程
  • 做破解软件网站赚广告费游戏网站模板免费下载
  • 秦皇岛网站开发报价济南网站建设 小程序
  • 棋牌 彩票网站建设手机网站开发注意的问题