EFCore动态切换Schema

最近做个分库分表项目,用到schema的切换感觉还是有些坑的,在此分享下。

 

先简要说下我们的分库分表

分库分表规则

我定的规则是,订单号(数字)除以16,得出的结果为这个订单所在的数据库,然后他的余数代表他所在这个库里面的哪个表。

然后在一个库里面有16个表,这个怎么实现呢?比较龊的办法是 Order1/Order2这样,不过后来我想了下,数据库默认(我们是Sql Server)是有schema的(默认是dbo的那个东西),

然后我就打起这个东西的主意,后续跟dba确认方案可行后,就决定比如在同一个库里,第一套订单表是 p0.Order,第二套订单表就是p1.Order(有效取值 p0~p15)。

 

代码设计

如果说每次操作订单,你都要记得要先根据订单号拆分规则,找到这是哪个数据库,再去找这是哪个schema,我觉得这代码也别写了,放弃把,这是不可维护的代码,不具备可持续发展性。

我们必须要将一个分库分表这么一件事,抽象为某个单一的逻辑。

于是乎,DbContext是个好东西!

当我们在说Ef的时候,实际上我们在讨论的主要就是他里面的DbContext。而一个DbContext,则逻辑上代表了一个数据库映射(包含数据库连接/表等和数据库相关的所有配置的集合)。

只要我们将分库分表这件事,抽象为如何获取一个DbContext,之后在对这个DbContext做你想做的操作,那么一切事情就简单多了!

 

于是乎我设计了这么一个接口:

image

你给我一个订单号,我还你一个DbContext~

 

正题

如何让DbContext支持分库分表

这里主要有2个问题,一个是如何改连接字符串,另一个如何改schema。

问题1简单,创建DbContext的时候本来就是要串连接字符串进去的,直接这里构造好连接字符串即可。

问题2比较复杂,也是这篇文章主要内容,首先我的设计是在创建DbContext,传入schema,在OnModelCreating里用这个schema初始化。

代码是这样子的:

image

于是乎你第一次创建DbContext的时候,schema是什么(别在意我代码变量名当时写错了这么个细节),就永远是什么,后续你重复创建其他DbContext的时候其实这句话并不能让你修改schema。

 

后面我发觉,EfCore在指定表名的时候,是可以顺带指定schema的,于是乎在改下,改成了类似这样:

image

然而实践证明依然并没有什么卵用。

 

坑了2次后我严重怀疑efcore里一定有某种级别的缓存机制,使得初次赋值之后某些信息不会再被更新(甚至于怀疑到efcore2.x引入的DbContextPool,然而我都是new出来的我没pool啊)。

 

后面一通乱找后不记得再哪个网址(但是记得一定是stackoverflow里)找到了对这个类 IModelCacheKeyFactory的一些描述。

好像是efcore会对Model(你的实体)和DbContext之间产生一个缓存,而我的分库分表用的DbContext只有一个(只是动态修改了某些参数配置),于是乎觉得应该就是这个东西缓存了的关系导致,然后我重写了这货的实现:

image

本质就是将DbContext里的当前的Schema暴露出给ModelCacheKey读取,然后进行Equal比较的时候Schema也作为一个Equal的因素,当两者比较不等的时候,就不会再采用之前错误Schema的缓存了

最后在创建DbContextOptionBuilder的时候Replace一下

builder.ReplaceService<IModelCacheKeyFactory, SchemaModelCacheFactory>();

即可完成

后续亲测都能动态切换schema,圆满完成。

 

 

后面发现(评论里提到的)其实这里已经有说明这个问题了,文档看不仔细,躺坑两行泪

https://docs.microsoft.com/zh-cn/ef/core/modeling/dynamic-model

posted @ 2019-03-02 15:05  LeoLaw  阅读(4727)  评论(9编辑  收藏  举报