Erstellen Sie den Ausdrucksbaum für LINQ mithilfe der Liste . Enthält Methode

c# entity-framework expression-trees linq

Frage

Problem

Ich arbeite daran, einige LINQ Abfragen für mehrere Berichte in unserer Webanwendung IQueryable strukturieren, und ich versuche, einige doppelte Abfrageprädikate in ihre eigenen IQueryable Erweiterungsmethoden zu verschieben, damit wir sie für diese Berichte und Berichte in Zukunft wiederverwenden können. Wie Sie wahrscheinlich ableiten können, habe ich das Prädikat für Gruppen bereits überarbeitet, aber das Prädikat für Codes gibt mir Probleme. Dies ist ein Beispiel für eine der bisherigen Berichtsmethoden:

DAL-Methode:

public List<Entities.QueryView> GetQueryView(Filter filter)
{
    using (var context = CreateObjectContext())
    {
        return (from o in context.QueryViews
                    where (!filter.FromDate.HasValue || o.RepairDate >= EntityFunctions.TruncateTime(filter.FromDate))
                    && (!filter.ToDate.HasValue || o.RepairDate <= EntityFunctions.TruncateTime(filter.ToDate))
                    select o)
                .WithCode(filter)
                .InGroup(filter)
                .ToList();
    }
}

IQueryable Erweiterung:

public static IQueryable<T> WithCode<T>(this IQueryable<T> query, Filter filter)
{
    List<string> codes = DAL.GetCodesByCategory(filter.CodeCategories);

    if (codes.Count > 0)
        return query.Where(Predicates.FilterByCode<T>(codes));

    return query;
}

Prädikat:

public static Expression<Func<T, List<string>, bool>> FilterByCode<T>(List<string> codes)
{
    // Method info for List<string>.Contains(code).
    var methodInfo = typeof(List<string>).GetMethod("Contains", new Type[] { typeof(string) });

    // List of codes to call .Contains() against.
    var instance = Expression.Variable(typeof(List<string>), "codes");

    var param = Expression.Parameter(typeof(T), "j");
    var left = Expression.Property(param, "Code");
    var expr = Expression.Call(instance, methodInfo, Expression.Property(param, "Code"));

    // j => codes.Contains(j.Code)
    return Expression.Lambda<Func<T, List<string>, bool>>(expr, new ParameterExpression[] { param, instance });
}

Das Problem, das ich habe, ist das Queryable.Where nicht akzeptiert eine Art von Expression<Func<T, List<string>, bool> . Die einzige Art, wie ich dieses dynamische Prädikat dynamisch erstellen kann, ist die Verwendung von zwei Parametern. Das ist der Teil, der mich wirklich stopft.

Was ich nicht verstehe, ist die folgende Methode funktioniert. Ich kann den exakten Lambda-Ausdruck übergeben, den ich dynamisch erstellen möchte, und er filtert meine Daten korrekt.

public List<Entities.QueryView> GetQueryView(Filter filter)
{
    // Get the codes here.
    List<string> codes = DAL.GetCodesByCategory(filter.CodeCategories);

    using (var context = CreateObjectContext())
    {
        return (from o in context.QueryViews
                    where (!filter.FromDate.HasValue || o.RepairDate >= EntityFunctions.TruncateTime(filter.FromDate))
                    && (!filter.ToDate.HasValue || o.RepairDate <= EntityFunctions.TruncateTime(filter.ToDate))
                    select o)
                .Where(p => codes.Contains(p.Code)) // This works fine.
                //.WithCode(filter)
                .InGroup(filter)
                .ToList();

        }

    }

Fragen

  1. Kann ich meine eigene Queryable.Where Überladung implementieren? Wenn ja, ist das überhaupt möglich?
  2. Wenn eine Überladung nicht möglich ist, gibt es eine Möglichkeit, das Prädikat p => codes.Contains(p.Code) dynamisch p => codes.Contains(p.Code) ohne zwei Parameter zu verwenden?
  3. Gibt es einen einfacheren Weg dies zu tun? Ich fühle mich, als würde ich etwas vermissen.

Akzeptierte Antwort

  1. Sie können Ihre eigene Erweiterungsmethode erstellen, sie mit Where IQueryable<T> , ein IQueryable<T> akzeptieren, ein IQueryable<T> und es andernfalls die Form der LINQ-Methoden emulieren. Es wäre keine LINQ Methode sein, aber es würde so aussehen. Ich würde Sie davon abhalten, eine solche Methode zu schreiben, einfach weil es wahrscheinlich andere verwirren würde; Wenn Sie eine neue Erweiterungsmethode erstellen möchten, verwenden Sie einen Namen, der in LINQ nicht verwendet wird, um Verwechslungen zu vermeiden. Kurz gesagt, das tun , was Sie jetzt tun, neue Erweiterungen erstellen , ohne sie tatsächlich zu benennen Where . Wenn Sie wollte wirklich ein nennen Where wäre nichts hindert Sie daran.

  2. Sicher, benutze einfach ein Lambda:

    public static Expression<Func<T, bool>> FilterByCode<T>(List<string> codes)
        where T : ICoded //some interface with a `Code` field
    {
        return p => codes.Contains(p.Code);
    }
    

    Wenn Sie Ihre Entitäten wirklich nicht in die Lage versetzen können, eine Schnittstelle zu implementieren (Tipp: Sie können das mit ziemlicher Sicherheit), dann würde der Code mit dem Code identisch sein, den Sie haben, aber die Liste als Konstante anstelle eines neuen Parameters verwenden:

    public static Expression<Func<T, bool>> FilterByCode<T>(List<string> codes)
    {
        var methodInfo = typeof(List<string>).GetMethod("Contains", 
            new Type[] { typeof(string) });
    
        var list = Expression.Constant(codes);
    
        var param = Expression.Parameter(typeof(T), "j");
        var value = Expression.Property(param, "Code");
        var body = Expression.Call(list, methodInfo, value);
    
        // j => codes.Contains(j.Code)
        return Expression.Lambda<Func<T, bool>>(body, param);
    }
    

    Ich würde den Gebrauch der früheren Methode stark ermutigen; Diese Methode verliert die Sicherheit des statischen Typs und ist komplexer und daher schwerer zu warten.

    Ein weiterer Hinweis, der Kommentar, den Sie in Ihrem Code haben: // j => codes.Contains(j.Code) ist nicht korrekt. Was das Lambda tatsächlich aussieht ist: (j, codes) => codes.Contains(j.Code); Das ist eigentlich merklich anders.

  3. Siehe die erste Hälfte von # 2.



Lizenziert unter: CC-BY-SA with attribution
Nicht verbunden mit Stack Overflow
Ist diese KB legal? Ja, lerne warum
Lizenziert unter: CC-BY-SA with attribution
Nicht verbunden mit Stack Overflow
Ist diese KB legal? Ja, lerne warum