Setting a dynamic sort name field in a linq query

2019-05-27 12:20发布

I want to be able to get an OrderBy query working with a lambda expression so that I get a SQL query with the TOP(n) key word (big performance boost).

I am able to do this if I specifiy ...

PaginatedList = query.OrderBy(x => x.QuoteID).Skip(() => skipValue).Take(() => pageSize)

But because I want the orderBy field to be dynamic through a UI selection of a name I want to do something like this:

var propertyInfo = typeof(Data.Quote).GetProperty(sortName);
Expression<Func<Data.Quote, object>> orderField = x => propertyInfo.GetValue(x, null);
PaginatedList = query.OrderBy(orderField).Skip(() => skipValue).Take(() => pageSize)

This gives me the error:

"LINQ to Entities does not recognize the method 'System.Object GetValue(System.Object)' method, and this method cannot be translated into a store expression."

I tried this that's not of type Expression<Func<T, object>>

var propertyInfo = typeof(Data.Quote).GetProperty(sortName);
Func<Data.Quote, object> orderField = x => propertyInfo.GetValue(x, null);
PaginatedList = query.OrderBy(x => orderField).Skip(() => skipValue).Take(() => pageSize)

And I get this error:

"Unable to create a constant value of type [...]. Only primitive types or enumeration types are supported in this context"

I'm sure there is a way to achieve this but at the moment not sure how.

3条回答
虎瘦雄心在
2楼-- · 2019-05-27 12:55

Instead of that you need to create an expression to select that property.From this source:

public static class Utility
{
    //makes expression for specific prop
    public static Expression<Func<TSource, object>> GetExpression<TSource>(string propertyName)
    {
        var param = Expression.Parameter(typeof(TSource), "x");
        Expression conversion = Expression.Convert(Expression.Property
        (param, propertyName), typeof(object));   //important to use the Expression.Convert
        return Expression.Lambda<Func<TSource, object>>(conversion, param);
    }


    public static IOrderedQueryable<TSource> 
    OrderBy<TSource>(this IQueryable<TSource> source, string propertyName)
    {
        return source.OrderBy(GetExpression<TSource>(propertyName));
    }
}

Then you can order by as I show below:

var result=Query.OrderBy(sortName)...;
查看更多
霸刀☆藐视天下
3楼-- · 2019-05-27 12:59

Here is how to achieve what you want:

var propertyInfo = typeof(Data.Quote).GetProperty(sortName);

ParameterExpression parameter = Expression.Parameter(typeof(T), "s");
MemberExpression property = Expression.Property(parameter, propertyInfo);
LambdaExpression sort = Expression.Lambda(property, parameter);

MethodCallExpression call = Expression.Call(
                                         typeof(Queryable),
                                         "OrderBy",
                                         new[] {typeof(T), property.Type},
                                         Query.Expression,
                                         Expression.Quote(sort));

var orderedQuery = (IOrderedQueryable<T>)Query.Provider.CreateQuery<T>(call);

PaginatedList = orderedQuery.Skip(skipValue).Take(pageSize);
查看更多
狗以群分
4楼-- · 2019-05-27 13:00

The value being copied in to propertyInfo is just an Object, as that is the type returned by GetProperty(). Hover over var and you will confirm this.

The GetValue method doesn't exist for an Object, so you need to cast it to the right type before making the call to GetValue.

查看更多
登录 后发表回答