我想编写一个被子类重写的抽象方法,该方法的作用是返回一个表达式,以便随后在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
始终为空。
编辑:我对方法的类型参数进行了一些更改。很抱歉没有第一次这样做。
答案 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
方法起作用。