niedziela, 10 lutego 2013

70-516 LINQ

O LINQu pisałem w wielu postach. Nie zamierzam więc rozpisywać się o nim, tylko zbiorę najważniejsze fakty i przykłady.

LINQ można użyć dla każdej kolekcji która implementuje IEnumerable lub generyczny IEnumerable<T>.

Zapytania LINQ można pisać w wersji SQLo podobnej. Różnicą jest to, iż rozpoczynamy od klauzuli FROM. Dalczego? Jeżeli nie zaczynalibyśmy od FROM, czyli wskazania źródła do którego się odwołujemy nie mielibyśmy wsparcia IntelliSense.

Opóźnione wykonanie zapytania - definicja LINQ jest traktowana jako zapytanie, które zostanie wykonane dopiero kiedy wynik będzie faktycznie potrzebny. Właściwość ta ma tę konsekwencję, że każde wywołanie zapytania będzie się odwoływało do źródła danych. Jeżeli chcemy przechować dane lokalnie i nie odwoływać się do źródła wielokrotnie, należy dane umieścić w kolekcji (np. Liście)


Cechy które składają się na LINQ:
  • inicjalizacja obiektów
  • niejawne typowanie - var; tylko jako zmienne lokalne; w deklaracji musi nastąpić ich inicjalizacja - nie może być to null;
  • typy anonimowe
  • wyrażenia lambda
  • metody rozszerzające - extension methods
Metody rozszerzające zawarte w interfejsie Enumerable:
  • All - sprawdza elementy kolekcji i zwraca prawdę jeżeli wszystkie elementy spełniają podane warunki
  • Any - sprawdza elementy kolekcji i zwraca prawdę jeżeli przynajmniej jeden element spełnia podane warunki
  • AsEnumerable - konwertuje dane do IEnumerable (np. z IQueryable)
  • AsQueryable - konwertuje do interfejsu IQueryble (np. z IEnumerable)
  • Average - średnia elementów
  • Cast - rzutuje elementy kolekcji na podany typ
  • Concat - pozwala łączyć ze sobą kolekcje, przykład:
    Code:
        class Program
        {
            static void Main(string[] args)
            {
                var personRepository = new PersonRepository();
                var collection1 = personRepository.GetPersonList1();
                var collection2 = personRepository.GetPersonList2();
                var combindedCollection = collection1.Concat(collection2).Concat(collection1).ToList();
            }
        }
    
        class Person
        {
            public string FirstName { get; set; }
            public string LastName { get; set; }
        }
    
        class PersonRepository
        {
            public IList<Person> GetPersonList1()
            {
                return new List<Person>
                           {
                               new Person {FirstName = "Jacek", LastName = "Kowalski"},
                               new Person {FirstName = "Marek", LastName = "Jackowski"}
                           };
            }
    
            public IList<Person> GetPersonList2()
            {
                return new List<Person>
                           {
                               new Person {FirstName = "Sebastian", LastName = "Kowalski"},
                               new Person {FirstName = "Seweryn", LastName = "Opolski"}
                           };
            }


  • Contains - sprawdza czy podany element znajduje się w kolekcji
  • Count - ilość elementów w kolekcji
  • DefaultIfEmpty - w przypadku gdy kolekcja nie zawiera żadnych elementów, zwraca jeden o domyślnej wartości (false, 0, null)
  • Distinct - usuwa duplikaty ze źródła
  • ElementAt(i) - zwraca element na danej pozycji w źródle
  • ElementAtOrDefault - jak wyżej z tą różnicą, że w przypadku nie znalezienia elementu nie zostanie rzucony wyjątek tylko zwrócona wartość domyślna
  • Except - zwraca elementy zawarte w pierwszej kolekcji a nieobecne w drugiej
  • First oraz FirstOrDefault - zwraca pierwszy element kolekcji
  • GroupBy - grupuje elementy według zadanego klucza, przykład:
    Code:
                var adventureWorksLtContext = new AdventureWorksLTContext();
                var customers = adventureWorksLtContext.Customers.ToList();
                IEnumerable<IGrouping<string, Customer>> customersGroupByFirstName = customers.GroupBy(x => x.FirstName);
                foreach (var group in customersGroupByFirstName)
                {
                    foreach (var customer in group)
                    {
                        Console.WriteLine("{0} {1}", customer.FirstName, customer.LastName);
                    }
                }


  • GroupJoin - zapytanie podobne do SQLowego Left Outer Join
  • Intersect - przecięcie zbiorów
  • Join - łączy dwa zbiory na zasadzie równości kluczy
  • Last oraz LastOrDefault - zwraca ostatni element kolekcji
  • LongCount - to samo co Count, tylko zwraca rezultat jako typ long
  • Max - zwraca największy element kolekcji
  • OfType<T> - zwraca elementy, które są typu T
  • Min - najmniejszy element kolekcji
  • OrderBy, OrderByDescending, ThenBy, ThenByDescending - sortowanie
  • Reverse - odwraca kolejność elementów w kolekcji
  • Select - projekcja rezultatu
  • SelectMany - jeden element wejściowy tworzy wiele wyjściowych. Szczególnie przydatny w przypadku pracy nad kolekcjami kolekcji - łączy wtedy kolekcje z obiektów w jedną wynikową
  • SequenceEqual - sprawdza czy obie kolekcje zawierają te same elementy, w tej samej kolejności
  • Single oraz SingleOrDefault - zwraca jeden element z kolekcji - jeżeli jest więcej elementów w kolekcji zostanie rzucony wyjątek, bądź zostanie zwrócona wartość domyślna
  • Skip - przeskakuje zadaną ilość elementów
  • SkipWhile - przeskakuje zadaną ilość elementów dopóki dany warunek jest spełniony
  • Sum - zwraca sumę 
  • Take - pobiera zadaną ilość elementów z kolekcji 
  • TakeWhile - to samo co Take, ale pobiera elementy spełniające warunek 
  • ToArray - konwertuje kolekcję na tablicę
  • ToDictionary - konwertuje kolekcję na słownik
  • ToList - konwertuje kolekcję do listy
  • ToLookUp - tworzy kolekcję grup
  • Union - łączy kolekcje, usuwa elementy powtarzające się
  • Where - filtruje elementy
  • Zip - łączy dwie kolekcje na podstawie zadanego warunku; liczba elementów jest równa mniejszej kolekcji

Zastosowania LINQ:
1. Stronicowanie:
Łączymy metody Skip i Take:
kolekcja.Skip(nr_strony * ilość_elementów_na_stronie).Take(ilość_elementów_na_stronie)

2. Złączenie równościowe - Inner Join

Code:
from a in collectionA
join b in collectionB
on a.Id equals b.IdA

3. Złączenia zewnętrzne - Outer Join

Code:
from a in collectionA
join b in collectionB
on a.Id equals b.IdA into c
from d in c.DefaultIfEmpty()

3. Iloczyn kartezjański - Cross Join

Code:
from a in collectionA
from b in collectionB


PLINQ - Parallel LINQ
PLINQ pozwala na użycie dodatkowych wątków w celu szybszego wykonania zadanego zadania. Aby uaktywnić wielowątkowe wykonywanie zapytania należy skorzystać z metody rozszerzającej AsParallel. Wykonane zachowanie nie zachowa kolejności wyników. Jeżeli chcemy aby kolejność została zachowana należy skorzystać z metody AsOrdered.

2 komentarze:

  1. Ja tylko dodam taką uwagę, że przy stronnicowaniu nie można mnożyć numeru strony razy ilość elementów na stronie, ponieważ w takim wypadku nie uzyskamy strony pierwszej. Mnożenie w skip dla początkowych wyników musi wynieść 0.

    OdpowiedzUsuń
  2. Świetnie napisane. Pozdrawiam serdecznie.

    OdpowiedzUsuń