我想编写一个被子类覆盖的抽象方法,该方法所做的是返回一个表达式,随后在 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/