2013-08-20 7 views
11

Ich versuche, eine Filtermethode für Entity Framework-Liste zu erstellen und besser zu verstehen, die Expression<Func<...Entity Framework Filter „Expression <Func <T, bool>>“

ich eine Testfunktion wie diese haben.

public IQueryable<T> Filter<T>(IEnumerable<T> src, Expression<Func<T, bool>> pred) 
{ 
    return src.AsQueryable().Where(pred); 
} 

und wenn ich dies tun:

context.Table.Filter(e => e.ID < 500); 

oder dies:

context.Table.Filter(e => e.SubTable.Where(et => et.ID < 500).Count() > 0 && e.ID < 500); 

alles gut funktioniert.

Aber wenn ich dies tun:

context.Table.Filter(e => e.SubTable.Filter(et => et.ID < 500).Count() > 0 && e.ID < 500); 

oder dies:

context.Table.Where(e => e.SubTable.Filter(et => et.ID < 500).Count() > 0 && e.ID < 500); 

Ich erhalte einen Fehler. LINQ to Entities does not recognize the method ...Filter...

Warum funktioniert es in einem Fall und nicht im Addierer? Was sollte ich im Filter ändern, damit es mit verwandten Tabellen funktioniert? Ich bevorzuge es, mich von anderen externen Bibliotheken fern zu halten, denn ich möchte lernen, wie es funktioniert, und es in Zukunft in jedem Szenario verwenden können.

In den ersten beiden Fällen wird der Filter in der Datenbank korrekt ausgeführt.

Antwort

20

Jon und Tim haben bereits erklärt, warum es nicht funktioniert.

Angenommen, der Filtercode in Filter ist nicht trivial, Sie könnten Filter ändern, so dass es einen Ausdruck zurückgibt, den EF übersetzen kann.

Nehmen wir an, Sie diesen Code haben:

context.Table.Where(x => x.Name.Length > 500); 

Sie können nun ein Verfahren mit den Renditen diesen Ausdruck erstellen:

context.Table.Where(FilterByNameLength(500)); 

The:

Expression<Func<YourEntity, bool>> FilterByNameLength(int length) 
{ 
    return x => x.Name.Length > length; 
} 

Verwendung so sein würde Ausdruck Sie bauen innerhalb FilterByNameLength kann beliebig komplex sein, solange Sie passieren konnten es direkt an Where.

+0

durch viel Lesen und Versuch und Irrtum Ich vermutete, dass dies der einzige Weg war. wird es Fälle wie context.Table.Where (e => eTable.Any (MyFilter())); –

+0

@ Pedro.The.Kid: Nein, wird es nicht. Das Ergebnis dieser Methode muss an das äußerste 'Where' übergeben werden, da jeder Ausdruck, den Sie an das äußerste' Where' übergeben, nicht wirklich ausgeführt, sondern interpretiert wird. –

+0

Ich habe einen Test gemacht und das funktioniert var filter = MyFilter(); context.Table.Where (e => eTable.AsQueryable(). Beliebig (Filter)); –

4

Warum funktioniert es in einem Fall und nicht im Addierer?

Weil EF nicht wirklich über Ihre Filter Methode "weiß". Es hat kein Verständnis dafür, was es zu tun hat, also weiß es nicht, wie es in SQL übersetzt werden soll. Vergleichen Sie das mit Where usw., die es tut verstehen.

Die Version, wo Sie es nennen arbeitet direkt auf den ersten Tabellen, weil auf diese Weise einen Anruf zu Filter enthält, nicht mit einem Ausdruck Baum am Ende - es Filter nur Anrufe direkt, was wiederum tut build up eine Abfrage ... aber eine, die EF versteht.

Ich wäre sehr überrascht, wenn Sie einen Weg erarbeiten könnten Ihre Filter Methode des Erhaltens innerhalb einer EF-Abfrage zu arbeiten ... aber Sie haben bereits gesagt, dass ohnehin Where Werke mit, warum so Filter überhaupt benutzen? Ich würde die Where Version verwenden - oder besser noch, verwenden Sie die Any Überlastung, die ein Prädikat nimmt:

context.Table.Filter(e => e.SubTable.Any(et => et.ID < 500) && e.ID < 500); 
+0

Gibt es irgendeinen Weg, den es geändert werden kann, um mit einem Ausdruckbaum zu enden, der Dosis nicht den Methodennamen einschließt? –

+0

@ Pedro.The.Kid: Nicht leicht. Sie könnten eine Methode schreiben, die die Ausdrucksbaumstruktur umschreibt und eine neue Abfrage auf dieser Basis erstellt, aber es ist nicht klar, warum Sie das tun möchten, und es wäre schwierig. –

+0

wie für die Verwendung von wo es einfach für den Test in der realen Umgebung wird es viel komplexer sein. –

6

Es ist nützlich, zu verstehen, den Unterschied zwischen Expression<Func<>> und Func<>.

Ein Expressione => e.ID < 500 speichert die Informationen über diesen Ausdruck: dass ein Te es, dass Sie die Eigenschaft ID zugreifst, die < Operator mit dem 500int Wert aufrufen.Wenn EF sich das ansieht, könnte es sich in etwas wie [SomeTable].[ID] < 500 verwandeln.

A Funce => e.ID < 500 ist ein Verfahren äquivalent zu:

static bool MyMethod(T e) { return e.ID < 500; } 

es als IL-Code kompiliert wird, das dies tut; Es ist nicht dazu gedacht, in eine SQL-Abfrage oder irgendetwas anderes "rekonstituiert" zu werden, sondern nur ausgeführt zu werden.

Wenn EF Ihre Expression nimmt, muss es jedes Stück davon verstehen, weil es das verwendet, um eine SQL-Abfrage zu erstellen. Es ist programmiert zu wissen, was die existierende Where Methode bedeutet. Es weiß nicht, was Ihre Filter Methode bedeutet, obwohl es eine triviale Methode ist, so gibt es einfach auf.

Verwandte Themen