2011-07-31 22 views
8

Mam metodę że chcę użyć, aby posortować listę:Nie można znaleźć OrderBy na Queryable za pomocą "dostarczonych argumentów".

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); 
    } 

uzyskać wyjątek, kiedy wykonać kod stosując następujące argumenty:

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

Wyjątkiem jest:

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

Czy ktoś może zobaczyć, czego tu brakuje?

EDIT:

Próbowałem innego przeciążenie Expression.Call() i dostał ten sam ten sam wyjątek:

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

czy możesz pokazać mi konstrukcję 'myPreExistingQuery' proszę? – Femaref

+0

Dodałem go powyżej wywołania metody. –

Odpowiedz

11

Skoro chcesz, aby wyrażenie selektor własność aby dokonać odpowiednich połączeń dynamicznie, musisz utworzyć dla niego nowe wyrażenie. Nie można użyć dostarczonego selektora tak, jak jest obecnie wpisany Expression<Func<T, object>> i nie zwraca konkretnego typu Expression<Func<T, SomeType>>. Możesz go skompilować, zmieniając argumenty typu wywołania, aby zaakceptować object, ale nie będzie działał tak, jak powinien, ponieważ będzie porównywanie odniesień do obiektu (a twój dostawca LINQ może i tak go odrzucić).

Aby odtworzyć swój wyraz wyboru, można to zrobić:

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); 
} 

miłą alternatywą do robienia tego byłoby zrobić rodzaj nieruchomość generic też. W ten sposób od samego początku otrzymasz odpowiednio silnie zaznaczony selektor.

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

To było dokładnie to. To, co powiedziałeś, ma sens. Wkleiłem kod i uruchomiłem testy. Dzięki za twoją wspaniałą odpowiedź. –

+0

Dodałem kolejną możliwość odtworzenia wyrażenia. Nie jest koniecznie tylko generowanie kodu źródłowego, możesz uczynić oba typy rodzajowymi. –

+0

Twoja alternatywa będzie dobra dla innych i zdecydowanie sprawi, że funkcja będzie bardziej elastyczna. Ale niestety nie mogę utworzyć typ właściwości rodzaj, ponieważ nie znam typu do czasu wykonywania. –

Powiązane problemy