最近做個分庫分表項目,用到schema的切換感覺還是有些坑的,在此分享下。
先簡要說下我們的分庫分表
分庫分表規則
我定的規則是,訂單號(數字)除以16,得出的結果為這個訂單所在的數據庫,然后他的余數代表他所在這個庫里面的哪個表。
然后在一個庫里面有16個表,這個怎么實現呢?比較齪的辦法是 Order1/Order2這樣,不過后來我想了下,數據庫默認(我們是Sql Server)是有schema的(默認是dbo的那個東西),
然后我就打起這個東西的主意,后續跟dba確認方案可行后,就決定比如在同一個庫里,第一套訂單表是 p0.Order,第二套訂單表就是p1.Order(有效取值 p0~p15)。
代碼設計
如果說每次操作訂單,你都要記得要先根據訂單號拆分規則,找到這是哪個數據庫,再去找這是哪個schema,我覺得這代碼也別寫了,放棄把,這是不可維護的代碼,不具備可持續發展性。
我們必須要將一個分庫分表這么一件事,抽象為某個單一的邏輯。
於是乎,DbContext是個好東西!
當我們在說Ef的時候,實際上我們在討論的主要就是他里面的DbContext。而一個DbContext,則邏輯上代表了一個數據庫映射(包含數據庫連接/表等和數據庫相關的所有配置的集合)。
只要我們將分庫分表這件事,抽象為如何獲取一個DbContext,之后在對這個DbContext做你想做的操作,那么一切事情就簡單多了!
於是乎我設計了這么一個接口:
你給我一個訂單號,我還你一個DbContext~
正題
如何讓DbContext支持分庫分表
這里主要有2個問題,一個是如何改連接字符串,另一個如何改schema。
問題1簡單,創建DbContext的時候本來就是要串連接字符串進去的,直接這里構造好連接字符串即可。
問題2比較復雜,也是這篇文章主要內容,首先我的設計是在創建DbContext,傳入schema,在OnModelCreating里用這個schema初始化。
代碼是這樣子的:
於是乎你第一次創建DbContext的時候,schema是什么(別在意我代碼變量名當時寫錯了這么個細節),就永遠是什么,后續你重復創建其他DbContext的時候其實這句話並不能讓你修改schema。
后面我發覺,EfCore在指定表名的時候,是可以順帶指定schema的,於是乎在改下,改成了類似這樣:
然而實踐證明依然並沒有什么卵用。
坑了2次后我嚴重懷疑efcore里一定有某種級別的緩存機制,使得初次賦值之后某些信息不會再被更新(甚至於懷疑到efcore2.x引入的DbContextPool,然而我都是new出來的我沒pool啊)。
后面一通亂找后不記得再哪個網址(但是記得一定是stackoverflow里)找到了對這個類 IModelCacheKeyFactory的一些描述。
好像是efcore會對Model(你的實體)和DbContext之間產生一個緩存,而我的分庫分表用的DbContext只有一個(只是動態修改了某些參數配置),於是乎覺得應該就是這個東西緩存了的關系導致,然后我重寫了這貨的實現:
本質就是將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