C# - LINQ to Entities Part II

還記得久遠之前限量的那篇 LINQ to Entities Part I 裡,隔了這麼久,限量才突然想到還有其他的未補完。之前 Part I 已經介紹過了 Select, SelectMany, Where, Distinct, GroupBy, OrderBy, OrderByDescending, Join。Part I 說的全部都是類 SQL 的查詢方法,接下來剩的當然還是有一些類 SQL的方法,中間還穿插了一些特殊用途的方法,那就讓我們繼續往下看下去。


下列是本篇要接下來分析的Extension 方法:
  1. Aggregate
  2. All
  3. Average
  4. Count
  5. Except
  6. First & FirstOrDefault
  7. Intersect
  8. Last & LastOrDefault
  9. Max
  10. Min
我們使用的資料和前篇一樣,是一個商品倉儲相關的資料表:

Good

Aggregate

Aggregate是可將集合進行彙總的擴充方法,你可以將彙總規則Func傳入,程式會逐筆執行Func,然後將每次的結果傳給下一輪。此外,在彙總之後還可以再使用另一個Func對彙總後的資料進行處理。以下為MSDN上的Aggregate語法(最複雜的):

public static TResult Aggregate<TSource, TAccumulate, TResult>(
this IEnumerable<TSource> source,
TAccumulate seed,
Func<TAccumulate, TSource, TAccumulate> func,
Func<TAccumulate, TResult> resultSelector
)

參數

  • seed:進行彙總的初始值。
  • func:彙總規則的Function,每一輪傳入的參數為 1. 目前彙總值; 2. 目前處理資料。
  • resultSelector:對最後彙總結果進行處理的Function,傳入的參數為最後彙總值。

Scenario

在所有Goods裡挑出價格大於8000台幣的資料,然後把價格轉為美金。
var expensives = 
    Goods.ToList().Aggregate(new List<Good>(), (gathers, good) => {
        if(good.Price > 8000) {
            gathers.Add(good);
        }
        return gathers;
    }, result => {
        result.ForEach(r => {
            r.Price = r.Price.Value / 30;
        });
        return result;
    });


Result







All

All是用來判斷是否集合中的資料都符合塞選的條件。

Scenario

判斷是否在Goods中是否所有的商品價格都大於1000元。
var isMoreThan1000 = Goods.All(x => x.Price > 1000);

SQL

SELECT COUNT(*) AS [value]
FROM [Good] AS t0
WHERE NOT (t0.[Price] > 1000)
因為SQL語法中是無法SELECT布林值出來,所以可以用SELECT COUNT的方式來看看是否有不符合的筆數資料,如果結果大於0代表有不符合的資料。

Result

False。從結果來看,因為所有資料中(7筆),有一個商品的價格只有50元,所以並不符合所有都大於1000的假設,故回傳結果為False。

Average

Average是用來計算集合中某的欄位的平均值。

Scenario

計算Goods中所有商品的價格平均。
var average = Goods.Average(x => x.Price);

SQL

SELECT AVG(t0.[Price])
FROM [Good] AS t0

Result

(9999 + 23000 + 19000 + 7000 + 5000 + 1000000 + 50) / 7 = 152007。


Count

Count用來計算符合條件的資料筆數。

Scenario

計算SalesID為2的資料筆數。

var sales2Goods = Goods.Count(x => x.SalesID == 2);

SQL


SELECT COUNT(*)
FROM [Good] AS t0
WHERE (t0.[SalesID] = 2

Result

3。我們也可以使用Where的方式取得資料筆數,如下:

var sales2Goods = Goods.Where(x => x.SalesID == 2).Count();


Except

Except可用來比較兩組集合的差異元素並回傳差異元素集合(注意: )。

Scenario

有兩個List,aList為Goods所有商品中價格大於5000的商品集合;bList為Goods所有商品中SalesID為2的商品集合。接著我們對aList中的商品進行對bList的商品進行排除。
var aList = Goods.Where(x => x.Price > 5000);
var bList = Goods.Where(x => x.SalesID == 2);
var diff = aList.Except(bList);

SQL

SELECT t0.[AddDate], t0.[Description], t0.[ID], t0.[Name], t0.[Price], t0.[SalesID]
FROM [Good] AS t0
WHERE ((t0.[Price] > 5000) AND NOT EXISTS(
  SELECT 0
  FROM [Good] AS t1
  WHERE ((t1.[SalesID] = 2) AND (t1.[ID] = t0.[ID]))
))

Result



First

取得集合中符合條件的第一筆資料。

Scenario

在所有商品中,SalesID為2的共有3筆。3筆依價格高低排序(高順位)後取得最價格的一筆。
var first = Goods.OrderByDescending(x => x.Price).First(x => x.SalesID == 2);

SQL

SELECT t0.[AddDate], t0.[Description], t0.[ID], t0.[Name], t0.[Price], t0.[SalesID]
FROM [Good] AS t0
WHERE (t0.[SalesID] = 2)
ORDER BY t0.[Price] DESC
LIMIT 0, 1

Result


上面的SQL使用LIMIT,指的是SELECT出來的結果限制從index 0開始往後算取1筆。看完了First就要提一下他的另一個延伸擴充方法 FirstOrDefault使用 First 要注意的是必須確定你搜尋出來的結果一定要有資料,不然會丟出InvalidOperationException。然而,使用FirstOrDefault在搜尋結果沒資料時,至少會回傳該集合Type的預設值,例如int就回傳0, string就回傳null,object或其他reference type回傳null。所以如果你不確定回傳的結果有沒有資料的話就建議使用FirstOrDefault


Intersect

Intersect是取得兩個集合的所有交集元素。

Scenario

在商品價格大於7000(aList)與SalesID為2(bList)的兩個集合中取得交集。
var aList = Goods.Where(x => x.Price > 7000);
var bList = Goods.Where(x => x.SalesID == 2);
var intersect = greaterThan5000.Intersect(salesIdEqualTo2);

SQL

SELECT t0.[AddDate], t0.[Description], t0.[ID], t0.[Name], t0.[Price], t0.[SalesID]
FROM [Good] AS t0
WHERE ((t0.[Price] > 7000) AND EXISTS(
  SELECT 0
  FROM [Good] AS t1
  WHERE ((t1.[SalesID] = 2) AND (t1.[ID] = t0.[ID]))
))


Result



Last

Last與First功能類似。First是取得集合中的第一筆,反之,Last為取得集合中的最後一筆。

Scenario

與First對比,我們就用和First的相同範例進行解說,在所有商品中,SalesID為2的共有3筆。3筆依價格高低排序(高順位)後取得最價格的一筆。
var last = Goods.OrderByDescending(x => x.Price).Last(x => x.SalesID == 2);

SQL


SELECT t0.[AddDate], t0.[Description], t0.[ID], t0.[Name], t0.[Price], t0.[SalesID]
FROM [Good] AS t0
WHERE (t0.[SalesID] = 2)
ORDER BY t0.[Price]
LIMIT 0, 1

Result


從Last Gen出來的SQL和Firt Gen出來的SQL可以看到,兩者在前段其實都差不多,但不同的是,我在Last的Sample Code下的是OrderByDescending,可是Gen出來的SQL竟然是ORDER BY,組合出來的意思是蠻符合邏輯的,但限量還是蠻不解的。

Max


Max功用與SQL語法的Max相同,就是取得集合中某個欄位的最大數值。

Scenario


在所有商品中取得最貴的價格。

var max = Goods.Max(x => x.Price);

SQL

SELECT MAX(t0.[Price])
FROM [Good] AS t0

Result

1000000。

Mix

與Max相對,與SQL語法的Min相同,就是取得集合中某個欄位的最小值。

Scenario


在所有商品中取得最便宜的價格。

var min = Goods.Min(x => x.Price);

SQL


SELECT MIN(t0.[Price])
FROM [Good] AS t0

Result

50。


最近實在是幾不太出時間來寫,這篇就花了蠻多時間的,但仔細看完後可以馬上用就感覺不錯。這回還未把剩下的方法全介紹完,所以期待Part III, Part IV...吧。


相關連結:

LINQ to Entities Part I 



參考來源:

MSDN - IEnumerable(T) 介面










留言