最近主導了旗下某核心專案升級到EfCore3
由於之前Core2升級時候也踩過不少的坑很多東西都有規劃和準備,整體上還是沒出太大問題
但是最近突然發現efcore對於使用了ownedType的生成語句有問題
查詢了一下資料發現已經有人在efcore的github上開了issus了,並且還討論的蠻多的了
https://github.com/dotnet/efcore/issues/18299
鑑於很多人看到一堆E文後會直接放棄,下面我簡單闡述下這個問題
EfCore自2.0的時候引入了一個叫OwnedType的特性,是用於完善之前EfCore1.x相比於Ef6使其少了的ComplexType特性
正常來說我們用Ef的時候是一個類對映到一個表,但是有時候某些表欄位過多的情況下,我們可能會想整理下把一個表裡某些資訊放到一個子類裡,但是其他資訊還是在主類
形如
class Order { public int Id { get; set; } public string Title { get; set; } public Address Address { get; set; } } class Address { public string Street { get; set; } public string City { get; set; } }
然後DbContext裡配置下
modelBuilder.Entity<Order>().OwnsOne(x => x.Address);
這種情況下最理想生成的語句應該是類似
select Id,Title,Street,City from Orders
這個樣子的形式才對,然後EfCore內部再通過自己對映的形式把後面4個欄位對映到Author類裡的Address這個類裡
但是在EfCore3裡他生成的語句是形如
SELECT o."Id", o."Title", t."Id", t."Address_City", t."Address_Street" FROM "Orders" AS o LEFT JOIN ( SELECT o0."Id", o0."Address_City", o0."Address_Street", o1."Id" AS "Id0" FROM "Orders" AS o0 INNER JOIN "Orders" AS o1 ON o0."Id" = o1."Id" WHERE (o0."Address_Street" IS NOT NULL) OR (o0."Address_City" IS NOT NULL) ) AS t ON o."Id" = t."Id" WHERE (t."Id" IS NULL)
儘管嚴格來說這個並不影響邏輯,但是這樣子join的話對Sql的效能和效率有挺不好的負面影響
就那個issus裡也有人做了測試,結果下圖
縱座標是每秒執行的查詢數(簡單理解為併發數吧)
橫座標是表裡有多少資料
可以看到表的資料量上升之後使用了OwnedType的EfCore3會出現顯著下滑(自己join自己多了)
另外該問題在EfCore2的時候是沒有的,就Core3才有(直覺是Core3的OwnedType被魔改了可以支援OwnsMany等的支援的時候引入的)
接下來就是一個好訊息和一個壞訊息了
好訊息是EfCore那邊認可了這個問題然後當前EfCore 5 preview-3修復了這個問題
壞訊息是至少目前確認這個修復不會合併到EfCore3.1.x(後期會不會有變數不清楚)
感覺這個蠻坑的,一般公司用的話都會是優先選用LTS,而當前的LTS就是3.1
而接下來的NetCore5(含EfCore5)並不是LTS所以對於公司組織的線上採用率應該會相對較低
那難道修復這個問題還要等一個目前還沒規劃的NetCore5.1?那是不是要等的有點兒太久了
最後,如果有升級EfCore3的且用了OwnedType(相當於Ef6時期的ComplexType)請謹慎評估下這個問題對你可能造成的影響
畢竟目前看起來,降級回去不大可能,燒香保佑下fix path能到3.1.x要麼就只能指望Core5.1了