从OrderBy

时间:2017-07-24 09:47:05

标签: c# linq lambda expression-trees

我想编写一个被子类重写的抽象方法,该方法的作用是返回一个表达式,以便随后在LINQ OrderBy()中使用。像这样:

注意:Message继承自Notes班级,MyModel继承自MsgModel

public class Notes
{
    // abstract definition; using object so that I can (I hope) order by int, string, etc.
    public abstract Expression<Func<MsgModel, object>> OrderByField();
    // ...

    private string GetOrderByFieldName()
    {
        // I am not sure how to write this
        // This is my problem 2. Please see below for problem 1 :-(

        var lambda = OrderByField() as LambdaExpression;

        MemberExpression member = lambda.Body as MemberExpression;

        PropertyInfo propInfo = member.Member as PropertyInfo;

        return propInfo.Name;
    }
}

public class Message : Notes
{
    // second type parameter is object because I don't know the type
    // of the orderby field beforehand
    public override Expression<Func<MyModel, object>> OrderByField()
    {
        return m => m.item_no;
    }
}

现在,如果我尝试通过这种方式订购:

var orderedQuery = myQueryable.OrderBy(OrderByField());

我收到此错误:

  

&#39;无法投放类型&System; Int.322&#39;键入&#39; System.Object&#39;。 LINQ to Entities仅支持转换EDM原语或枚举类型。&#39;

我可以马上说,类型参数对象是问题的原因。因此,当我将type参数更改为int时,只要我按int字段(例如字段item_no)进行排序,它就可以正常工作。

Q1。我怎样才能让它发挥作用?当然,我可以使用字符串属性OrderByField而不是表达式返回方法和顺序,可能是通过为IQueryable编写一些扩展方法(可能使用this great answer)。但是我希望在设置顺序时有更多智能感知。

Q2。如何从方法OrderByField()返回的表达式中按列获取订单名称。显然我尝试过的并不起作用。 member始终为空。

编辑:我对方法的类型参数进行了一些更改。很抱歉没有第一次这样做。

2 个答案:

答案 0 :(得分:3)

显然,Expression<Func<T, object>>不等同于Expression<Func<T, K>>,因此无法直接替换Queryable.OrderBy<T, K>及类似方法所需的后续内容。

通过创建非通用Expression via Expression.Lambda方法并动态发出对相应{的调用,可以在LambdaExpression类的帮助下使其工作正常{1}}方法。

以下是自定义扩展方法(我对How to use a string to create a EF order by expression?的回答的修改版本)中封装的所有内容:

Queryable

这里的一个重要细节是public static partial class QueryableExtensions { public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> source, Expression<Func<T, object>> keySelector) { return source.OrderBy(keySelector, "OrderBy"); } public static IOrderedQueryable<T> OrderByDescending<T>(this IQueryable<T> source, Expression<Func<T, object>> keySelector) { return source.OrderBy(keySelector, "OrderByDescending"); } public static IOrderedQueryable<T> ThenBy<T>(this IOrderedQueryable<T> source, Expression<Func<T, object>> keySelector) { return source.OrderBy(keySelector, "ThenBy"); } public static IOrderedQueryable<T> ThenByDescending<T>(this IOrderedQueryable<T> source, Expression<Func<T, object>> keySelector) { return source.OrderBy(keySelector, "ThenByDescending"); } private static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> source, Expression<Func<T, object>> keySelector, string method) { var parameter = keySelector.Parameters[0]; var body = keySelector.Body; if (body.NodeType == ExpressionType.Convert) body = ((UnaryExpression)body).Operand; var selector = Expression.Lambda(body, parameter); var methodCall = Expression.Call( typeof(Queryable), method, new[] { parameter.Type, body.Type }, source.Expression, Expression.Quote(selector)); return (IOrderedQueryable<T>)source.Provider.CreateQuery(methodCall); } } 为值类型返回表达式引入了Expression.Convert,因此需要从实际的lambda body 中删除它,这是通过以下部分代码:

Expression<Func<T, object>>

答案 1 :(得分:2)

您的Notes课程需要几种通用类型。第一个是因此你可以从它派生并仍然允许表达式过滤派生类。第二种是指定您希望用于订购的财产的类型。例如:

public abstract class Notes<T, TProperty> where T : Notes<T, TProperty>
{
    public abstract Expression<Func<T, TProperty>> OrderByField();

    public string GetOrderByFieldName()
    {
        //snip
    }
}

public class Message : Notes<Message, int>
{
    public int item_no { get; set; }

    public override Expression<Func<Message, int>> OrderByField()
    {
        return m => m.item_no;
    }
}

这也应该允许GetOrderByFieldName方法起作用。