[LINQ] 使用 Dynamic LINQ 補足強型別所帶來的小遺憾

使用 Dynamic LINQ 補足強型別所帶來的小遺憾 

前言

在開發撰寫LINQ語句時,透過其強型別的特性再搭配Intellisense輔助,讓我們可以行雲流水般地使用Lambda運算式篩選所需資料;但是當我們的條件是透過前端傳回,例如由前端控制資料排序是依據哪個欄位,所以只會回傳欄位名稱字串,此時若只允許以強型別方式操作LINQ就會形成莫大的阻力了,當然還是可以自行使用反射(Reflection)方式來解決,但還有更好的方式就是使用 Dynamic LINQ 來達成。以下介紹。

 

環境

System.Linq.Dynamic 1.0.4

 

案例說明

誠如前言所述,我們需要一個測驗(Quiz)資料排序功能,定義Quiz物件包含資訊學生ID及各項成績如下。

public class Quiz
{
    public int StudentId { get; set; }
    public int MathScore { get; set; }
    public int ChineseScore { get; set; }
}

前端就只回傳排序欄位(sortKey)名稱與排列方向(isDesc),接著就依據該名稱來對資料進行排序。

public class QuizController : Controller
{

    public ActionResult SortQuiz(string sortKey = "MathScore", bool isDesc = false)
    {
        // sample data
        var quizzes = new List<Quiz>()
        {
            new Quiz() {StudentId=1, MathScore=33, ChineseScore=88},
            new Quiz() {StudentId=2, MathScore=88, ChineseScore=66},
            new Quiz() {StudentId=3, MathScore=11, ChineseScore=22}
        }.AsQueryable();

        // sort data
        quizzes = Sort(quizzes, sortKey, isDesc);
        return View(quizzes);
    }
   
}

如果使用直挺挺的方式處理排序功能,約略代碼如下。

public IQueryable<Quiz> Sort(IQueryable<Quiz> quizzes, string key, bool isDesc)
{
    Expression<Func<Quiz, object>> sortExpression;
    switch (key)
    {
        case "StudentId":
            sortExpression = (q => q.StudentId);
            break;
        case "MathScore":
            sortExpression = (q => q.MathScore);
            break;
        case "ChieseScore":
            sortExpression = (q => q.ChineseScore);
            break;
        default:
            sortExpression = (q => q.StudentId);
            break;
    }

    // sort direction
    quizzes = isDesc
              ? quizzes.OrderByDescending(sortExpression)
              : quizzes.OrderBy(sortExpression);

    return quizzes;
}

從上面的代碼中不難發現,為了遷就其強型別的特性而造成程式碼過於攏長;因此這時就可以使用 Dynamic LINQ 來處理不同欄位排序問題。首先請先至NuGet安裝 System.Linq.Dynamic Package。

引用 Dynamic LINQ 後就可以使用字串方式來撰寫LINQ。我們可以發現程式碼變得相當精簡,就如同在編寫SQL語句般地暢快,但此舉會讓程式缺乏編譯時偵錯的保護,因此需特別注意傳入 key 值是否與屬性名稱一致,如果傳入值異常,那麼這個錯誤最終就只會在Runtime炸開而造成災情。

using System.Linq.Dynamic;

public IQueryable<Quiz> Sort(IQueryable<Quiz> quizzes, string key, bool isDesc)
{
    quizzes = quizzes.OrderBy(key + (isDesc ? " descending" : ""));
    return quizzes;
}

執行結果如下所示,排序條件與撈出資料順序相符

除排序(OrderBy)功能外,搜尋條件(Where)也可使用字串方式進行,參考代碼如下。

執行結果如下所示,篩選及排序條件與撈出資料相符

 

參考資訊

http://weblogs.asp.net/scottgu/dynamic-linq-part-1-using-the-linq-dynamic-query-library

https://ericflemingblog.wordpress.com/2015/01/02/dynamic-linq-queries/


希望此篇文章可以幫助到需要的人

若內容有誤或有其他建議請不吝留言給筆者喔 !