SQLsugar自(zì)連接導航查詢中select使用(yòng★↓)子(zǐ)查詢沒有(yǒu)數(shù)據

在 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)失效:

  1. 導航查詢(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)。
  2. 子(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ú) u1u2),需通(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)。

發表回複

您的(de)郵箱地(dì)址不(bù)會(huì)被公開(kāi)。 必填項已用(yòng) * 标注