2010-12-30 9 views
8

Natknąłem się na dziwne zachowanie LINQ do SQL - czy ktoś może rzucić trochę światła na to?LINQ do SQL: Ponowne użycie wyrażenia lambda

Chcę zdefiniować wyrażenie lambda i użyć go w mojej instrukcji LINQ. Poniższy kod działa poprawnie:

[...] 
Func<Table1, bool> lambda = x => x.Id > 1000; 
var result = dataContext.Table1s.Where(lambda); 
[...] 

Ale gdy próbuję używać wyrażenia lambda w oświadczeniu na powiązanej tabeli

[...] 
Func<Table1, bool> lambda = x => x.Id > 1000; 
var result = dataContext.Table2s.Where(x => x.Table1s.Any(lambda)); 
[...] 

uzyskać wyjątek:

Unsupported overload used for query operator 'Any'. 

Ale a tego nie otrzymuję: działa dobrze, gdy umieszczam swoją lambdę bezpośrednio w zapytaniu:

[...] 
var result = dataContext.Table2s.Where(x => x.Table1s.Any(y => y.Id > 1000)); 
[...] 

DLACZEGO ?!

Dzięki.

+0

Spróbuj użyć 'var lamda = x => x.Id> 1000;'. Nie wiem, czy to pomoże, ale może ... – Alxandr

+0

@Alxandr - to nie jest legalne. Wyrażenia lambdy mogą być kompilowane do 'Func <>' lub 'Expression >' iw twoim przykładzie kompilator nie będzie mógł powiedzieć, który z nich chcesz i spowoduje błąd. –

Odpowiedz

18

Okay, oto oferta: dataContext.Table1s jest typu IQueryable<T>. IQueryable<T> definiuje metody Where i Any, które pobierają predykat typu Expression<Func<T, bool>>. Okładka Expression<> ma kluczowe znaczenie, ponieważ pozwala LINQ na SQL na przetłumaczenie wyrażenia lambda na SQL i wykonanie go na serwerze bazy danych.

Jednak IQueryable<T> zawiera także IEnumerable<T>. IEnumerable<T> również definiuje metody Where i Any, ale wersja IEnumerable przyjmuje predykat typu Func<T, bool>. Ponieważ jest to funkcja skompilowana, a nie wyrażenie, nie można jej przetłumaczyć na SQL. W wyniku tego kodu ...

Func<Table1, bool> lambda = x => x.Id > 1000; 
var result = dataContext.Table1s.Where(lambda); 

... będzie ciągnąć każdy rekord z Table1s do pamięci, a następnie filtrować rekordy w pamięci. Działa, ale to naprawdę zła wiadomość, jeśli twój stół jest duży.

Func<Table1, bool> lambda = x => x.Id > 1000; 
var result = dataContext.Table2s.Where(x => x.Table1s.Any(lambda)); 

Ta wersja ma dwa wyrażenia lambda. Drugi, przekazywany bezpośrednio do Where, to Expression, który zawiera odniesienie do Func. Nie można ich mieszać, a komunikat o błędzie, który otrzymujesz, mówi, że wywołanie Any oczekuje Expression, ale przechodzisz pod numer Func.

var result = dataContext.Table2s.Where(x => x.Table1s.Any(y => y.Id > 1000)); 

W tej wersji, twój wewnętrzny lambda jest automatycznie przekształcane do Expression bo to jedyny wybór, jeśli chcesz, aby Twój kod zostać przekształcona SQL przez LINQ to SQL. W innych przypadkach wymuszasz na lambmie Func zamiast na Expression - w tym przypadku nie jesteś, więc to działa.

Jakie jest rozwiązanie? Jest to całkiem proste:

Expression<Func<Table1, bool>> lambda = x => x.Id > 1000; 
+1

+1 naprawdę dobre wyjaśnienie. –

+0

Dzięki. Niestety nie zostanie skompilowany, błąd kompilatora: Argument 2: nie można przekonwertować z 'System.Linq.Expressions.Expression >' na 'System.Func ' –

+1

Ten komunikat o błędzie wskazuje, że Wywołanie 'Where' lub' Any' na obiekcie, który implementuje IEnumerable, ale nie IQueryable - ponieważ wersje [IEnumerable] (http://msdn.microsoft.com/en-us/library/bb534803.aspx) oczekują Func, gdzie wersje [IQueryable] (http://msdn.microsoft.com/en-us/library/bb548547.aspx) oczekują wyrażenia. Jeśli nie zaakceptuje wyrażenia, obiekt może nie być IQueryable ... –

0

Czy Table1 odnosi się do tej samej przestrzeni nazw? W pierwszym przykładzie kwerendujesz przeciwko obiektom Table1, które są bezpośrednio pod dataContext, w drugim przykładzie kwerendy przeciwko obiektom Table1, która jest właściwością obiektów Table2, aw ostatnim przykładzie używasz anonimowy funkcja, która rozwiązuje problem.

chciałbym spojrzeć na typ z Table1 obiektów, które jest właściwością jest Table2 obiektu i porównać go do Table1 obiektu, który jest podłączony bezpośrednio do dataContext. Domyślam się, że różnią się one i twoje wyrażenie lambda używa typu obiektu, który jest podłączony do dataContext.