«

»

sie 26

Linq inside (IQueryable i Expression)

W ostatnim wpisie z tej serii chciałem przedstawić najważniejszą cechę linq, mianowicie niezależność od źródła danych.
Jak pisałem wcześniej Linq jest połączeniem 5 elementów:

We wszystkich poprzednich wpisach pracowaliśmy na kolekcjach, które przechowywane były w pamięci, jednak linq ma dużo większe możliwości obsługuje przeróżne źródła danych. Jak? Zacznijmy od przykładu. Załóżmy, że mamy bazę danych o takim prostym, schemacie.
schemat bazy danych

Jak widać w bazie mamy 4 proste tabelki, i chcemy przechowywać informację o klientach, zamówieniach i pozycjach na nich oraz o towarach. W tym przykładzie, skorzystam z Entity Framowrka, który umożliwia nam w łatwy sposób odczytanie danych z bazy. Załóżmy teraz, że chcemy wyświetlić wszystkich klientów, których imiona zaczynają się na R.

Wygląda to właściwie tak samo jak, wtedy gdy odwoływaliśmy się do obiektów w będących przechowywanych w pamięci a nie w bazie danych. Metoda where wygląda tak jakbyśmy mogli ją zastosować do zwykłej listy obiektów. To jest właśnie największa zaleta linq, niezależnie od źródła danych linq udostępnia nam te same metody operacji na danych. Ok, ale przecież aby uzyskać dane z bazy należy napisać zapytanie SQL. W tym przypadku mogło by to być takie zapytanie.

Linq musi, więc jakoś zamienić kod z poprzedniego przykładu na takie właśnie zapytanie, ale jak to właściwie wygląda? Zobaczmy co nam podpowiada intellisense visual studio gdy klikniemy w metodę Where.

linqdb2

Okazuje się, że nie działamy już na interfejsie IEnumerable tylko IQueryable oraz nie przekazujemy już Func tylko Expression<Func> chociaż na pierwszy rzut, oka wszystko wygląda podobnie.
Jaka jest różnica pomiędzy IEnumerable a IQueryable? Taka, że korzystając z pierwszego interfejsu działamy na obiektach w pamięci operacyjnej, a w drugim przypadku obiekty przechowywane są gdzieś w ‘jakimś’ źródle danych.
Co oznacza w takim razie tak groźnie wyglądająca deklaracja parametru Expression<Func<Clients,bool>>?
Linq, aby przetłumaczyć kod zawarty w metodzie where na (w tym przypadku) zapytanie sql, korzysta z tak zwanych drzew wyrażeń (expression trees). Parametr w metodzie where to właśnie element, takiego drzewa. Spróbuję przedstawić idę tego na prostym przykładzie. Spróbujmy zapisać te same delegaty raz za pomocą Expression a raz za pomocą Func i zobaczmy jaka jest różnica.

Oraz wynik działania tego programu.
linqdb3
Okazuje się, że wyrażenia zapisanego do Expression nie da się bezpośrednio wywołać należy najpierw wywołać metodę Compile. Gdy wypiszemy, takie wyrażenie na konsoli okazuje się, że wyświetli się nam kod wyrażenie lambda, która zapisaliśmy w zmiennej. Expression można więc rozumieć jako sposób zapisania delegata i służy on do tego aby takiego delegata zamienić w naszym przypadku na część kwerendy SQL.

Aby do źródła danych można było się odwołać korzystając z linq, musi zostać zaimplementowany interfejs IQueryable i IQueryProvider tylko gdzie? W naszym przypadku zapewnia to wszystko Entity Framework. Właśnie ten drugi interfejs jest pewnego rodzaju tłumaczem potrafi on przetłumaczyć to co zapisaliśmy w Expression na zapytanie SQL.

Podsumowując, linq jest abstrakcją nad źródłem danych czyli zbiorem metod, które możemy wykonywać niezależnie od tego czy odwołujemy się do relacyjnej bazy danych, bazy NoSQL, pliku XML czy WebServisu o ile oczywiście, ktoś był tak miły i dostarczył implementację IQueryable i IQueryProvider, aby to wszystko było możliwe;)

PS. Bazę wykorzystam w kolejnych wpisach…

2 Komentarze

  1. mariusz

    temat ciekawy, ale – niestety – ledwie “liźnięty” :)

    1. graf

      Zgadza się, na ten temat można pisać długo, a moim celem było zwięzłe pokazanie do czego służą te mechanizmy. Tak, aby ktoś kto nigdy nie używał linq miał pojęcie co siedzi w środku a nie koniecznie był w stanie napisać własnego prowidera. Jeśli jednak ktoś chce napisać własnego prowidera do linq polecam serie wpisów serie wpisów

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *

Możesz użyć tych znaczników i atrybutów HTMLa: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">

Current day month ye@r *