前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >多库操作:多个数据库的动态切换(一)

多库操作:多个数据库的动态切换(一)

作者头像
老张的哲学
发布2022-04-11 11:27:36
2K0
发布2022-04-11 11:27:36
举报
文章被收录于专栏:NetCore 从壹开始

在平时的开发中,受到传统模式的影响,我们都是习惯了单一的数据库表操作,把数据都建到一个库里边,然后进行增删改查,这个是很经典的开发模式。 但是随着项目开发,总会出现这样的应用场景: 1、我们新的系统建立了新库,但是老板让我们把具有相同结构的老数据库也带上(导入到一起或者定时同步,这里不讨论) 2、项目慢慢变大,我们要分库分表了,可能订单数据和用户数据被分开了,但是同一个api业务逻辑里,可能我们需要操作多个DB,比如我正在走的是主库,然后有一个操作,需要把数据从另一个DB里区保存或者查询。 3、想在测试的时候,同时无缝测试多个库连接,比如我的Blog.Core,每次我提交一个版本,都需要对Sqlite、MySql、MSSql(LocalDB)等同时做测试,那我就想在不停掉项目的前提下,做多库测试。

其实说了那么多,就是想实现一个工作,就是多库操作,毕竟这是一个趋势,今天我们就简单说一下多库操作的第一弹 —— 动态切换数据库。过程很简单,这里就先说一下吧。

鸣谢:@Game结束 小伙伴提供思路和代码。

1、修改配置DB连接字符串集合

目前我的Blog.Core项目中,使用的是SqlSugar的ORM,如果你用其他的,也是可以的,思路都是一样的,可能具体操作细节和写法上不太一样。

修改我们的appsettings.json,配置连接字符串

代码语言:javascript
复制
  "DBS": [
    /*
      MySql = 0,
      SqlServer = 1,
      Sqlite = 2,
      Oracle = 3,
      PostgreSQL = 4
    */
    {
      "ConnId": 1,// 连接id,可以配置到数据库
      "DBType": 2,// db类型,枚举,具体的看上边
      "Enabled": true,// 是否开启当前数据库db
      "Connection": "WMBlog.db" // 连接字符串
    },
    {
      "ConnId": 2,
      "DBType": 1,
      "Enabled": true,
      "Connection": "Server=.;Database=WMBlogDB;User ID=sa;Password=123;",
      "ProviderName": "System.Data.SqlClient"
    },
    {
      "ConnId": 3,
      "DBType": 0,
      "Enabled": false,
      "Connection": "Server=localhost; Port=3306;Stmt=; Database=wmblogdb; Uid=root; Pwd=456;"
    },
    {
      "ConnId": 4,
      "DBType": 3,
      "Enabled": false,
      "Connection": "Provider=OraOLEDB.Oracle; Data Source=WMBlogDB; User Id=sss; Password=789;",
      "OracleConnection_other1": "User ID=sss;Password=789;Data Source=(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=192.168.8.65)(PORT=1521)))(CONNECT_DATA=(SERVICE_NAME = orcl)))"
    }
  ],

这个设计很简单,我们可以配置到appsettings.json里,同时也可以配置到内存里,至于能不能放到数据库里,我还没有操作过,不过放到配置文件里已经基本可以了。

2、配置连接数据对象

我们这里使用的既然是Sqlsugar,那就把相应的连接配置对象注入到服务里,本来使用的是上下文,但是后来为了事务,也发现上下文有点儿多余,因为sqlsugar自带了部分上下文的功能,所以就直接使用的ISqlSugarClient。

那首先我们就需要把刚刚json配置文件的连接字符串,封装一下:

在 Common 层的 BaseDBConfig 中,创建操作类:

代码语言:javascript
复制
 public class MutiDBOperate
 {
     public string ConnId { get; set; }
     public string Conn { get; set; }
     public DataBaseType DbType { get; set; }
 }

然后把json数据封装一下:

代码语言:javascript
复制
 public static List<MutiDBOperate> MutiConnectionString => MutiInitConn();
 
  public static List<MutiDBOperate> MutiInitConn()
 {
     List<MutiDBOperate> listdatabase = new List<MutiDBOperate>();
     string Path = "appsettings.json";
     using (var file = new StreamReader(Path))
     using (var reader = new JsonTextReader(file))
     {
         var jObj = (JObject)JToken.ReadFrom(reader);
         if (!string.IsNullOrWhiteSpace("DBS"))
         {
             var secJt = jObj["DBS"];
             if (secJt != null)
             {
                 for (int i = 0; i < secJt.Count(); i++)
                 {
                     if (secJt[i]["Enabled"].ObjToBool())
                     {
                         listdatabase.Add(SpecialDbString(new MutiDBOperate()
                         {
                             ConnId = secJt[i]["ConnId"].ObjToString(),
                             Conn = secJt[i]["Connection"].ObjToString(),
                             DbType = (DataBaseType)(secJt[i]["DBType"].ObjToInt()),
                         }));
                     }
                 }
             }
         }
         return listdatabase;
     }
 }

3、注入服务

这里要说一下,既然是多个库连接,我就需要一个主库,就是当前的DB,为了达到切换的目的,我也在配置文件里做了相应的配置:

这个值,就是当前某一个连接对象的ConnId。同时也为了在代码里能控制这个值,我采用对象静态类的方式:

代码语言:javascript
复制
namespace Blog.Core.Common.DB
{
    public static class MainDb
    {
        // 这个id,就是我们配置文件的id
        public static string CurrentDbConnId = "1";
    }
}

现在我们把刚刚的配置数据对象一起注入到服务里:

代码语言:javascript
复制
 /// <summary>
 /// SqlSugar 启动服务
 /// </summary>
 public static class SqlsugarSetup
 {
     public static void AddSqlsugarSetup(this IServiceCollection services)
     {
         if (services == null) throw new ArgumentNullException(nameof(services));

         // 把多个连接对象注入服务,这里可以采用Transient瞬态,也可以Scope
         services.AddTransient(o =>
         {
             List<ISqlSugarClient> sqlSugarClients = new List<ISqlSugarClient>();

             var beforeDbConnect = BaseDBConfig.MutiConnectionString;
             var index = beforeDbConnect.FindIndex(x => x.ConnId == MainDb.CurrentDbConnId);
             if (index > 0)
             {
                 // 做一个交换,切换主db连接放到第一位
                 var firstDb = beforeDbConnect[0];
                 var changeDb = beforeDbConnect[index];
                 beforeDbConnect[index] = firstDb;
                 beforeDbConnect[0] = changeDb;
             }

             beforeDbConnect.ForEach(m =>
             {
                 sqlSugarClients.Add(new SqlSugarClient(new ConnectionConfig()
                 {
                     ConfigId = m.ConnId,
                     ConnectionString = m.Conn,
                     DbType = (DbType)m.DbType,
                     IsAutoCloseConnection = true,
                     //InitKeyType = InitKeyType.SystemTable
                 })
                );
             });
             return sqlSugarClients;
         });
     }
 }

4、仓储构造函数注入DB

目前我采用的架构是【service+repository+unitofwork】的模式,所以我们获取的Db就是ISqlSugarClient实例,那我们就把它注入进去。

在 UnitOfWork 的构造函数中,注入:

代码语言:javascript
复制
 private readonly List<ISqlSugarClient> _sqlSugarClients;

 public UnitOfWork(List<ISqlSugarClient> sqlSugarClients)
 {
     // 每次取最上边的db,这个顺序已经在注册服务的时候,切换好了
     _sqlSugarClient = sqlSugarClients[0];  
 }

5、其他修改

我们修改了db连接方式,那项目启动时候Seed种子数据的上下文也需要更改一下:

6、做下测试,动态切换

那到底能不能使用呢,这里我们测试一下:

首先我们打开两个数据库连接,一个是Sqlite,一个是MSSql,同时我们在两个各自的博客表中,数据做区分。

我们先执行一下blog查询,然后把maindb切换成"2"频道,也就是mssql的:

并不完美,遗留问题

这样我们每次访问api,是可以单独的做控制,但是这里有一个问题,就是我们同一个api内,是无法实现动态切换的目的的,我也在研究,在下篇文章中,我会想办法解决这个问题。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-12-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 NetCore 从壹开始 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1、修改配置DB连接字符串集合
  • 2、配置连接数据对象
  • 3、注入服务
  • 4、仓储构造函数注入DB
  • 5、其他修改
  • 6、做下测试,动态切换
  • 并不完美,遗留问题
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档