2011-07-31 17 views
8

我有,我想用排序列表的方法:使用「提供的參數」在Queryable上找不到OrderBy。

var myPreExistingQuery = new List<SomeType>{ new SomeType() }.AsQueryable(); 
var query = BuildQuery(myPreExistingQuery, "OrderBy", x => x.SomeProperty); 

唯一的例外是:

private static IQueryable<T> BuildQuery<T>(IQueryable<T> query, 
              string methodName, 
              Expression<Func<T, object>> property)    
    { 
     var typeArgs = new[] { query.ElementType, property.Body.Type }; 

     methodCall = Expression.Call(typeof (Queryable), 
                methodName, 
                typeArgs, 
                query.Expression, 
                property); 

     return query.Provider.CreateQuery<T>(methodCall); 
    } 

我得到一個異常,當我使用以下ARGS執行代碼

No method 'OrderBy' on type 'System.Linq.Queryable' is compatible with the supplied arguments. 

任何人都可以看到我在這裏失蹤?

編輯:

我試圖Expression.Call的另一個重載(),並得到了相同的相同的異常:

private static IQueryable<T> BuildQuery<T>(IQueryable<T> query, string methodName, Expression<Func<T, object>> propertyExpression)    
    { 
     var methodCall = Expression.Call(query.Expression, 
             methodName, 
             new[] {query.ElementType, property.Body.Type}, 
             new[] {propertyExpression}); 

     return query.Provider.CreateQuery<T>(methodCall); 
    } 
+0

你能告訴我'myPreExistingQuery'的結構嗎? – Femaref

+0

我在方法調用上面添加了它。 –

回答

11

既然你想讓你的屬性選擇器表達式來動態做出相應的電話,你必須爲它創建一個新的表達式。您目前無法使用提供的選擇器,因爲它目前輸入的內容爲Expression<Func<T, object>>,並且未返回您的特定類型Expression<Func<T, SomeType>>。你可能可以通過改變調用的類型參數來接受它,以接受object,但它不會像預期的那樣工作,因爲它將進行對象引用比較(並且您的LINQ提供程序可能會拒絕它)。

要重新建立您選擇的表達,你可以這樣做:

private static IQueryable<T> BuildQuery<T>(
    IQueryable<T> query, 
    string methodName, 
    Expression<Func<T, object>> property) 
{ 
    var typeArgs = new[] { query.ElementType, property.Body.Type }; 
    var delegateType = typeof(Func<,>).MakeGenericType(typeArgs); 
    var typedProperty = Expression.Lambda(delegateType, property.Body, property.Parameters); 

    var methodCall = Expression.Call(
     typeof(Queryable), 
     methodName, 
     typeArgs, 
     query.Expression, 
     typedProperty); 

    return query.Provider.CreateQuery<T>(methodCall); 
} 

一個不錯的選擇,這樣做將是使物業類型普通了。這樣,你將從一開始就得到一個適當的強類型選擇器。

private static IQueryable<TSource> BuildQuery<TSource, TProperty>(
    IQueryable<TSource> query, 
    string methodName, 
    Expression<Func<TSource, TProperty>> property) 
{ 
    var typeArguments = property.Type.GetGenericArguments(); 

    var methodCall = Expression.Call(
     typeof(Queryable), 
     methodName, 
     typeArguments, 
     query.Expression, 
     property); 

    return query.Provider.CreateQuery<TSource>(methodCall); 
} 
+0

完全是IT。你說的話很有道理。我粘貼了你的代碼,我的測試開始運行。感謝您的傑出答案。 –

+0

我已經添加了另一個重新創建表達式的方法。嚴格來說,沒有必要僅僅使源通用,你可以使這兩種類型通用。 –

+0

你的選擇對別人很好,它絕對可以讓你的功能更加靈活。但不幸的是,我不能使屬性類型通用,因爲直到運行時才知道類型。 –

相關問題