join 子句可用於將來自不同源序列並且在對象模型中沒有直接關系的元素相關聯。 唯一的要求是每個源中的元素需要共享某個可以進行比較以判斷是否相等的值。 例如,食品經銷商可能擁有某種產品的供應商列表以及買主列表。 例如,可以使用 join 子句創建該產品同一指定地區供應商和買主的列表。
join 子句將 2 個源序列作為輸入。 每個序列中的元素都必須是可以與其他序列中的相應屬性進行比較的屬性,或者包含一個這樣的屬性。 join 子句使用特殊 equals 關鍵字比較指定的鍵是否相等。 join 子句執行的所有聯接都是同等聯接。 join 子句的輸出形式取決於執行的聯接的具體類型。 以下是 3 種最常見的聯接類型:
-
內部聯接
-
分組聯接
-
左外部聯接
內部聯接
以下示例演示了一個簡單的內部同等聯接。 此查詢生成一個“產品名稱/類別”對平面序列。 同一類別字符串將出現在多個元素中。 如果 categories 中的某個元素不具有匹配的 products,則該類別不會出現在結果中。
var innerJoinQuery = from category in categories join prod in products on category.ID equals prod.CategoryID select new { ProductName = prod.Name, Category = category.Name }; //produces flat sequence
Group Join
含有 into 表達式的 join 子句稱為分組聯接。
var innerGroupJoinQuery = from category in categories join prod in products on category.ID equals prod.CategoryID into prodGroup select new { CategoryName = category.Name, Products = prodGroup };
分組聯接會生成分層的結果序列,該序列將左側源序列中的元素與右側源序列中的一個或多個匹配元素相關聯。 分組聯接沒有等效的關系術語;它本質上是一個對象數組序列。
如果在右側源序列中找不到與左側源中的元素相匹配的元素,則 join 子句會為該項生成一個空數組。 因此,分組聯接基本上仍然是一種內部同等聯接,區別在於分組聯接將結果序列組織為多個組。
如果只選擇分組聯接的結果,則可訪問各項,但無法識別結果所匹配的項。 因此,通常更為有用的做法是:選擇分組聯接的結果並將其放入一個也包含該項名的新類型中,如上例所示。
當然,還可以將分組聯接的結果用作其他子查詢的生成器:
var innerGroupJoinQuery2 = from category in categories join prod in products on category.ID equals prod.CategoryID into prodGroup from prod2 in prodGroup where prod2.UnitPrice > 2.50M select prod2;
左外部聯接
在左外部聯接中,將返回左側源序列中的所有元素,即使右側序列中沒有其匹配元素也是如此。 若要在 LINQ 中執行左外部聯接,請結合使用 DefaultIfEmpty 方法與分組聯接,指定要在某個左側元素不具有匹配元素時生成的默認右側元素。 可以使用 null 作為任何引用類型的默認值,也可以指定用戶定義的默認類型。 以下示例演示了用戶定義的默認類型:
var leftOuterJoinQuery = from category in categories join prod in products on category.ID equals prod.CategoryID into prodGroup from item in prodGroup.DefaultIfEmpty(new Product { Name = String.Empty, CategoryID = 0 }) select new { CatName = category.Name, ProdName = item.Name };
組合鍵
可通過使用組合鍵測試多個值是否相等。 有關詳細信息,請參閱如何:使用組合鍵進行聯接。 還可以在 group 子句中使用組合鍵。
var query = from o in db.Orders from p in db.Products join d in db.OrderDetails on new {o.OrderID, p.ProductID} equals new {d.OrderID,d.ProductID} into details from d in details select new {o.OrderID, p.ProductID, d.UnitPrice};
if the Orders table and OrderDetails table each used different names for their columns, you could create composite keys by assigning identical names in the anonymous types:
join...on new {Name = o.CustomerName, ID = o.CustID} equals new {Name = d.CustName, ID = d.CustID }
