2015-07-14 29 views
9

Mam dwie metody rozszerzenie jak tenLINQ to Entities nie obsługuje metody rozszerzenia?

public static IQueryable<T> CurrentVersion(this IQueryable<T> queryable, DateTime date) 
    { 
     return queryable.Where(p => p.CreationDate>date); 
    } 

    public static IEnumerable<T> CurrentVersion(this IEnumerable<T> queryable, DateTime date) 
    { 
     return queryable.Where(p => p.CreationDate>date); 
    } 

mój model jest

public class Group { 
    .. 
    ICollection<GroupMembers> GroupMembers { get; set; } 
} 

Podczas korzystania z metody wydłużania się tego zapytania wszystko jest ok

var q = Db.Groups.CurrentVersion(); 
var result = q.ToList(); 

Ale kiedy go używać w przepływie zapytania pojawia się błąd

var q = Db.Groups.SelectMany(p => p.GroupMembers.AsQueryable().CurrentVersion(date)); 

OR 

var q = Db.Groups.SelectMany(p => p.GroupMembers.AsEnumerable().CurrentVersion(date)); 

var result = q.ToList(); // Here I get error 

Błąd:

LINQ to Entities does not recognize the method 'System.Linq.IQueryable 1[..](System.Linq.IQueryable 1[..., System.DateTime)' method, and this method cannot be translated into a store expression.

Teraz mam dwa pytania:

  1. google ten błąd i znalazł wiele problemów, tak samo jak moje pytanie w stackoverflow. Wszystkie odpowiedzi brzmiały "Od Linq do Entities nie można przekonwertować tej metody rozszerzenia na zapytanie SQL". Teraz byłbym wdzięczny, gdyby ktoś mi pomógł wiedzieć, dlaczego moje pierwsze zapytanie nie powoduje żadnych błędów?

  2. Jak zmienić metodę rozszerzenia, która może zostać rozpoznana przez Linq-to-Entities?

Odpowiedz

6

Zapytanie Linq należy przetłumaczyć na sql. Kiedy masz rozszerzenie CurrentVersion nazwie inline tak:

Db.Groups.CurrentVersion(); 

następnie EF tylko wywołuje metodę CurrentVersion, dostaje wynikające IQueryable obiektu i przekształca się do zapytania. Z drugiej strony w przypadku tego zapytania:

var q = Db.Groups.SelectMany(p => p.GroupMembers.AsQueryable().CurrentVersion(date)); 

Wewnętrzne wyrażenie w SelectMany nie może być nigdy wywołane w kodzie! Jest przeznaczony do przetłumaczenia na sql. Jest więc traktowany jako obiekt Expression i jest następnie przetwarzany, ale w twoim przypadku zawiera Wywołanie Wywołania, ponieważ wywołujesz metodę, ale nie można tego przetłumaczyć na sql z oczywistych powodów. Tak więc od wewnątrz paramerze SelectMany lambda nie można wywołać żadnej metody, musisz podać tam odpowiednie wyrażenie. Najcenniejszą rzeczą dostarczoną przez metodę CurrentVersion jest filtrowanie wyrażenia. Zmienić metodę tak:

public static Expression<T, bool> CurrentVersion(DateTime date) 
{ 
    return p => p.CreationDate > date; 
} 

używać go tak:

var q = Db.Groups.Where(ExpressionsHelper.CurrentVersion(date)); 
... 
Expression<T, bool> filterExpression = ExpressionsHelper.CurrentVersion(date); 
Db.Groups.SelectMany(p => p.GroupMembers.AsQueryable().Where(filterExpression)); 

Jeśli zażyczyć można stil mają swoją metodę rozszerzenia filtrowania podział logiki z nowej metody:

public static IQueryable<T> CurrentVersion(this IQueryable<T> queryable, DateTime date) 
{ 
    return queryable.Where(ExpressionsHelper.CurrentVersion(date)); 
} 
+0

Myślę, że twoje rozszerzenie nie różni się od mojej metody rozszerzenia, ponieważ queryable.Where (ExpressionsHelper.CurrentVersion (data)) jest równe z queryable.Where (p => p.CreationDate> date); –

+0

Rzeczywiście queryable.Where (ExpressionsHelper.CurrentVersion (date)) powinny działać jako queryable.Where (p => p.CreationDate> date); ale to był cel, prawda? Zmieniłem twoją metodę, aby logika filtrowania mogła zostać ponownie użyta, jak chcesz. Różnica polega na tym, że proponowana przeze mnie metoda działa, a EF może ją sparsować. – mr100

+0

Ale mojej wersji metody rozszerzenia CurrentVersion nie można zastosować do zagnieżdżonego zapytania! Możesz go używać tylko z prostymi zapytaniami, takimi jak Db.Groups.Obecna wersja(); Dla złożonych musisz użyć Db.Groups.SelectMany (p => p.GroupMembers.AsQueryable(). Gdzie (ExpressionsHelper.CurrentVersion (date))); Metoda rozszerzenia jest prezentowana tylko jako próbka - jeśli chcesz ją mieć, możesz ją mieć bez duplikowania kodu. – mr100

Powiązane problemy