c# - 从要在 OrderBy 中使用的方法返回表达式

标签 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());

我收到这个错误:

'Unable to cast the type 'System.Int32' to type 'System.Object'. LINQ to Entities only supports casting EDM primitive or enumeration types.'

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

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

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


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

最佳答案

显然是 Expression<Func<T, object>>不等于 Expression<Func<T, K>> ,因此不能直接替换 Queryable.OrderBy<T, K> 所需的后者和类似的方法。

仍然有可能在 Expression 的帮助下使其工作通过创建一个非通用类 LambdaExpression通过 Expression.Lambda 方法并动态发出对相应 Queryable 的调用方法。

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

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<Func<T, object>>介绍 Expression.Convert 对于返回值类型的表达式,因此需要将其从实际的 lambda body 中剥离,这是通过以下部分代码完成的:

var body = keySelector.Body;
if (body.NodeType == ExpressionType.Convert)
    body = ((UnaryExpression)body).Operand;

关于c# - 从要在 OrderBy 中使用的方法返回表达式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45277310/

相关文章:

loops - Java 8 Stream 嵌套 for 循环

c# - Entity Framework 7 自引用表返回 null

c# - 另一个程序集中的 MVVM 行为

c# - 将对象转换为可查询对象

java - 在 Java 8 中按 map 列表分组

c++ - 与本地 lambda 一起使用时,模板函数会导致编译器错误

c# - 任务WhenAll异常处理

c# - ASP.NET 核心 : CORS headers only for certain static file types

c# - 使用左连接 lambda 的 Entity Framework 加载数据

c# - 使用 LINQ 过滤字符串