首頁>技術>

LinqSharp 是個開源 LINQ 擴充套件庫,它允許您編寫簡單程式碼來生成複雜查詢,包括查詢擴充套件和動態查詢生成。

LinqSharp.EFCore 是對 EntityFramework 的增強庫,提供更多資料註解、資料庫函式及自定義儲存規則等。

https://github.com/zmjack/LinqSharp

由於內容較多,將分篇介紹公開內容、原理及案例分享:

LinqSharp:簡化複雜查詢LinqSharp:動態構建 LINQ 查詢LinqSharp.EFCore:表設計資料註解LinqSharp.EFCore:欄位標準化資料註解LinqSharp.EFCore:函式對映LinqSharp.EFCore:列式儲存代理LinqSharp.EFCore:關聯計算與審計

本文多數示例,提供線上執行測試(.NET Fiddle)。

LinqSharp 為 LINQ 提供了以下增強(“記憶體查詢”或“SQL生成”):

預設返回方法MinOrDefault:提供預設返回的 Min 方法;MaxOrDefault:提供預設返回的 Max 方法;AverageOrDefault:提供預設返回的 Average 方法。查詢值最小或最大的記錄WhereMin:查詢指定欄位最小的記錄;WhereMax:查詢指定欄位最大的記錄。資料搜尋Search:在指定欄位或連結表字段中模糊或精確查詢資料;分頁查詢SelectPage:查詢結果分頁或執行分頁查詢。序列排序OrderByCase / ThenByCase:按指定字串序列排序。構建動態查詢XWhere:構建動態查詢。

以下方法僅提供於記憶體查詢:

按組元素數量分組GroupByCount:按分組記錄元素數量分組。樹結構查詢SelectMore:按樹結構遍歷,選擇“所有樹節點中滿足條件的節點”;SelectUntil:按樹結構遍歷,直到在每個子路徑中找到滿足條件的節點,選擇該節點;SelectWhile:按樹結構遍歷,選擇“所有子路徑中連續滿足條件的路徑節點”。

示例庫 Northwnd

Northwnd 是 SQL Server 早期附帶的示例資料庫,該資料庫描述“公司銷售產品網”簡單案例場景。包括“僱員(Employees)”“產品訂單(Orders)”“供應商(Suppliers)”的關係網路。

本文示例使用的是它的 Sqlite 版本(Code First):

https://github.com/zmjack/Northwnd

透過 NuGet 安裝:

dotnet add package Northwnddotnet add package LinqSharpdotnet add package LinqSharp.EFCore

簡單使用:

using (var sqlite = NorthwndContext.UseSqliteResource()){    ...}

輸出 SQL

“輸出 SQL”是研究“SQL 生成”的基礎,使用 LinqSharp.EFCore 中的 ToSql 方法:

(線上示例:ToSql | C# Online Compiler)

using (var sqlite = NorthwndContext.UseSqliteResource()){    var query = sqlite.Regions        .Where(x => x.RegionDescription == "Northern");    var sql = query.ToSql();}

生成 SQL:

SELECT "r"."RegionID", "r"."RegionDescription"FROM "Regions" AS "r"WHERE "r"."RegionDescription" = 'Northern';

注1:由於不同版本的 EntityFrameworkCore 的 SQL 生成器設計不同,因此,生成 SQL 可能會存在差異。(EntityFrameworkCore 5.0 公開了 ToQueryString 來支援這項功能)。

注2:LinqSharp.EFCore 最新版本不相容所有 EntityFrameworkCore,需使用“大版本號”與 EntityFrameworkCore 一致的發行庫(例如,2.2.x,3.0.x,3.1.x)。

預設返回方法擴充套件

MinOrDefault:原函式 Min 的不拋異常版本,異常返回預設值;MaxOrDefault:原函式 Max 的不拋異常版本,異常返回預設值;AverageOrDefault:原函式 Average 的不拋異常版本,異常返回預設值。

(線上示例:MinOrDefault | C# Online Compiler)

// throw 'Sequence contains no elements'new int[0].Min();new int[0].MinOrDefault();      // 0new int[0].MinOrDefault(-1);    // -1

查詢值最小或最大的記錄

WhereMin:查詢指定欄位最小的記錄;WhereMax:查詢指定欄位最大的記錄。

WhereMin 和 WhereMax 會進行兩次查詢:

查詢指定欄位的“最小值”或“最大值”;查詢指定欄位“最小值”或“最大值”的記錄。

例如,查詢員工(Empolyees)表中年齡最小的員工:

(線上示例:WhereMax | C# Online Compiler)

var query = sqlite.Employees    .WhereMax(x => x.BirthDate);var result = query.Select(x => new{    x.EmployeeID,    x.FirstName,    x.BirthDate,}).ToArray();

生成 SQL:

/* Step 1 */SELECT MIN("e"."BirthDate")FROM "Employees" AS "e";/* Step 2 */SELECT *FROM "Employees" AS "e"WHERE "e"."BirthDate" = '1966-01-27 00:00:00';

執行結果:

資料搜尋

Search:返回“從指定欄位或外來鍵表字段中進行模糊或精確查詢”的查詢結果。

Search 函式提供了四種搜尋模式(SearchOption):

Contains(預設):任何指定欄位中“包含”搜尋字串;NotContains:所有指定欄位中都“不包含”搜尋字串;Equals:搜尋字串與某指定欄位“相等”;NotEquals:搜尋字串“不在”任何指定欄位中。

例如,查詢僱員(Employees)表中地址(Address)或城市(City)包含字母 m 的僱員:

(線上示例:Search | C# Online Compiler)

var query = sqlite.Employees    .Search("m", e => new    {        e.Address,        e.City,    });var result = query.Select(x => new{    x.EmployeeID,    x.Address,    x.City,}).ToArray();

生成 SQL:

SELECT *FROM "Employees" AS "e"WHERE (('m' = '') OR (instr("e"."Address", 'm') > 0))    OR (('m' = '') OR (instr("e"."City", 'm') > 0));

執行結果:

Search 函式同樣提供了外連結串列的查詢(主表或從表查詢)。

例如,查詢供應商(Suppliers)表中供應任何種類豆腐(Tofu)的供應商:

(線上示例:Search (Details) | C# Online Compiler)

var query = sqlite.Suppliers    .Include(x => x.Products)    .Search("Tofu", s => new    {        ProductNames = s.Products.Select(x => x.ProductName),    });var result = query.Select(x => new{    x.SupplierID,    x.CompanyName,    Products = string.Join(", ", x.Products.Select(p => p.ProductName)),}).ToArray();

生成 SQL:

SELECT *FROM "Suppliers" AS "s"LEFT JOIN "Products" AS "p" ON "s"."SupplierID" = "p"."SupplierID"WHERE EXISTS (    SELECT 1    FROM "Products" AS "p0"    WHERE ("s"."SupplierID" = "p0"."SupplierID")       AND (('Tofu' = '') OR (instr("p0"."ProductName", 'Tofu') > 0)))ORDER BY "s"."SupplierID", "p"."ProductID";

執行結果:

分頁查詢

SelectPage:查詢結果分頁或執行分頁查詢。(分頁引數從第 1 頁開始)

例如,查詢僱員(Employees)表,按每頁 2 條記錄分頁,選擇第 3 頁的記錄返回:

(線上示例:SelectPage | C# Online Compiler)

var query = sqlite.Employees    .SelectPage(pageNumber: 3, pageSize: 2);var result = query.Select(x => new{    x.EmployeeID,    x.Address,    x.City,}).ToArray();

生成 SQL:

SELECT *FROM "Employees" AS "e"ORDER BY (SELECT 1)LIMIT 2 OFFSET 4;

執行結果:

序列排序

OrderByCase / ThenByCase:按指定字串序列排序。

例如,查詢地區(Regions)表,將結果按 N / E / W / S 的地區序列排序返回:

(線上示例:OrderByCase | C# Online Compiler)

var query = sqlite.Regions    .OrderByCase(x => x.RegionDescription, new[]    {        "Northern",        "Eastern",        "Western",        "Southern",    });var result = query.Select(x => new{    x.RegionID,    x.RegionDescription,});

執行 SQL:

SELECT *FROM "Regions" AS "r"ORDER BY CASE    WHEN "r"."RegionDescription" = 'Northern' THEN 0    ELSE CASE        WHEN "r"."RegionDescription" = 'Eastern' THEN 1        ELSE CASE            WHEN "r"."RegionDescription" = 'Western' THEN 2            ELSE CASE                WHEN "r"."RegionDescription" = 'Southern' THEN 3                ELSE 4            END        END    ENDEND;

執行結果:

按組元素數量分組

數量分組函式 GroupByCount 用於根據指定每組記錄數量(每組最多允許 n 條記錄)進行特殊分組。

例如,將如下指定字串按每行 16 個字元分成多行:

var s = "0123456789ABCDEF0123456789ABCDEF"    .GroupByCount(16)    .Select(g => new string(g.ToArray()));

0123456789ABCDEF0123456789ABCDEF

樹結構查詢

SelectMore:按樹結構遍歷,選擇“樹節點中 所有 滿足條件的 節點”;SelectUntil:按樹結構遍歷,直到 在每個子路徑中找到滿足條件的節點,選擇 該節點;SelectWhile:按樹結構遍歷,選擇“所有子路徑 中連續滿足條件的 路徑節點”。

例如,僱員(Employees)表按照 EmployeeID 和 ReportsTo 定義結構如下:

SelectMore

按樹結構遍歷,選擇“樹節點中 所有 滿足條件的 節點”。

例如,查詢由 2 號僱員 Andrew 領導的所有成員(2, 1, 3, 4, 5, 6, 7, 9, 8):

方法:使用 SelectMore 從根節點查詢即可。

(線上示例:SelectMore | C# Online Compiler)

var employees = sqlite.Employees    .Include(x => x.Superordinate)    .Include(x => x.Subordinates)    .ToArray();var query = employees    .Where(x => x.EmployeeID == 2)    .SelectMore(x => x.Subordinates);var result = query.Select(x => new{    x.EmployeeID,    x.FirstName,    x.ReportsTo,    ReportsTo_ = x.Superordinate?.FirstName,});

執行結果:

SelectUntil

按樹結構遍歷,直到 在每個子路徑中找到滿足條件的節點,選擇 該節點。

例如,查詢由 2 號僱員 Andrew 領導的所有基層員工(葉節點,1, 3, 6, 7, 9, 8):

方法:使用 SelectUntil 從根節點查詢,直到節點 Subordinates 為空。

(線上示例:SelectUntil | C# Online Compiler)

var employees = sqlite.Employees    .Include(x => x.Superordinate)    .Include(x => x.Subordinates)    .ToArray();var query = employees    .Where(x => x.EmployeeID == 2)    .SelectUntil(x => x.Subordinates, x => !x.Subordinates.Any());var result = query.Select(x => new {    x.EmployeeID,    x.FirstName,    x.ReportsTo,    ReportsTo_ = x.Superordinate?.FirstName,});

執行結果:

SelectWhile

按樹結構遍歷,選擇“所有子路徑 中連續滿足條件的 路徑節點”。

例如,查詢由 2 號僱員 Andrew 領導的所有非基層員工(非葉節點,2, 5):

方法:使用 SelectWhile 從根節點查詢路徑,直到節點 Subordinates 為空。

(線上示例:SelectWhile | C# Online Compiler)

var employees = sqlite.Employees    .Include(x => x.Superordinate)    .Include(x => x.Subordinates)    .ToArray();var query = employees    .Where(x => x.EmployeeID == 2)    .SelectWhile(x => x.Subordinates, x => x.Subordinates.Any());var result = query.Select(x => new {    x.EmployeeID,    x.FirstName,    Subordinates = string.Join(", ", x.Subordinates        .SelectMore(s => s.Subordinates)        .Select(s => s.FirstName)),});

執行結果:

原文:https://mp.weixinbridge.com/mp/wapredirect?url=https%3A%2F%2Fgithub.com%2Fzmjack%2FLinqSharp%2Fblob%2Fmaster%2FREADME-CN.md

13
最新評論
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • DocFetcher - 開源的檔案內容搜尋工具