2013年10月11日 星期五

ASP.NET MVC 使用 jQuery EasyUI DataGrid - 多欄排序 (Multiple Column Sorting) Part.2

前一篇「ASP.NET MVC 使用 jQuery EasyUI DataGrid - 多欄排序 (Multiple Column Sorting) Part.1」已經完成了多欄排序的伺服器端的程式內容,不過還是有修改的空間,所以這篇文章就接續完成需要修改的部份。

 


在前一篇裡有提到,在處理多欄排序時我們不使用 Dynamic Expression API 去作多欄位排序的處理,而是改用 IOrderedQueryableHelper 動態產生 IOrderedEnumerable<T>,但如果在啟用多欄排序的情況下只有做單一欄位的排序時,例如進入 Customer/Index 頁面時,DataGrid 的排序是預設對 CustomerID 進行 asc 排序,這樣的情況似乎就不需要使用 IOrderedQueryableHelper 來取得 IOrderedEnumerable<T>,所以因應這樣的情況就將單一欄位排序與多欄排序的處理做個區分。

 

Step.1

將原本在 CustomerService.GetJsonForGrid 方法內的程式給抽出來,

image

建立一個私有方法 GetMultipleColumnSortingQuery  放置剛才所抽離出來的程式,

image

 

Step.2

在之前「ASP.NET MVC 使用 jQuery EasyUI DataGrid - 排序 (Sorting)」這篇文章裡有說過,為預防傳入的 order 與 sort 值有錯誤而造成程式執行的錯誤,所以在 CustomerService 裡針對傳入的 propertyName 會檢查是否為 Customer 的屬性名稱之一。

這個部份我希望不要在程式裡直接給欄位名稱的字串,而是自動從 Entity KeyMemers 裡取得第一個 KeyMember 名稱,讓程式增加彈性,在「Entity Framework 5 - 取得 Entity 的 Property Names 與 KeyMembers」這篇文章裡就有說明過,所以在 EntityHelper.cs 增加以下的程式內容:

image

/// <summary>
/// Keys the members.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static List<string> KeyMembers<T>() where T : class
{
    List<string> result = new List<string>();
 
    string entityName = typeof(T).Name;
 
    string keyMembers = EntityHelper.GetAllEntityKeyMembers()
        .FirstOrDefault(x => x.Key == entityName).Value;
 
    if (!string.IsNullOrWhiteSpace(keyMembers))
    {
        result = keyMembers.Split(',').ToList();
    }
    return result;
}
 
/// <summary>
/// Gets all entity key members.
/// </summary>
/// <returns></returns>
private static Dictionary<string, string> GetAllEntityKeyMembers()
{
    var result = new Dictionary<string, string>();
 
    var mw = new MetadataWorkspace(
        new[] {"res://*/"},
        new[] {Assembly.GetExecutingAssembly()});
 
    var tables = mw.GetItems(DataSpace.CSpace);
 
    foreach (var e in tables.OfType<EntityType>())
    {
        result.Add(e.Name, string.Join(",", e.KeyMembers));
    }
    return result;
}

 

Step.3

接著建立一個 GetSingleColumnSortingQuery 方法,用來操作單一欄位排序的 IQueryalbe<Customer> 的處理,在 GetSingleColumnSortingQuery 方法裡就會使用到 Step.2 所新增加的方法。

image

在資料排序處理的部份就不使用 Dynamic Expression API,而是使用 IOrderedQueryableHelper 來處理。

private IQueryable<Customer> GetSingleColumnSortingQuery(
    int page,
    int pageSize,
    string propertyName,
    string sortOrder)
{
    //取得 Entity 所有的 Property 名稱
    var entityPropertyNames = EntityHelper.EntityPropertyNames<Customer>();
 
    if (!entityPropertyNames.Contains(propertyName))
    {
        propertyName = EntityHelper.KeyMembers<Customer>().FirstOrDefault();
    }
 
    if (!(sortOrder.Equals("asc", StringComparison.OrdinalIgnoreCase)
          || sortOrder.Equals("desc", StringComparison.OrdinalIgnoreCase)))
    {
        sortOrder = "asc";
    }
 
    var query = db.Customers.AsQueryable();
 
    query = sortOrder.Equals("asc", StringComparison.OrdinalIgnoreCase)
        ? query.OrderBy<Customer>(propertyName)
        : query.OrderByDescending<Customer>(propertyName);
 
    query = query.Skip((page - 1) * pageSize).Take(pageSize);
 
    return query;
}

 

Step.4

完成了 GetSingleColumnSortingQuery, GetMultipleColumnSortingQuery 以及增加 EntityHelper 內的程式之後,現在重新修改 CustomerService.GetJsonForGrid 裡的程式。

程式裡並不是直接拿傳進方法的 propertyName 來判斷是否為單一欄位排序或是多欄位排序,而是以處理過 propertyName 與 order 的結果來判斷,以 propertySortTuples 的資料數量來做決定,

image

public JArray GetJsonForGrid(
    int page = 1,
    int pageSize = 10,
    string propertyName = "CustomerID",
    string order = "asc")
{
    // 取得多個排序欄位與順序的 Tuple 結果
    var propertySortTuples =
        EntityHelper.GetPropertySortTuples<Customer>(propertyName, order);
 
    var query = propertySortTuples.Count().Equals(1)
        ? this.GetSingleColumnSortingQuery(page, pageSize, propertyName, order)
        : this.GetMultipleColumnSortingQuery(page, pageSize, propertySortTuples);
    
    JArray ja = new JArray();
 
    foreach (var item in query)
    {
        var itemObject = new JObject
        {
            {"CustomerID", item.CustomerID},
            {"CompanyName", item.CompanyName},
            {"ContactName", item.ContactName},
            {"ContactTitle", item.ContactTitle},
            {"Address", item.Address},
            {"City", item.City},
            {"Region", item.Region},
            {"PostalCode", item.PostalCode},
            {"Country", item.Country},
            {"Phone", item.Phone},
            {"Fax", item.Fax}
        };
        ja.Add(itemObject);
    }
    return ja;
}

 

Step.5

到上一個步驟就完成了我們這一次的修改,那麼這一個步驟還需要做什麼處理呢?

在這一個步驟裡我們可以依照「ASP.NET MVC + NLog + Clutch.Diagnostics.EntityFramework 追蹤 EF 執行的 SQL Command」這一篇文章裡的內容加入 NLog 與 Clutch.Diagnostics.EntityFramework,在進入偵錯模式的時候將 Entity Framework 所產生並執行的 SQL Command 內容顯示在 Visual Studio 的「輸出」視窗內,讓我們即時觀察在單一欄位或多欄位排序處理時在資料庫所執行的 SQL Command 內。

一開始進入 Customer 頁面時,DataGrid 第一次顯示資料的預設查詢

image

多增加了 CompanyName 的排序欄位

image

將 ConmpanyName 的排序順序變更為降冪

image

再增加排序欄位

image

 


最後這邊提醒一下,多欄位的排序先後順序是會依據你點選 DataGrid 欄位表頭時的順序,並不是依據 DataGrid 上所顯示的欄位排列順序,以免有人會錯意而造成誤會,如果要避免讓使用者誤會的話,可以去顯示使用者點選多欄位排序時的選擇先後順序,至於這個方法就留給各位自行去實做出來啦!

 

延伸閱讀:

Entity Framework 5 - 取得 Entity 的 Property Names 與 KeyMembers

ASP.NET MVC + NLog + Clutch.Diagnostics.EntityFramework 追蹤 EF 執行的 SQL Command

 

以上

沒有留言:

張貼留言

提醒

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