Wie man einen einfachen Eigenschaftsselektorausdruck in ef6 erstellt

c# entity-framework expression-trees linq

Frage

Wie kann ich einen Eigenschaftsselektor für Entity-Framework so erstellen?

public static List<T> StandardSearchAlgorithm<T>(this IQueryable<T> queryable, Func<T, string> property, string query)
{

    return queryable.Where(e => property(e).ToLower().IndexOf(query) > -1).ToList();

}

Ich möchte, dass der Anrufcode so sauber und einfach ist:

var usernameResults = _db.Users.StandardSearchAlgorithm(u => u.Username, query);

Ich erhalte eine "Der LINQ-Ausdruck-Knotentyp 'Invoke' wird in LINQ to Entities nicht unterstützt." Error. Ich kann nicht herausfinden, wie man den Ausdruck aufbauen kann.

AKTUALISIEREN:

Basierend auf der Antwort von MBoros ist hier der Code, mit dem ich gelandet bin. Es funktioniert großartig.

Der Schlüssel zu Ausdrucksbäumen ist zu verstehen, dass Ausdrucksbäume alles darauf abzielen, das, was Sie normalerweise in Code schreiben (wie "e => e.Username.IndexOf (query)") in eine Reihe von Objekten aufzuteilen: "e" erhält ein eigenes Objekt "Username" ein eigenes Objekt, "IndexOf ()" ein eigenes Objekt, die "query" -Konstante ein eigenes Objekt und so weiter. Der zweite Schlüssel besteht darin zu wissen, dass Sie eine Reihe von statischen Methoden für die Expression Klasse verwenden können, um verschiedene Arten dieser Objekte zu erstellen, wie unten gezeigt.

    PropertyInfo pinfo = (PropertyInfo)((MemberExpression)property.Body).Member;
    ParameterExpression parameter = Expression.Parameter(typeof(T), "e");
    MemberExpression accessor = Expression.Property(parameter, pinfo);
    ConstantExpression queryString = Expression.Constant(query, typeof(string));
    ConstantExpression minusOne = Expression.Constant(-1, typeof(int));
    MethodInfo indexOfInfo = typeof(string).GetMethod("IndexOf", new[] { typeof(string) }); // easiest way to do this
    Expression indexOf = Expression.Call(accessor, indexOfInfo, queryString);
    Expression expression = Expression.GreaterThan(indexOf, minusOne);
    Expression<Func<T, bool>> predicate = Expression.Lambda<Func<T, bool>>(expression, parameter);
    //return predicate.Body.ToString(); // returns "e => e.Username.IndexOf(query) > -1" which is exactly what we want.

    var results = queryable.Where(predicate).ToList();
    return results;

Jetzt habe ich ein echtes Problem, aber ich werde es in einer separaten Frage fragen. Meine echte Abfrage sieht so aus:

public static List<T> StandardSearchAlgorithm<T>(this IQueryable<T> queryable, Func<T, string> property, string query)
{

    return queryable.Where(e => property(e).IndexOf(query) > -1).Select(e=> new { Priority = property(e).IndexOf(query), Entity = e } ).ToList();

}

Also muss ich einen Ausdruck erstellen, der einen anonymen Typ zurückgibt !! Oder selbst wenn ich eine Klasse zur Hilfe erstelle, muss ich einen Ausdruck schreiben, der ein neues Objekt zurückgibt. Aber ich werde das in eine separate Frage aufnehmen.

Akzeptierte Antwort

Sie können CLR-Delegaten nicht einfach in SQL aufrufen. Sie können jedoch den Eigenschaftenselektor als Ausdrucksbaum übergeben. Ihre Signatur wäre also:

public static List<T> StandardSearchAlgorithm<T>(this IQueryable<T> queryable, Expression<Func<T, string>> property, string query)

Calling würde gleich aussehen. Aber jetzt, wo Sie einen Ausdruck in der Hand haben, können Sie sich diese Antwort ansehen: Übergeben Sie den Ausdrucksparameter als Argument an einen anderen Ausdruck. Er gibt Ihnen die Werkzeuge, um einen Ausdrucksbaum einfach in einen anderen zu bringen. In Ihrem Fall würde es so aussehen:

Expression<Func<T, bool>> predicate = e => property.AsQuote()(e).Contains(query);
predicate = predicate.ResolveQuotes();
return queryable.Where(predicate).ToList();

Sobald Sie dort sind, haben Sie immer noch die .ToLower (). Contains () -Aufrufe (verwenden Sie .Contains anstelle von .IndexOf ()> 1). Das ist wirklich schwierig. Normalerweise verwendet die Datenbank ihre Standardsortierung. Wenn sie also auf CI gesetzt wird (Groß- und Kleinschreibung wird nicht beachtet), wird der Vergleich durchgeführt. Wenn Sie keine Einschränkungen haben und die db-Sortierung anpassen können, würde ich dafür gehen. In diesem Fall können Sie den Aufruf von .ToLower () weglassen. Andernfalls überprüfen Sie diese Anser: https://stackoverflow.com/a/2433217/280562



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