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 來處理這樣的需求…… 真的,程式開發人員的時間是有限的、是珍貴的,時間應該浪費在美好的事物上面。

 

以上

沒有留言:

張貼留言

提醒

千萬不要使用 Google Talk (Hangouts) 或 Facebook 及時通訊與我聯繫、提問,因為會掉訊息甚至我是過了好幾天之後才發現到你曾經傳給我訊息過,請多多使用「詢問與建議」(在左邊,就在左邊),另外比較深入的問題討論,或是有牽涉到你實作程式碼的內容,不適合在留言板裡留言討論,請務必使用「詢問與建議」功能(可以夾帶檔案),謝謝。