我想編寫一個由子類重寫的抽象方法,該方法的作用是返回一個表達式,以便隨後在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());
我收到此錯誤:
'無法將類型'System.Int32'強制轉換為'System.Object'。 LINQ to Entities僅支持轉換EDM原語或枚舉類型。
我可以馬上說,類型參數對像是問題的原因。因此,當我將type參數更改為int時,只要我按int字段(例如字段item_no
)排序,它就可以正常工作。
Q1。我怎樣才能讓它發揮作用?當然,我可以使用字符串屬性OrderByField
而不是表達式返回方法和它的順序,可能是通過為IQueryable編寫一些擴展方法(也許使用這個很棒的答案 )。但是我希望在設置順序時有更多智能感知。
Q2。如何從OrderByField()
方法返回的表達式中獲取列的名稱。顯然我嘗試過的東西不起作用。該member
始終為null。
編輯:我對方法的類型參數進行了一些更改。很抱歉沒有第一次這樣做。
顯然, Expression<Func<T, object>>
不等同於Expression<Func<T, K>>
,因此不能用作Queryable.OrderBy<T, K>
和類似方法所要求的後者的直接替換。
通過Expression.Lambda
方法創建非通用LambdaExpression
並動態發出對相應Queryable
方法的調用,仍然可以在Expression
類的幫助下使其工作。
以下是自定義擴展方法中封裝的所有內容(我對如何使用字符串通過表達式創建EF順序的答案的修改版本? ):
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 體中去掉它,這是通過以下代碼部分完成的:
var body = keySelector.Body;
if (body.NodeType == ExpressionType.Convert)
body = ((UnaryExpression)body).Operand;
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
方法工作。