Pokazywanie postów oznaczonych etykietą linq. Pokaż wszystkie posty
Pokazywanie postów oznaczonych etykietą linq. Pokaż wszystkie posty

niedziela, 26 października 2008

LINQ Decomposed

LINQ (Language Integrated Query), to bardzo przydatne rozszerzenie języków z platformy .NET o "generyczne operacje na kolekcjach". Chodzi mniej więcej o to, że wprowadzono do języka programowania elementy języka zapytań (formuły LINQ). Zamiast jes skrupulatnie opisywać odsyłam w tym momencie do msdn:linq.

Wygląda całkiem fajnie - proste operacja agregowania, selekcji i projekcji w samym języku i to całkiem elegancko, szybko i jeszcze type-safe - no tak przecież powinno być już dawno.
Na tym niespodzianki się jednak nie kończą, okazuje się bowiem, że jest to również pewien rodzaj vendor-dependent middleware! Formuła LINQ może zostać przetłumaczona na zapytanie do bazy danych. Jak to możliwe? Scenariusz mamy podzielony na dwie części:

Cz.1
Sama formuła LINQ w czasie kompilacji jest tłumaczona na natywne zapytanie, czyli zapytanie zadane obiektem (coś na kształt db4o:tutorial). W trakcie kompilacji formuły zostaną przepisane mniej więcej w ten sposób.


Dim formula_query = From emp In db_context.Employees _
Where emp.Country = "USA" _
Select emp.FirstName, emp.LastName

Dim native_query = db_context.Employees. _
Where(Function(empl) empl.Country = "USA"). _
Select(Function(empl) New With {.FirstName = empl.FirstName, .LastName = empl.LastName})


Obiektem zapytania jest w przypadku pracy z samymi kolekcjami jest IEnumerable a w przypadku źródła danych jej specjalizacji IQueryable (a właściwie specjalizacji IQueryable, dostarczonej przez dostawce sterownika)

Cz. 2
Zapytanie jest realizowane leniwie czyli dopiero wtedy gdy poprosimy obiekt zapytania o enumerator dzieje się cała akcja, która opiera się na mechanizmie przepisywania drzew składniowych. W trakcie wykonania silnik dostawcy dostaje AST, który sobie jakoś tam analizuje i to co może zrobić to np. wygenerować zapytanie sql w postaci stringa i posłać go do bazy, odczytać a potem utworzyć kolekcję tych obiektów i zwrócić programiście (no ale oczywiście może o wiele wiele więcej). Napisałem prosty programik na podstawe tego blog:msdn:linq (copy-paste programming), który zamiast robić cokolwiek, to po prostu wypisuje info o każdym węźle. Troche bardziej user-friendly wyjaśnienie jest tutaj codeguru.


var q = from el in context.Data where el +1 > 5 select el;


Call Data.Where(el => ((el + 1) > 5))
Constant Data
Quote el => ((el + 1) > 5)
Lambda el => ((el + 1) > 5)
GreaterThan ((el + 1) > 5)
Add (el + 1)
Parameter el
Constant 1
Constant 5


Pare przykładów.

Dim q = (From order In db_context.Orders Join detail In db_context.Order_Details _
On detail.OrderID Equals order.OrderID _
Select order.ShipName, detail.Quantity).Take(10)

przepisane jest na zapytanie:

SELECT TOP (10) [t0].[ShipName], [t1].[Quantity]
FROM [dbo].[Orders] AS [t0]
INNER JOIN [dbo].[Order Details] AS [t1] ON [t0].[OrderID] = [t1].[OrderID]

późne wiązanie

Dim q = From el In db_context.Employees _
Select el.FirstName, el.LastName, el.Country, el.City
q = From el In q Where el.Country = "USA"
q = From el In q Where el.City = "Seattle"

przepisane jest na zapytanie:

SELECT [t0].[FirstName], [t0].[LastName], [t0].[Country], [t0].[City]
FROM [dbo].[Employees] AS [t0]
WHERE ([t0].[City] = @p0) AND ([t0].[Country] = @p1)

Warto zauważyć, że w przypadku pracy z kolekcjami silnik jest już w samym standardzie LINQ i zachowuje się dosyć inteligentnie wykorzystując yield. Możemy sobie zdefiniować mniej lub bardziej skomplikowaną "transformacje" (tak to nazwijmy) kolekcji w postaci obiektu, a wykonanie jej nie będzie tworzyło stosów pośrednich podczas obróbki, co znaczy tyle, żedla każdego elementu wykona wszystkie kroki transformacji od początku do końca (można powiedzieć, że tworzymy wrażenie składania funkcji i foldowania.

Jeszcze na koniec pare wniosków i faktów:
  • Wygląda to wszystko całkiem fajnie, bo dostajemy bardzo użyteczne rozszerzenie języka programowania. Osobiście bardzo często to wykorzystuje i co ciekawsze nie jako middleware, ale właśnie do pracy z kolekcjami i wcześniej gdy naiwnie pisałem jakieś tam zapętlające się pętle tak teraz dostaje bardzo czytelny i efektywny kod.
  • LINQ nie jest córą Microsoftu, tylko urodziła się jako koncepcja akademicka, która przechodzi tez do innych języków ruby:rinq, php, js:jslinq, i nawet rozaważa się aby do JAVA wstawić jakieś LINQ, zwłaszcza gdy mają zostać dodane domknięcia (closures) w JAVA 1.7
  • Zarzutem dla LINQ jest kiepska wydajność. Jako, że zapytanie jest generowanie w czasie wykonania w istocie robi to pewien narzut, który z powodzeniem można było zrobić w czasie projektowania, no ale ze swego doświadczenia mogę stwierdzić, że dopóki nie będzie się uzywało formuły linq w pętli nie stanowi to aż tak dużego narzutu
  • Koncepcja linq doskonale nadaje się do rozszerzenia jej do PLINQ (Parallel LINQ) o tym będzie później
  • Microsoft zrobił coś co robił już bardzo często, czyli stworzył naprawdę ładny kawałek oprogramowania, a potem zaprezentował jego jeden mały fragment. Warto zauważyć , że przecież w czasie wykonania dostajemy AST pewnych struktur języka, możemy je analizować i jeszcze dodatkowo mieć wypływ na to co wypluwają - no to przecież nie trywialny mechanizm, a jak wiemy może być całkiem użyteczny również.