連接和Lookups
Enumerable當中的Join和GroupJoin使用兩個步驟來工作. 首先, 它們將inner序列加載到一個lookup當中, 然後再將一個outer序列和這個lookup組合在一起.
一個lookup是一個分組序列, 並且可以通過鍵直接訪問, 或者你可以認為它是一個序列字典, 每一個鍵可以接受多個元素. 並且lookup是只讀的, 定以如下:
1: public interface ILookup : 2: 3: IEnumerable>, IEnumerable 4: 5: { 6: 7: int Count { get; } 8: 9: bool Contains (TKey key); 10: 11: IEnumerable this [TKey key] { get; } 12: 13: }
當處理本地集合的時候, 我們可以使用joining操作符作為一個額外的可選策略來手工創建或者查詢lookups, 這允許你在多個查詢上面重用相同的lookup.
ToLookup擴展方法創建了一個lookup. 以下的例子將所有的purchases加載到一個lookup當中去, 它們的關鍵是CustomerID字段.
1: ILookup<int?,Purchase> purchLookup = 2: 3: purchases. ToLookup (p => p.CustomerID, p => p);
第一個參數選擇了鍵, 第二個參數選取那些作為值被加載到lookup的對象.
讀取一個lookup就像在讀取一個字典, 除非indexer返回了包含匹配項的序列, 而不是簡單的匹配. 以下的例子枚舉了ID是1的客戶采購訂單:
1: foreach (Purchase p in purchLookup [1]) 2: 3: Console.WriteLine (p.Description);
在適當的位置使用lookup, 我們可以讓SelectMany / Select查詢執行得跟Join / GroupJoin一樣有效率. 在一個lookup上面使用SelectMany與Join是等價的:
1: from c in customers 2: 3: from p in purchLookup [c.ID] 4: 5: select new { c.Name, p.Description, p.Price }; 6: 7: Tom Bike 500 8: 9: Tom Holiday 2000 10: 11: Dick Bike 600 12: 13: Dick Phone 300 14: ...
增加一個DefaultIfEmpty的調用可以讓此查詢變成一個Outer Join:
1: from c in customers 2: 3: from p in purchLookup [c.ID].DefaultIfEmpty() 4: 5: select new 6: 7: { 8: 9: c.Name, 10: 11: Descript = p == null ? null :p.Description, 12: 13: Price = p == null ? (decimal?) null :p.Price 14: 15: };
在projection內讀lookup與GroupJoin是等價的:
1: from c in customers 2: 3: select new { 4: 5: CustName = c.Name, 6: 7: CustPurchases = purchLookup [c.ID] 8: 9: };
待續!