OrderBy on Queryable kann mit den "gelieferten Argumenten" nicht gefunden werden.

c# expression-trees linq

Frage

Ich habe eine Methode, mit der ich eine Liste sortieren kann:

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

Ich bekomme eine Ausnahme, wenn ich den Code mit den folgenden Argumenten ausführen:

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

Die Ausnahme ist:

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

Kann jemand sehen, was ich hier vermisse?

BEARBEITEN:

Ich habe eine weitere Überladung von Expression.Call () versucht und dieselbe Ausnahme erhalten:

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

Akzeptierte Antwort

Da der Eigenschaftsauswahlausdruck die entsprechenden Aufrufe dynamisch ausführen soll, müssen Sie einen neuen Ausdruck dafür erstellen. Sie können den bereitgestellten Selektor nicht verwenden, da er derzeit Expression<Func<T, object>> und nicht den spezifischen Typ Expression<Func<T, SomeType>> . Sie können es vielleicht kompilieren, indem Sie die Typargumente des Aufrufs zum Akzeptieren des object ändern, aber es wird nicht wie erwartet funktionieren, da es Objektreferenzvergleiche durchführt (und Ihr LINQ-Provider wird es möglicherweise trotzdem ablehnen).

Um Ihren Selektorausdruck neu zu erstellen, könnten Sie Folgendes tun:

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

Eine gute Alternative dazu wäre, den Eigenschaftentyp auch generisch zu machen. Auf diese Weise erhalten Sie von Anfang an einen entsprechend stark typisierten 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);
}


Lizenziert unter: CC-BY-SA with attribution
Nicht verbunden mit Stack Overflow
Lizenziert unter: CC-BY-SA with attribution
Nicht verbunden mit Stack Overflow