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
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ż.
Brak komentarzy:
Prześlij komentarz