日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

EFCore查询语句生成流程、让EFCore支持批量Update/Delete/MergeInto

發布時間:2023/12/4 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 EFCore查询语句生成流程、让EFCore支持批量Update/Delete/MergeInto 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

引子

之前發現了一款叫 EFCore.BulkExtensions 的 nuget 包。里面提供了大量的 BulkInsertOrUpdateOrDelete 和 BatchUpdate 的拓展,可以很方便的解決批量更新和刪除的問題,不用讓 EFCore 一條一條的刪除和更新。

其中幾個比較有用的函數簽名是

Task<int> BatchDeleteAsync(this IQueryable<T> queryable); Task<int> BatchUpdateAsync(this IQueryable<T> queryable, Expression<Func<T, T>> updateExpression);

但是在升級到 ASP.NET Core 3.1 的時候,所有 Where 中的?someArray.Contains(i.Key)?全部掛掉了。而我的程序里用這一語句比較多,遂下載了其源代碼并合并了當時作者幾個月都沒合并的一個PR。

研究代碼,總結了該程序的基本運行過程:

  • 通過反射獲取各種私有變量來訪問到 DbContext

  • updateExpression 由這個包自己訪問表達式樹獲得

  • 讓 IQueryable 執行 GetEnumerator 讓 EFCore 生成對應的 Select 語句,進行字符串拼接

  • 由 DbContext.Database.ExecuteSqlRaw 來完成語句執行

  • 但是這過程有幾個問題:

  • 有幾種句式 updateExpression 會翻譯不了

  • 由其原來實現的 updateExpression 翻譯后的某些參數的 SQL 類型不對

  • 我需要一個 INSERT INTO SELECT FROM 的句式,它不支持

  • 我需要一個 upsert 功能,但是原來的 BulkInsertOrUpdate 不能在原表基礎上操作

  • 遂研究?IQueryable.Provider.Execute<T>?是什么執行流程。

    語句生成過程

    我覺得在翻代碼的過程中,有這么一首歌比較符合我的心情:如果你愿意一層一層一層一層的撥開我的心,你會發現,你會訝異,你是我最壓抑最深處的秘密。

  • 調用?QueryCompiler.ExtractParameters,將其中的閉包捕捉變量參數化

  • 檢查是否已經緩存了這個查詢表達式,如果沒有則轉入?QueryCompilationContext?處理,否則轉到8

  • QueryTranslationPreprocessor?處理,在原來的表達式樹上先跳舞

  • QueryableMethodTranslatingExpressionVisitor?將原來的表達式樹翻譯成一個?ShapedQueryExpression,而這一個表達式則包含了幾個部分:SelectExpression、ShaperExpression?和?ResultCardinality。其中前者是可以翻譯成 SQL 語句的表達式,中間的是將查詢出來的元組映射到實體類型,最后一個是查詢的維度(Enumerable、Single、SingleOrDefault)

  • QueryTranslationPostprocessor?處理,其中比較重要的是將查詢的字段加入 SELECT 的 Projection 列表

  • ShapedQueryCompilingExpressionVisitor?將?ShapedQueryExpression?緩存,并轉換成為?IRelationcalCommandCache,然后構造一個?QueryableEnumerable?的 NewExpression。前者包含了該查詢語句需要的參數、查詢語法樹、查詢字符串,后者是進行語句執行的類

  • 將上述 NewExpression 和將?QueryCompilationContext?中的查詢參數加到?QueryContext?中的語句合并成為一個代碼塊,然后 Lambda Compile

  • 生成?DbCommand?由?IRelationcalCommandCache?獲取字符串并加入各種參數進行查詢

  • 翻譯結束了,查詢到這里也就可以開始了。

    支持批量操作?

    IRelationalCommandCache?是怎么生成字符串的呢?沒錯,就是?QuerySqlGenerator?啦。

    那么,也就是說,我們能過拿到 Select Expression 的話,一切都好說。

    上述過程中,最后的?IRelationalCommandCache?中會包含這個?SelectExpression。我們可以魔改這個啊!

    DELETE?語句的生成比較簡單。我們構建一個?DeleteExpression?類,將要刪除的 Table、刪除中的 Predicate、刪除個數限制 Limit、原來的一些 Join 全部獲取出來,就好了。然后在我們自己繼承的?SqlServerQuerySqlGenerator?中實現這個部分。

    INSERT INTO SELECT?也比較簡單,只要構建一個?InsertIntoSelectExpression?類,將要插入的表 Table 和 SelectExpression 保存起來,就好了。

    UPDATE SET?可能比較麻煩。但是我們可以騷操作啊!將那個 updateExpression 變成 Select 的字段,然后再讀取 SelectExpression 中的 ProjectionExpression 不就好了嗎~我真是個小天才。

    MERGE INTO?是最煩的,因為結構過于復雜,涉及到 Target、Source、JoinPredicate、Limit、Matched、NotMatchedByTarget、NotMatchedBySource。過程中還要實現一些表的更名之類的。目前我只是實現了這些,但是想做出?Matched When?功能以后再發布到 nuget 上,這個實現實在是過于復雜,不知道有沒有人幫幫我啊 TAT。

    由于翻譯?SqlExpression?最方便還是基于?QuerySqlGenerator?操作,所以就寫一個?EnhancedQuerySqlGenerator?類來滿足我們的需求,并在 DbContextOptionsBuilder 那邊將這個 Factory 替換掉。

    實現了這些,GitHub 地址:Microsoft.EntityFrameworkCore.Bulk,可以在 github packages 上下載目前版本的 nuget 包。

    另外?src/Internal/TranslationGoThrough.cs?中有上述語句生成過程的一個縮影,和系統版本幾乎一致,唯一不同的是修改了?ExtractParameters?函數。

    因為原來的 Extract 過程有一個事情很詭異:在生成參數的時候,我們可以進行一些本地執行,但是如果不阻止某些本地執行程的話,可能會導致 UPDATE 語句的字段全部空。例如 updateExpression 中沒有利用到原表的參數并且不捕捉閉包變量的時候,那么不會被本地執行,但是如果沒有利用到原表的參數還捕捉閉包變量的時候,它就會被直接本地執行,字段空啦~(確實不懂他們這段代碼邏輯怎么寫的,你生成查詢的時候優化這個的話,怎么不把前面一個也優化掉啊……

    原文地址:https://www.90yang.com/efcore-query-sql-generation/



    總結

    以上是生活随笔為你收集整理的EFCore查询语句生成流程、让EFCore支持批量Update/Delete/MergeInto的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。