1. 程式人生 > >Linq To Entities中的動態排序

Linq To Entities中的動態排序

ops 基於 做了 生成 con component 功能 bar foreach

換了工作有一個月了,一樣的工作、一樣的代碼、一樣的體力活仍就……

Linq To Entityes 也是不新玩意了,近半年來也一直與之打交道,但一直也沒對其深究過。今天新加的功能要對所有列支持排序,這也不是什麽高難度的工作了,對與TSQL來說是寫過幾百遍了,但在Linq To Enitities中有點小惡心。

Linq To Entityes中的對象是個Queryable類型的,Queryable.OrderBy()方法的參數是一個Lamdba

source.OrderBy(t=>t.ID).Skip(0).Take(10)

表達式。第一個想到的便是用字段名反射出類型,如

source.OrderBy(t=>t.GetType().GetField(“ID”)).Skip(0).Take(10);

而運行時會報“無法識別GetField()方法”。

Linq實際上是一個表達式樹,是基於語言級別的代碼,OrderBy中需要一個表達式樹。

在老外的網站上找到了如下代碼,豁然開朗

        public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> source, string property)
        { 
            return ApplyOrder<T>(source, property, "OrderBy"); 
        }
 
        public static IOrderedQueryable<T> OrderByDescending<T>(this IQueryable<T> source, string property)
        { 
            return ApplyOrder<T>(source, property, "OrderByDescending"); 
        }
 
        public static IOrderedQueryable<T> ThenBy<T>(this IOrderedQueryable<T> source, string property)
        {
            return ApplyOrder<T>(source, property, "ThenBy"); 
        }
 
        public static IOrderedQueryable<T> ThenByDescending<T>(this IOrderedQueryable<T> source, string property)
        { 
            return ApplyOrder<T>(source, property, "ThenByDescending"); 
        }
 
        static IOrderedQueryable<T> ApplyOrder<T>(IQueryable<T> source, string property, string methodName)
        {
            string[] props = property.Split(‘.‘);
            Type type = typeof(T); ParameterExpression arg = Expression.Parameter(type, "x");
            Expression expr = arg; foreach (string prop in props)
            { 
                // use reflection (not ComponentModel) to mirror LINQ 
                PropertyInfo pi = type.GetProperty(prop); expr = Expression.Property(expr, pi); type = pi.PropertyType;
            } 
            Type delegateType = typeof(Func<,>).MakeGenericType(typeof(T), type); 
            LambdaExpression lambda = Expression.Lambda(delegateType, expr, arg); 
            object result = typeof(Queryable).GetMethods().Single(method => method.Name == methodName 
                            && method.IsGenericMethodDefinition 
                            && method.GetGenericArguments().Length == 2 
                            && method.GetParameters().Length == 2)
                            .MakeGenericMethod(typeof(T), type)
                            .Invoke(null, new object[] { source, lambda }); 
            return (IOrderedQueryable<T>)result;
        }

上面的代碼滿足所有的排序功能了,我的項目中只需要對單列排序,所以對代碼做了簡化

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <param name="property">排序字段</param>
        /// <param name="isAscdening"></param>
        /// <returns></returns>
        static IOrderedQueryable<T> ApplyOrder<T>(IQueryable<T> source, string property, bool isAscdening)
        {
            Type type = typeof(T);
            ParameterExpression arg = Expression.Parameter(type, "x");
            Expression expr = arg;
 
            PropertyInfo pi = type.GetProperty(property);
            expr = Expression.Property(expr, pi);
            type = pi.PropertyType;
 
            Type delegateType = typeof(Func<,>).MakeGenericType(typeof(T), type);
            LambdaExpression lambda = Expression.Lambda(delegateType, expr, arg);
 
            object result;
            if (true == isAscdening)
            {
                result = typeof(Queryable).GetMethods().
                    Single(method => method.Name == "OrderBy" && method.IsGenericMethodDefinition
                        && method.GetGenericArguments().Length == 2 && method.GetParameters().Length == 2).
                    MakeGenericMethod(typeof(T), type)
                    .Invoke(null, new object[] { source, lambda });
            }
            else
            {
                result = typeof(Queryable).GetMethods().
                    Single(method => method.Name == "OrderByDescending" && method.IsGenericMethodDefinition
                        && method.GetGenericArguments().Length == 2 && method.GetParameters().Length == 2).
                    MakeGenericMethod(typeof(T), type)
                    .Invoke(null, new object[] { source, lambda });
            }
            return (IOrderedQueryable<T>)result;
        } 

還可以將方法做Queryable的擴展方法,使用起來會更方便

根據列名動態生成查詢

技術分享 static IQueryable<T> MakeQuery<T>(IQueryable<T> source, string colName, string colValue)
{
Type theType = typeof(T);
//創建一個參數c
ParameterExpression param = Expression.Parameter(typeof(T), "c");
//c.City=="London"
Expression left = Expression.Property(param,typeof(T).GetProperty(colName));
Expression right = Expression.Constant(colValue);
Expression filter = Expression.Equal(left, right);

Expression pred = Expression.Lambda(filter, param);
//Where(c=>c.City=="London")
Expression expr = Expression.Call(typeof(Queryable), "Where",
new Type[] { typeof(T) },
Expression.Constant(source), pred);
//生成動態查詢
IQueryable<T> query = source.AsQueryable()
.Provider.CreateQuery<T>(expr);
return query;
}

Linq To Entities中的動態排序