最近作個分庫分表項目,用到schema的切換感受仍是有些坑的,在此分享下。數據庫
先簡要說下咱們的分庫分表緩存
我定的規則是,訂單號(數字)除以16,得出的結果爲這個訂單所在的數據庫,而後他的餘數表明他所在這個庫裏面的哪一個表。ui
而後在一個庫裏面有16個表,這個怎麼實現呢?比較齪的辦法是 Order1/Order2這樣,不事後來我想了下,數據庫默認(咱們是Sql Server)是有schema的(默認是dbo的那個東西),設計
而後我就打起這個東西的主意,後續跟dba確認方案可行後,就決定好比在同一個庫裏,第一套訂單表是 p0.Order,第二套訂單表就是p1.Order(有效取值 p0~p15)。blog
若是說每次操做訂單,你都要記得要先根據訂單號拆分規則,找到這是哪一個數據庫,再去找這是哪一個schema,我以爲這代碼也別寫了,放棄把,這是不可維護的代碼,不具有可持續發展性。接口
咱們必需要將一個分庫分表這麼一件事,抽象爲某個單一的邏輯。文檔
因而乎,DbContext是個好東西!字符串
當咱們在說Ef的時候,實際上咱們在討論的主要就是他裏面的DbContext。而一個DbContext,則邏輯上表明瞭一個數據庫映射(包含數據庫鏈接/表等和數據庫相關的全部配置的集合)。get
只要咱們將分庫分表這件事,抽象爲如何獲取一個DbContext,以後在對這個DbContext作你想作的操做,那麼一切事情就簡單多了!it
因而乎我設計了這麼一個接口:
你給我一個訂單號,我還你一個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