EfCore3的OwnedType會導致Sql效率問題

最近主導了旗下某核心項目升級到EfCore3

由於之前Core2升級時候也踩過不少的坑很多東西都有規劃和準備,整體上還是沒出太大問題

但是最近突然發現efcore對於使用了ownedType的生成語句有問題

查找了一下資料發現已經有人在efcore的github上開了issus了,並且還討論的蠻多的了

//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里也有人做了測試,結果下圖

image

縱坐標是每秒執行的查詢數(簡單理解為並發數吧)

橫坐標是表裡有多少數據

可以看到表的數據量上升之後使用了OwnedType的EfCore3會出現顯著下滑(自己join自己多了)

接下來就是一個好消息和一個壞消息了

好消息是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了