2013-09-27 2 views
6

Ich versuche, eine linq to Entity-Erweiterung-Methode zu schreiben, die eine Func verwendet, um eine Eigenschaft Id auszuwählen und es mit einer Liste von IDs zu vergleichen.Wie funktioniert ein Func in einem Ausdruck mit Linq zu Entity Framework?

Klassen

public class A 
{ 
    public int AId { get; set; } 
} 

public class B 
{ 
    public int BId { get; set; } 
} 

Erweiterungsmethode

public static IQueryable<T> WithId<T>(this IQueryable<T> entities, 
    Func<T, int> selector, IList<int> ids) 
    { 
     Expression<Func<T, bool>> expression = x => ids.Contains(selector(x)); 
     return entities.Where(expression); // error here (when evaluated) 
    } 

Methode aufrufen

var ids = new List<int> { 1, 2, 3 }; 
DbContext.EntityAs.WithId(e => e.AId, ids); 
DbContext.EntityBs.WithId(e => e.BId, ids); 

Das Problem, das ich bin Erfahren ist, dass es versucht, die Funktion aufzurufen, die in Entity Framework nicht zulässig ist.

Wie kann ich einen Eigenschaftsselektor (Func) verwenden, um die Abfrage auszuwerten?

+0

Der Umfang des Codes, den Sie in einer EF-Abfrage aufrufen können, ist dadurch begrenzt, dass er noch in SQL übersetzt werden muss. In Ihrem Fall kann EF nicht automatisch eine IList übersetzen. –

+0

Ich bin mir nicht sicher, dass Sie damit richtig liegen. DbContext.EntityAs.Where (e => ids.Contains (e.Id)) wird von EF korrekt übersetzt. Ich versuche nur, eine wiederverwendbare Funktion zu machen, so dass ich definieren kann, auf welcher Eigenschaft ausgewählt werden soll. – David

+0

Da EF weiß, wie zu tun 'wählen x wo x in (1,2,3)' im Fall von aufzählbar oder 'wählen x wo x in (wählen Sie y)' im Falle einer anderen Entity-Beziehung. In Ihrem Fall müsste EF etwas wie 'select x mit x in (wählen Sie y wo F (y) in (F (1), F (2), ...))' '. Während es möglich ist, dies manuell zu tun, unterstützt EF einfach nicht die Groß-/Kleinschreibung * noch * –

Antwort

15

Sie müssen eine Expression<Func<T, int>> anstelle einer Func<T, int> übergeben und den vollständigen Ausdruck selbst aufbauen. Dadurch wird der Trick:

public static IQueryable<T> WithId<T>(this IQueryable<T> entities, 
    Expression<Func<T, int>> propertySelector, ICollection<int> ids) 
{ 
    var property = 
     (PropertyInfo)((MemberExpression)propertySelector.Body).Member; 

    ParameterExpression parameter = Expression.Parameter(typeof(T)); 

    var expression = Expression.Lambda<Func<T, bool>>(
     Expression.Call(
      Expression.Constant(ids), 
      typeof(ICollection<int>).GetMethod("Contains"), 
      Expression.Property(parameter, property)), 
     parameter); 

    return entities.Where(expression); 
} 

Wenn Sie versuchen, Ihren Code trocken zu halten, wenn sie mit Ihrem O/RM arbeiten, können Sie oft mit Ausdruck Bäume müssen Geige. Hier ist another fun example.

+0

Fantastisch. Ich habe gerade experimentiert, wie man den Ausdrucksbaum erstellt von http://blogs.msdn.com/b/miah/archive/2009/02/06/dynamic-expression-trees.aspx und http://stackoverflow.com/questions/ 820896/listobject-contains-expression-tree, konnte aber nicht herausfinden, wie die Sammlung/Liste erstellt wird. Vielen Dank! – David

+3

@DavidLiddle: Ich gebe Ihnen ein kleines Geheimnis: Ich schreibe einfach die LINQ-Abfrage, kompiliere und öffne Reflector, um zu sehen, was der C# -Compiler erzeugt. Sie können diese Informationen auch im Debugger sehen, aber Reflector ist viel einfacher. – Steven

+0

Können Sie ein Beispiel geben "einfach die LINQ-Abfrage schreiben". Mit ILSpy sehe ich genau die LINQ-Abfrage, die ich geschrieben habe! – David

Verwandte Themen