網頁

2014年4月25日 星期五

練習題 - LINQ Multi Columns Dynamic Group

前一篇文章「練習題 - LINQ Single Column Dynamic Group」練習了各種動態單一欄位的 Group 操作,這一篇則是要來練習動態多欄位的 Group 操作。

使用環境:VS2013, ASP.NET MVC 5, EntityFramework 6.1.0

使用資料庫:Northwind


基本操作

首先來看看一般的情況下(非動態)是如何處理多欄位的 Group 操作。

下面的操作是使用 LINQ Method Syntax 的寫法,指定 SupplierID 與 CategoryID 來作為 Group 的 Key,

image

執行結果

image

T-SQL

image

 

接著再試試改用 LINQ Query Syntax 的方式來重新寫過上面的操作,

image

執行的頁面結果就跟上面的的一樣,所以就不顯示,以下直接顯示 T-SQL 內容,

image

 

 

使用 DynamicQuery(Dynamic Expression API)

看完以上的分別使用 LINQ Method Syntax 與 Query Syntax 的多欄位 Group 操作,其實病沒有使用動態多欄位的操作,這邊我必須坦承,其實對於要如何使用 LINQ 製作動態多欄位的 Group,我還真的沒有頭緒,網路上有很多的作法,但是都沒有讓我覺得容易理解與使用的方法,最後我還是選擇使用 DynamicQuery 來處理。

image

完整的程式碼

public ActionResult Index3()
{
    db.Database.Log = (log) => Debug.WriteLine(log);
 
    //it: Current Instance
 
    var query = db.Products
                  .GroupBy("new(SupplierID,CategoryID)", "it", null)
                  .Select("new(Key.SupplierID as SupplierID, Key.CategoryID as CategoryID, Count() as Count)")
                  .OrderBy("SupplierID, CategoryID");
 
    var message = new StringBuilder();
    foreach (var result in query)
    {
        message.AppendLine(result + "</br>");
    }
    ViewBag.GroupResult = message.ToString();
 
    return View();
}

執行結果

image

T-SQL

image

 

老實說,在 Controller 裡寫的那一段根本不算是動態(看看下面的程式),除非 GroupBy, Select, OrderBy 裡面的文字是用動態產生的,但我實在是不想去處理產生這些的動態文字,因為後續的變動性與維護性都不是很好。

public ActionResult Index3()
{
    db.Database.Log = (log) => Debug.WriteLine(log);
 
    //it: Current Instance
 
    var query = db.Products
                  .GroupBy("new(SupplierID,CategoryID)", "it", null)
                  .Select("new(Key.SupplierID as SupplierID, Key.CategoryID as CategoryID, Count() as Count)")
                  .OrderBy("SupplierID, CategoryID");
 
    var message = new StringBuilder();
    foreach (var result in query)
    {
        message.AppendLine(result + "</br>");
    }
    ViewBag.GroupResult = message.ToString();
 
    return View();
}

 

 

使用 GroupByMany 方法

twMVC 的夥伴「Bruce Chen 陳傳興」對於 LINQ 動態多欄位 Group 的處理提供了兩篇文章參考,

Playing with Linq grouping: GroupByMany ? – Mitsu’s blog- Site Home - MSDN Blogs

Linq GroupByMany dynamically – Mitsu’s blog - Site Home - MSDN Blogs

綜合以上兩篇文章的作法,我整理了以下的實作內容。

 

首先建立一個 GroupResult 物件,

image

再來針對 IEnumerable<GroupResult> 建立兩個擴充方法,

image

public static class MyEnumerableExtensions
{
    public static IEnumerable<GroupResult> GroupByMany<TElement>(
        this IQueryable<TElement> elements,
        params Func<TElement, object>[] groupSelectors)
    {
        if (groupSelectors.Length > 0)
        {
            var selector = groupSelectors.First();
 
            //reduce the list recursively until zero
            var nextSelectors = groupSelectors.Skip(1).ToArray();
            var result = elements
                .GroupBy(selector)
                .Select(
                    g => new GroupResult
                    {
                        Key = g.Key,
                        Count = g.Count(),
                        Items = g,
                        SubGroups = g.GroupByMany(nextSelectors)
                    });
 
            return result;
        }
        else
        {
            return null;
        }
    }
 
 
    public static IEnumerable<GroupResult> GroupByMany<TElement>(
        this IEnumerable<TElement> elements,
        params Func<TElement, object>[] groupSelectors)
    {
        if (groupSelectors.Length > 0)
        {
            var selector = groupSelectors.First();
 
            //reduce the list recursively until zero
            var nextSelectors = groupSelectors.Skip(1).ToArray();
            var result = elements
                .GroupBy(selector)
                .Select(
                    g => new GroupResult
                    {
                        Key = g.Key,
                        Count = g.Count(),
                        Items = g,
                        SubGroups = g.GroupByMany(nextSelectors)
                    });
 
            return result;
        }
        else
        {
            return null;
        }
    }
}

Controller 內的程式做法

image

View

image

執行結果

image

T-SQL

image

看到上面的 T-SQL 結果之後,我想大家應該都知道最後的這一個方法效能並不是很好,因為是將所有的資料從資料庫抓出來之後,再從系統裡去做 Group 的操作。

 

從上面一路看下來,可以得知,要使用 LINQ 去做到動態多欄位的 Group 操作還真的不好做耶。

或許是我能力不足、功力尚淺(簡言之就是嫩!)

也許是我還沒有碰到真的非得要作到 LINQ 做動態多欄位的 Group 操作,所以我無法了解為何一定要這麼做,如果說是因為要做報表或是一些統計彙整的功能,我真的建議就請 DBA 高手來幫我們處理這部份的需求或是建議我們應該要怎麼處理,至於用 LINQ 來處理這樣的需求…… 真的,程式開發人員的時間是有限的、是珍貴的,時間應該浪費在美好的事物上面。

 

以上

沒有留言:

張貼留言