在 C# 開發中,Linq(Language Integrated Query)提供了強大的數據查詢能力,尤其是在處理集合間的關聯操作時。本文將詳細解析 C# Linq 中的左關聯查詢,并通過實際案例說明其用法。
左關聯查詢基礎
左關聯(Left Join)是 SQL 中的一種關聯查詢方式,它會返回左表中的所有記錄,無論右表中是否有匹配記錄。在 Linq 中實現左關聯需要使用GroupJoin
和DefaultIfEmpty
方法組合,或者使用查詢語法中的into
和DefaultIfEmpty
關鍵字。
基本語法結構
csharp
var query = from left in leftTablejoin right in rightTableon left.Key equals right.Key into tempGroupfrom temp in tempGroup.DefaultIfEmpty()select new {LeftProp = left.Property,RightProp = temp?.Property};
這里的關鍵點是:
- 使用
into
關鍵字將右表分組到臨時集合 - 使用
DefaultIfEmpty()
處理可能為空的情況 - 在結果選擇器中使用空條件運算符
?.
處理可能的 null 值
實際案例分析
讓我們通過一個實際案例來理解左關聯的應用場景。假設我們有一個企業資產管理系統,需要查詢特定日期范圍內有資產檢查記錄的企業,并標記哪些企業有記錄。
以下是完整的示例代碼:
csharp
using System;
using System.Collections.Generic;
using System.Linq;namespace LinqLeftJoinDemo
{// 企業信息類public class Enterprise{public string Code { get; set; }public string Name { get; set; }public string Industry { get; set; }}// 資產檢查記錄類public class AssetCheckRecord{public string EnterpriseID { get; set; }public DateTime StartDate { get; set; }public DateTime EndDate { get; set; }public string MonthEndType { get; set; }public decimal CheckScore { get; set; }}// 資產類型枚舉public enum PigAssets{FixedAssets,CurrentAssets,IntangibleAssets}class Program{static void Main(string[] args){// 模擬企業數據var enterprises = new List<Enterprise>{new Enterprise { Code = "E001", Name = "ABC公司", Industry = "制造業" },new Enterprise { Code = "E002", Name = "XYZ公司", Industry = "零售業" },new Enterprise { Code = "E003", Name = "123公司", Industry = "服務業" },new Enterprise { Code = "E004", Name = "TEST公司", Industry = "科技業" }};// 模擬資產檢查記錄數據var checkRecords = new List<AssetCheckRecord>{new AssetCheckRecord{EnterpriseID = "E001",StartDate = new DateTime(2023, 10, 1),EndDate = new DateTime(2023, 10, 31),MonthEndType = PigAssets.FixedAssets.ToString(),CheckScore = 95.5m},new AssetCheckRecord{EnterpriseID = "E003",StartDate = new DateTime(2023, 10, 1),EndDate = new DateTime(2023, 10, 31),MonthEndType = PigAssets.FixedAssets.ToString(),CheckScore = 88.0m}};// 查詢條件DateTime startDate = new DateTime(2023, 10, 1);DateTime endDate = new DateTime(2023, 10, 31);string assetType = PigAssets.FixedAssets.ToString();// 第一步:篩選符合條件的檢查記錄var filteredRecords = checkRecords.Where(r => r.StartDate.Date == startDate.Date&& r.EndDate.Date == endDate.Date&& r.MonthEndType.Equals(assetType)).ToList();// 第二步:執行左關聯查詢var result = from e in enterprisesjoin r in filteredRecordson e.Code equals r.EnterpriseID into enterpriseGroupfrom g in enterpriseGroup.DefaultIfEmpty()select new{EnterpriseID = e.Code,Name = e.Name,HasCheckRecord = g != null,CheckScore = g?.CheckScore ?? 0};// 輸出結果Console.WriteLine("企業資產檢查記錄查詢結果:");Console.WriteLine("企業代碼\t企業名稱\t是否有檢查記錄\t檢查得分");foreach (var item in result){Console.WriteLine($"{item.EnterpriseID}\t{item.Name}\t{item.HasCheckRecord}\t\t{item.CheckScore}");}}}
}
代碼解析
上述代碼實現了一個典型的左關聯查詢場景:
數據準備:創建了兩個實體類
Enterprise
和AssetCheckRecord
,并初始化了示例數據。條件篩選:首先篩選出特定日期范圍內且資產類型匹配的檢查記錄:
csharp
var filteredRecords = checkRecords.Where(r => r.StartDate.Date == startDate.Date&& r.EndDate.Date == endDate.Date&& r.MonthEndType.Equals(assetType)).ToList();
左關聯查詢:使用查詢語法實現左關聯,確保返回所有企業信息,并標記是否有檢查記錄:
csharp
var result = from e in enterprisesjoin r in filteredRecordson e.Code equals r.EnterpriseID into enterpriseGroupfrom g in enterpriseGroup.DefaultIfEmpty()select new { ... };
結果處理:通過
DefaultIfEmpty()
方法處理可能的空值情況,并使用空合并運算符??
提供默認值。
執行結果分析
運行上述代碼,輸出結果如下:
plaintext
企業資產檢查記錄查詢結果:
企業代碼 企業名稱 是否有檢查記錄 檢查得分
E001 ABC公司 True 95.5
E002 XYZ公司 False 0
E003 123公司 True 88
E004 TEST公司 False 0
可以看到,即使某些企業沒有對應的檢查記錄(如 E002 和 E004),它們仍然會出現在結果集中,并且HasCheckRecord
字段被標記為False
,檢查得分為 0。
方法語法實現左關聯
除了查詢語法,Linq 還提供了方法語法來實現左關聯,以下是等效的方法語法實現:
csharp
var result = enterprises.GroupJoin(filteredRecords,e => e.Code,r => r.EnterpriseID,(e, group) => new { Enterprise = e, Records = group }).SelectMany(temp => temp.Records.DefaultIfEmpty(),(temp, r) => new{EnterpriseID = temp.Enterprise.Code,Name = temp.Enterprise.Name,HasCheckRecord = r != null,CheckScore = r?.CheckScore ?? 0});
這種方法語法雖然更加函數式,但理解起來可能稍復雜一些。