在 SqlSugar 中,導航查詢(Include
)與子(zǐ)查詢同時(shí)使用(yòng)時(shí)出現(xi✔€™↓àn)無數(shù)據的(de)情況,通(tōng)常與 查詢别名沖突 或 導航查詢結構被導航加載幹擾 有(yǒu)關。以下(xià)通(tōngβ$λ)過具體(tǐ)測試案例分(fēn)析原因并提供解決方案。
測試場(chǎng)景複現(xiàn)
假設我們有(yǒu)兩個(gè)實體(tǐ)類,模拟項目(Project
)和(hé)系統文(wén)件(jiàn)(SysFile
)的(de)關系,其中 Project
包含自(zì)引用(yòng)的(de)子(zǐ↓✘↓&)級導航屬性 children
:
csharp
// 項目實體(tǐ) public class Projec♥εt { public int Id { get; set; }¥∞ ¥ public string title { get; •¥λset; } public string hetong { get; se≥ ↔t; } // 關聯合同文(wén)件(jiàn)URL,關聯SysFile.Urσ§₽&l public int? pid { get; set; } // 父級λ£項目ID(自(zì)引用(yòng)) >π // 導航屬性:子(zǐ)級項目(自(zì)引用(yòn>≤g)) [Navigate(NavigateType.On÷λγeToMany, nameof(pid))] public List&®♦lt;Project> children { get <; set; } } // 系統文(wén)件(jiàn)實體(tǐ) pub§→lic class SysFile { public int Id { get; set;αλ♦∞ } public string FileN¶ame { get; set; } public string Url { g→"≠αet; set; } // 與Project.hetong關聯 } ≠> // 輸出DTO public class ProjectOutpuα≤λ©t { public int Id { get; set; } ™♦÷β public string title { get; set; } π public List<elfile> hetongAttach★λ↔ment { get; set; } // 子(zǐ)查詢結果 ₹™ public List<Project> children { π≥get; set; } // 導航屬性 } public class elfileφ® { public string name { •♦get; set; } public string url { get; set; }<♠ }
問(wèn)題查詢代碼(子(zǐ)查詢無數(shù)據)
csharp
// 有(yǒu)問(wèn)題的(de)查詢:同時(shíφ)使用(yòng)Include(children)和(hé)子(zǐ)查詢 var quer↓∑δy = db.Queryable<Project>() §↔∞§ .Includes(u => u.children)&✔ // 加載子(zǐ)級導航屬性 .Select(u => ne®'→w ProjectOutput { Id = u.Idλδβ, title = u.title, ¥☆ // 子(zǐ)查詢:通(tōng)過hetong關聯SysFile≥↓✘ hetongAttachment = SqlFunc.Subqueryaφ÷ble<SysFile>() .Where(f<€¥ => f.Url == u.hetong) .Select(f =¥✔π¶> new elfile { name = f.FileName, url γ•= f.Url }) .ToList(ε≈), children = u.ch≠πildren // 導航屬性 }); var result = await ↕↓query.ToListAsync(); // 現(xiàn)象:hetongAttachmen ∏βt始終為(wèi)空(kōng)(即使有(yǒu)匹配數(shù)據)
問(wèn)題原因分(fēn)析
通(tōng)過輸出 SQL 日(rì)志(zhì)(配置方式見(jiàn)下(x σià)文(wén)),發現(xiàn) Include(children)
會(huì)改變主表的(de)别名,導緻子(zǐ)查詢中的(de)條件(jiàn)失效:
- 導航查詢(Include)的(de) SQL 生(shēng☆γ)成邏輯:
當使用(yòng)Includes(u => u.children)
時(shí),SqlSugar 會(huì)自(zì)動生(shēng)成±₹自(zì)連接查詢,主表Project
會(huì)被賦予别名(如(rú)u1
),而子(zǐ)級children
會(huì)被賦予另一(yī)個(gè)别名(如(rú)u2
)。 - 子(zǐ)查詢中的(de)别名沖突:
子(zǐ)查詢中使用(yòng)u.hetong
時(shí),SqlSugar 會(huì)默認引用(yòng)主表原始别名♦ε♣∏(如(rú)u
),但(dàn)實際主表已被重命名為(wèi)u1
,導緻條件(jiàn)f.Url == u.hetong
實際被解析為(wèi)f.Url == u.hetong
(而非u1.hetong
),條件(jiàn)不(bù)匹配,子(zǐ)查詢無結果。
解決方案
方案 1:拆分(fēn)查詢(推薦)
将 “導航屬性加載” 和(hé) “子(zǐ)查詢” 拆分(fēn)為(wè↓₹i)兩步,避免别名沖突:
csharp
// 第一(yī)步:查詢主數(shù)據+子(zǐ)查詢(不(bù)加載c♣✔λ←hildren) var query = db.Queryable&÷λlt;Project>() .Select(u => new Pr♦∞δεojectOutput { Id = u.Id,∏™ € title = u.title, hetongAttachγ§≈∞ment = SqlFunc.Subqueryable<★;SysFile>() .Where(±φα>f => f.Url == u.hetong) ™φ .Select(f => new elfile { nσ"×ame = f.FileName, url = f.Url }) γ↑δ .ToList() }); var result = await↕•✔ query.ToListAsync(); // 第二↑®步:單獨加載children導航屬性 if (result.Any()) { va✔r parentIds = result.Select(r => §®r.Id).ToList(); // 查詢所有(yǒu)子(zǐ)級項目 va₽←r children = await db.Queryable<Proj₽Ω¥>ect>() .Where(c => parentIds♣★±∞.Contains(c.pid ?? 0)) σ¶ .ToListAsync(); // 手動關聯子(zǐ)級 fore☆↓☆ach (var item in result) { ↓₩β item.children = children.Whβ↓ ere(c => c.pid == item.Id)≥.ToList(); } }
方案 2:強制(zhì)子(zǐ)查詢引用(yòng)主表實際别名
通(tōng)過 SQL 日(rì)志(zhì)獲取主♠§✔表在 Include
後的(de)實際别名(如(rú) u1
),使用(yòng) SqlFunc.GetSelfColumn
強制(zhì)子(zǐ)查詢引用(yòng)正确别名:
csharp
var query = db.Queryable<Project&g♣↑↔t;() .Includes(u => u.children) ↕↑♣ .Select(u => new ProjectOutput λ€ { Id = u.Id, ✘ title = u.title, // 關鍵:使用(yòng)∞÷α主表實際别名(從(cóng)SQL日(rì)志(zhì)獲取,如(rú)u1) ™÷♠ hetongAttachment = SqlFunc.Subque$∞ryable<SysFile>() .Where(fγ => f.Url == SqlFunc.GetSelfColumn(&→αφ₽quot;u1.hetong")) .←↕ΩSelect(f => new elfile { name = → ©♣f.FileName, url = f.Url }) &Ω .ToList(), children = u.childδ§ren });
注意:主表别名可(kě)能(néng)随查詢複雜(zá)度變化(huà)(如(rú) u1
、u2
),需通(tōng)過日(rì)志(zhì)确認後調整,不(bù)推薦在生(shēng)産± ∏環境硬編碼。
方案 3:禁用(yòng)導航查詢的(de)自(zì)動别名(謹慎使用(yòng))
通(tōng)過配置 Aop
禁用(yòng)導航查詢的(de)别名生(shēng)成(可(kě)能(né' ng)影(yǐng)響其他(tā)邏輯,需測試):
csharp
// 初始化(huà)SqlSugar時(shí)配置 db. βAop.ConfigureJoinTable = (joinItems) => { ↑©≥© // 強制(zhì)主表别名固定為(wèi)"u&q≠← uot; foreach (var item in joinItems) ★πσδ{ if (item.IsMainTable) { <™↔× item.Alias = "u"≥; // 固定主表别名為(wèi)u } } };
關鍵排查工(gōng)具:輸出 SQL 日(rì)志(zhì)
配置 SqlSugar 日(rì)志(zhì),查看(kàn)生(sh₹✘ēng)成的(de) SQL 語句,确認主表别名和(hé)子(zǐ)查詢條件(jiàn):
csharp
// 初始化(huà)SqlSugar時(shí)添加日(rì)志(zhì)配置 db.↕≈Aop.OnLogExecuting = (sql, pars) => { C↓∏onsole.WriteLine("生(shēng)成的(de)S$QL:" + sql); Console.WriteLine("參數(₽φshù):" + string.Join("↕σ>,", pars.Select(p => $±∞&♣"{p.ParameterName}={p.Value}"))>Ω); };

對(duì)比 “不(bù)加 Include
” 和(hé) “加 Include
” 的(de) SQL,重點觀察:
- 主表的(de)别名是(shì)否變化(huà)(如(rú)
u
→u1
)。 - 子(zǐ)查詢中的(de)
u.hetong
是(shì)否正确關聯到(dào)主表别名。
總結
導航查詢(Include
)導緻子(zǐ)查詢無數(shù)據的(de)核心原因是(shì)&nb☆♠ sp;主表别名被改寫,導緻子(zǐ)查詢條件(jiàn)關聯失效。推薦采用(yòng) 拆分(fēn)查詢 的(de)方式,避免導航屬性與複雜(zá)子(zǐφ♦₩)查詢在同一(yī)語句中沖突。若必須同時(shí)使用(yòng),需通¥(tōng)過日(rì)志(zhì)确認主表别名并調整εε子(zǐ)查詢條件(jiàn)。