zoukankan      html  css  js  c++  java
  • 使用Expression做Linq的參數化排序

    Linq非常的好用,減少大量的資料庫操作手序,使用具名的類別,減少了在程式中寫SQL寫錯字的可能性,問題來了,如果我想用QueryString中的參數,作為排序的依據,但是因為是具名的類別,不能指定字串,剛開始我是用switch一個一個指定,但欄位一多就覺得這方法很笨,在搜尋更好的方法中發現使用System.Linq.Expressions.Expression可以決解這個問題。 如果各位有仔細看,會發現System.Linq.Queryable下的Method參數都有Expression,如本篇要用的OrderBy。

    1
    2
    3
    4
    public static IOrderedQueryable<TSource> OrderBy<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector);
    public static IOrderedQueryable<TSource> OrderBy<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector, IComparer<TKey> comparer);
    public static IOrderedQueryable<TSource> OrderByDescending<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector);
    public static IOrderedQueryable<TSource> OrderByDescending<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector, IComparer<TKey> comparer);
     

    那什麼是Expression呢?

    簡單說是動態產生Delegate,真的執行的時候才Compile,你會說在Visaul Studio明明就Compile啦,怎麼又到執行才Compile?

    實際上在Visaul Studio Compile時,會先編成Expression,等到執行時再將Expression Compile成Delegate,如下面範例。

    1
    2
    3
    4
    5
    6
    //在Visual Studio這樣寫的東西。
    .OrderBy(x=>x.Name);
    //Compile後其實是編成Expression(從.Net Reflector中取得後,有修改成易讀格式)。
    Parameter p = Expression.Parameter(typeof(type), "x");// 參數X
    Expression.Lambda<Func<type, string>>(Expression.Property(p, "Name"), p));
    // x.Name//並不是所有的Lambda都會編成Expression,而是只有參數是Expression才會編成Expression,其他的還是直接編成Method。
     
    想更了解Expression的朋友可以參考,下面的文章。

    System.Linq.Expressions 命名空間

    EXPRESSION TREES, TAKE TWO – INTRODUCING SYSTEM.LINQ.EXPRESSIONS V4.0

    Building LINQ Queries at Runtime in C#

    初识System.Linq.Expressions

    所以也可以自己產生Expression做OrderBy的參數,範例如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    if (!string.IsNullOrEmpty(this.Request.QueryString["Order"])){
       // 產生Expression
       var param = Expression.Parameter(typeof(Project), "x");
       var orderExpression = Expression.Lambda<Func<Project, object>>(Expression.Property(param, this.Request.QueryString["Order"]), param);
       if (this.Request.QueryString["OrderDirection"] == "Desc")
       {
           query = query.OrderByDescending(orderExpression);
       }
       else
       {
           query = query.OrderBy(orderExpression);
       }
    }

    但上面的範例遇到遇到nullable的型別會掛到所以又寫了幾個Extension來使用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    public static class IQueryableExtension
    {
        private static MethodInfo orderbyInfo = null;
        private static MethodInfo orderbyDecInfo = null;
     
        public static IQueryable<T> OrderBy<T>(this IQueryable<T> query, string property) where T : class
        {  
            Type entityType = typeof(T);
            Type entityPropertyType = entityType.GetProperty(property).PropertyType;
     
            var orderPara = Expression.Parameter(entityType, "o");
            var orderExpr = Expression.Lambda(Expression.Property(orderPara, property), orderPara);
     
            if (orderbyInfo==null)
            {
                //因為呼叫OrderBy需要知道型別,不知道的情況下無法直接呼叫,所以用反射的方式呼叫
                //泛型的GetMethod很難,所以用GetMethods在用Linq取出Method,找到後快取。
                orderbyInfo = typeof(Queryable).GetMethods().Single(x => x.Name == "OrderBy" && x.GetParameters().Length == 2);
            }
     
            //因為是泛型Mehtod要呼叫MakeGenericMethod決定泛型型別
            return orderbyInfo.MakeGenericMethod(new Type[] { entityType, entityPropertyType }).Invoke(null, new object[] { query, orderExpr }) as IQueryable<T>;
        }
     
        public static IQueryable<T> OrderByDescending<T>(this IQueryable<T> query, string property)
        {
            Type entityType = typeof(T);
            Type entityPropertyType = entityType.GetProperty(property).PropertyType;
     
            var orderPara = Expression.Parameter(entityType, "o");
            var orderExpr = Expression.Lambda(Expression.Property(orderPara, property), orderPara);
     
            if (orderbyDecInfo == null)
            {
                orderbyDecInfo = typeof(Queryable).GetMethods().Single(x => x.Name == "OrderByDescending" && x.GetParameters().Length == 2);
            }
     
            return orderbyDecInfo.MakeGenericMethod(new Type[] { entityType, entityPropertyType }).Invoke(null, new object[] { query, orderExpr }) as IQueryable<T>;
        }
    }
  • 相关阅读:
    mac 10.15.7 修改PATH
    oc 属性类型一般用法
    ubuntu解压zip文件名乱码
    telnet 退出
    docker 根据容器创建镜像
    mac android adb device 没有显示设备
    Yii2 查看所有的别名 alias
    Yii2 App Advanced 添加 .gitignore
    ubuntu 18.04 搜狗突然就提示乱码
    An error occured while deploying the file. This probably means that the app contains ARM native code and your Genymotion device cannot run ARM instructions. You should either build your native code to
  • 原文地址:https://www.cnblogs.com/hyl8218/p/3164716.html
Copyright © 2011-2022 走看看