.NET:使用 LinqSharp 简化复杂查询
LinqSharp?是個開源?LINQ?擴展庫,它允許您編寫簡單代碼來生成復雜查詢,包括查詢擴展和動態(tài)查詢生成。
LinqSharp.EFCore?是對?EntityFramework?的增強庫,提供更多數(shù)據(jù)注解、數(shù)據(jù)庫函數(shù)及自定義儲存規(guī)則等。
https://github.com/zmjack/LinqSharp
由于內容較多,將分篇介紹公開內容、原理及案例分享:
LinqSharp:簡化復雜查詢
LinqSharp:動態(tài)構建 LINQ 查詢
LinqSharp.EFCore:表設計數(shù)據(jù)注解
LinqSharp.EFCore:字段標準化數(shù)據(jù)注解
LinqSharp.EFCore:函數(shù)映射
LinqSharp.EFCore:列式存儲代理
LinqSharp.EFCore:關聯(lián)計算與審計
本文多數(shù)示例,提供在線運行測試(.NET Fiddle)。
LinqSharp?為?LINQ?提供了以下增強(“內存查詢”或“SQL生成”):
默認返回方法
MinOrDefault:提供默認返回的?Min?方法;
MaxOrDefault:提供默認返回的?Max?方法;
AverageOrDefault:提供默認返回的?Average?方法。查詢值最小或最大的記錄
WhereMin:查詢指定字段最小的記錄;
WhereMax:查詢指定字段最大的記錄。數(shù)據(jù)搜索
Search:在指定字段或鏈接表字段中模糊或精確查詢數(shù)據(jù);分頁查詢
SelectPage:查詢結果分頁或執(zhí)行分頁查詢。序列排序
OrderByCase / ThenByCase:按指定字符串序列排序。構建動態(tài)查詢
XWhere:構建動態(tài)查詢。
以下方法僅提供于內存查詢:
按組元素數(shù)量分組
GroupByCount:按分組記錄元素數(shù)量分組。樹結構查詢
SelectMore:按樹結構遍歷,選擇“所有樹節(jié)點中滿足條件的節(jié)點”;
SelectUntil:按樹結構遍歷,直到在每個子路徑中找到滿足條件的節(jié)點,選擇該節(jié)點;
SelectWhile:按樹結構遍歷,選擇“所有子路徑中連續(xù)滿足條件的路徑節(jié)點”。
示例庫 Northwnd
Northwnd?是?SQL Server?早期附帶的示例數(shù)據(jù)庫,該數(shù)據(jù)庫描述“公司銷售產(chǎn)品網(wǎng)”簡單案例場景。包括“雇員(Employees)”“產(chǎn)品訂單(Orders)”“供應商(Suppliers)”的關系網(wǎng)絡。
本文示例使用的是它的?Sqlite?版本(Code First):
https://github.com/zmjack/Northwnd
通過?NuGet?安裝:
dotnet add package Northwnd dotnet add package LinqSharp dotnet 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?一致的發(fā)行庫(例如,2.2.x,3.0.x,3.1.x)。
默認返回方法擴展
MinOrDefault:原函數(shù)?Min?的不拋異常版本,異常返回默認值;
MaxOrDefault:原函數(shù)?Max?的不拋異常版本,異常返回默認值;
AverageOrDefault:原函數(shù)?Average?的不拋異常版本,異常返回默認值。
(在線示例:MinOrDefault | C# Online Compiler)
// throw 'Sequence contains no elements' new int[0].Min();new int[0].MinOrDefault(); // 0 new 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';運行結果:
數(shù)據(jù)搜索
Search:返回“從指定字段或外鍵表字段中進行模糊或精確查詢”的查詢結果。
Search?函數(shù)提供了四種搜索模式(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?函數(shù)同樣提供了外鏈表的查詢(主表或從表查詢)。
例如,查詢供應商(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 1FROM "Products" AS "p0"WHERE ("s"."SupplierID" = "p0"."SupplierID") AND (('Tofu' = '') OR (instr("p0"."ProductName", 'Tofu') > 0))) ORDER BY "s"."SupplierID", "p"."ProductID";運行結果:
分頁查詢
SelectPage:查詢結果分頁或執(zhí)行分頁查詢。(分頁參數(shù)從第?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:按指定字符串序列排序。
例如,查詢地區(qū)(Regions)表,將結果按?N / E / W / S?的地區(qū)序列排序返回:
(在線示例: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, });執(zhí)行 SQL:
SELECT * FROM "Regions" AS "r" ORDER BY CASEWHEN "r"."RegionDescription" = 'Northern' THEN 0ELSE CASEWHEN "r"."RegionDescription" = 'Eastern' THEN 1ELSE CASEWHEN "r"."RegionDescription" = 'Western' THEN 2ELSE CASEWHEN "r"."RegionDescription" = 'Southern' THEN 3ELSE 4ENDENDEND END;運行結果:
按組元素數(shù)量分組
數(shù)量分組函數(shù)?GroupByCount?用于根據(jù)指定每組記錄數(shù)量(每組最多允許?n?條記錄)進行特殊分組。
例如,將如下指定字符串按每行?16?個字符分成多行:
var s = "0123456789ABCDEF0123456789ABCDEF".GroupByCount(16).Select(g => new string(g.ToArray())); 0123456789ABCDEF0123456789ABCDEF
樹結構查詢
SelectMore:按樹結構遍歷,選擇“樹節(jié)點中?所有?滿足條件的?節(jié)點”;
SelectUntil:按樹結構遍歷,直到?在每個子路徑中找到滿足條件的節(jié)點,選擇?該節(jié)點;
SelectWhile:按樹結構遍歷,選擇“所有子路徑?中連續(xù)滿足條件的?路徑節(jié)點”。
例如,雇員(Employees)表按照?EmployeeID?和?ReportsTo?定義結構如下:
SelectMore
按樹結構遍歷,選擇“樹節(jié)點中?所有?滿足條件的?節(jié)點”。
例如,查詢由?2?號雇員?Andrew?領導的所有成員(2, 1, 3, 4, 5, 6, 7, 9, 8):
方法:使用?SelectMore?從根節(jié)點查找即可。
(在線示例: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
按樹結構遍歷,直到?在每個子路徑中找到滿足條件的節(jié)點,選擇?該節(jié)點。
例如,查詢由?2?號雇員?Andrew?領導的所有基層員工(葉節(jié)點,1, 3, 6, 7, 9, 8):
方法:使用?SelectUntil?從根節(jié)點查找,直到節(jié)點?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
按樹結構遍歷,選擇“所有子路徑?中連續(xù)滿足條件的?路徑節(jié)點”。
例如,查詢由?2?號雇員?Andrew?領導的所有非基層員工(非葉節(jié)點,2, 5):
方法:使用?SelectWhile?從根節(jié)點查找路徑,直到節(jié)點?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)), });運行結果:
下篇文章將介紹如何使用?LinqSharp?“動態(tài)生成查詢”,敬請關注。
總結
以上是生活随笔為你收集整理的.NET:使用 LinqSharp 简化复杂查询的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何使用 HttpReports 监控
- 下一篇: 理解C#泛型运作原理