2013-09-27 13 views
6

Próbuję napisać linq do metody rozszerzenia encji, która ma Func, aby wybrać identyfikator nieruchomości i porównać go z listą identyfikatorów.Jak używać Func w wyrażeniu z Linq do Entity Framework?

Klasy

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

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

Extension Metoda

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

wywołanie metody

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

Problem Jestem doświadczanie polega na tym, że próbuje wywołać funkcję, która nie jest dozwolona w Entity Framework.

Jak mogę użyć selektora właściwości (Func) do oceny zapytania?

+0

Zakres kodu, który można wywołać w zapytaniu EF, jest ograniczony przez fakt, że nadal wymaga przetłumaczenia w SQL. W twoim przypadku EF nie wie, jak tłumaczyć ILista automatycznie. –

+0

Nie jestem pewien, czy masz rację. DbContext.EntityAs.Where (e => ids.Contains (e.Id)) jest poprawnie przetłumaczony przez EF. Po prostu próbuję utworzyć funkcję wielokrotnego użytku, aby móc określić, którą właściwość wybrać. – David

+0

Ponieważ EF wie, jak to zrobić: "wybierz x, gdzie x in (1,2,3)' w przypadku przeliczalnych lub 'wybierz x, gdzie x in (wybierz y)' w przypadku innej relacji encji. W twoim przypadku EF musiałby skompilować coś w stylu 'wybierz x gdzie x in (wybierz y gdzie F (y) w (F (1), F (2), ...))'. Chociaż można to zrobić ręcznie EF nie obsługuje tylko przypadku * jeszcze * –

Odpowiedz

15

Będziesz musiał podać Expression<Func<T, int>> zamiast Func<T, int> i samodzielnie zbudować całe wyrażenie. Będzie to rade:

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

Kiedy starać się utrzymać swój kod DRY podczas pracy z O/RM, będzie często trzeba bawić z drzew ekspresyjnych. Oto another fun example.

+0

Fantastyczne. Eksperymentowałem, jak zbudować drzewo wyrażeń z http://blogs.msdn.com/b/miah/archive/2009/02/06/dynamic-expression-trees.aspx i http://stackoverflow.com/questions/ 820896/listobject-zawiera-wyrażenie-drzewo, ale nie może dowiedzieć się, jak zbudować Collection/List zawiera. Dziękuję Ci! – David

+3

@DavidLiddle: Powiem ci trochę w tajemnicy: po prostu piszę zapytanie LINQ, kompiluję i otwieram Reflector, żeby zobaczyć, co generuje kompilator C#. Możesz również zobaczyć te informacje w debugerze, ale Reflector jest znacznie łatwiejszy. – Steven

+0

Czy możesz podać przykład "po prostu napisz zapytanie LINQ". Używając ILSpy widzę właśnie dokładne zapytanie LINQ, które napisałem! – David

Powiązane problemy