Když se probírám tím, co LINQ je, zjišťuji, že je těžké najít přesnou definici a také najít hraniční čáry mezi jednotlivými technologiemi. Zkratka LINQ znamená Language INtegrated Query. Jedná se o množinu extenzních funkcí (viz předchozí příspěvek na mém blogu), z nichž některé mají v jazyce C# nová odpovídající klíčová slova (ve VB.NET také, ale ten neovládám). S tím mají někteří puristé trochu problém, neboť do syntaxe jazyka se vnášejí prvky závislé na implementaci základních tříd. Na to lze odpovědět dvěma argumenty. Prvním je relativizace - v jazyce už takové prvky jsou např. foreach závisí na IEnumerable a using na IDisposable (tvrďák může říct, že if závisí na System.Boolean). Druhý je pragmatický - lze snadno zpochybnit čistotu jazyka jako hodnotu samu o sobě - důležitá je snadnost a rychlost vývoje.

Dost teorií. LINQ přináší do jazyka prvky k výběru ze struktur dat (dosud nespecifikovaných) způsoby analogickýi SQL jazyku. Podle toho, co jsou ony nespecifikované struktury, dělí se (velmi přibližně a s překryvy funkčnosti!!!) na:

  • LINQ to IEnumerable<T> - tohle je můj termín, který by naši marketingoví hoši asi nepřekousli. Proto se často uvádí pouze jako LINQ (částečně nesprávně, protože LINQ je všechno) anebo jako LINQ to Objects (opět fakticky nesprávně, protože v .NET frameworku je všechno objekt a LINQ rozhodně nad vším nefunguje. Zkrátka je to manipulace s kolekcemi čistě v paměti. Najdete ho v knihovně System.Core, to je jednoduché kritérium.
  • LINQ to XML, dříve nazývaný XLINQ - ve skutečnosti se jedná o zcela novou knihovnu pro práci s XML (alternativu DOMu v podobě XmlDocument anebo streaming technologií XmlReader/XmlWriter). Jedná se o třídy pro reprezentaci XML takovou, aby se pohodlně vytvářela a přitom bylo možné použít LINQ to IEnumerable<T>. Kromě toho obsahuje řadu extenzních funkcí specifických pro hierarchie, např. AncestorsAndSelf působícími nad IEnumerable<XElement>. Veškeré funkce najdete v knihovně System.Xml.Linq. Více někdy příště.
  • LINQ over DataSet - zjednodušeně řečeno se jedná o sadu wrapperů a extenzí nad objekty datasetu (tabulka, řádek apod.), které tyto objekty zpřístupňují jako IEnumerable<T> kolekce, se kterými lze manipulovat pomocí LINQ to IEnumerable<T>. Nacházejí se v knihovně System.Data.DataSetExtensions. Více někdy příště.
  • LINQ to SQL, dříve nazývaný DLINQ. V podstatě je to objektově-relační mapper, který je napsán tak, aby šlo s daty manipulovat pomocí základních LINQ funkcí. Místo IEnumerable<T> používá IQueryable<T>, což má naprosto zásadní vliv na jeho funkci - proč si povíme někdy příště. Nachází se v knihovně System.Data.Linq.
  • LINQ to Entities - Entities je nový koncept v ADO.NET. Původně měly být součástí .NET frameworku 3.5, ale budou vydány až s novým SQL Serverem 2008 někdy v létě - i když budou pracovat i s jinými databázemi. Informací je zatím spíše méně než více, tak snad někdy v budoucnu o nich něco napíšu.

Dnes tedy něco o "základním LINQu". Dole následuje pár příkladů. Zajímavé a z kódu neodhadnutelný je fakt, že se vyhodnocení dotazů provádí co nejpozději (to je právě klíčové v LINQ to SQL, snad příště). To znamená, že dokud nepotřebujete výsledky dotazu (např. v cyklu foreach anebo při volání metody ToList), k vyhodnocení nedochází. Seznam všech funkcí je zde. A nyní demo kód:

            List<Clovek> lide = new List<Clovek>()

            {

                new Clovek{CeleJmeno="Pavel Novak", Vek=35, MaVousy=false},

                new Clovek{CeleJmeno="Ho Ci Min", Vek=72, MaVousy=false},

                new Clovek{CeleJmeno="Javier Perez Juarez Simon Miguel Diaz", Vek=47, MaVousy=true},

                new Clovek{CeleJmeno="Kato", Vek=24, MaVousy=false},

                new Clovek{CeleJmeno="Michael Jurek", Vek=19, MaVousy=false},

                new Clovek{CeleJmeno="Vladimir Iljic Lenin", Vek=52, MaVousy=true},

                new Clovek{CeleJmeno="Karel Novak", Vek=35, MaVousy=true},

                new Clovek{CeleJmeno="Karel Jaromir Erben", Vek=62, MaVousy=false}

            };

 

            // LINQ je sada extenznich funkci a klicovych slov v jazyce

            // Zakladni veci jdou obema zpusoby, nektere pokrocilejsi veci pouze pomoci extenznich funkci

 

            // Projekce - Select

            // Trivialni pripad - vystup je identicky jako vstup

            var identita1 =lide.Select(c=>c);

            var identita2 = from c in lide select c;

            // Vyber jedineho pole (vysledek je IEnumerable<string>

            var jednoPole1 = lide.Select(c => c.CeleJmeno);

            var jednoPole2 = from c in lide select c.CeleJmeno;

            // Vyber podmnoziny poli - vyuziva anonymni typ

            var vyberPoli1 = lide.Select(c => new { c.CeleJmeno, c.Vek });

            var vyberPoli2 = from c in lide select new { c.CeleJmeno, c.Vek };

            // Vyber podmnoziny poli - vyuziva anonymni typ

            var volnyVyber1 = lide.Select(c => new { Names=c.Jmena, NameLength=c.Jmena.Length, Age=365*c.Vek });

            var volnyVyber2 = from c in lide select new { Names = c.Jmena, NameLength = c.Jmena.Length, Age = 365 * c.Vek };

 

            // Projekce s a bez hierarchie - SelectMany

            // Pouziti Select zachova hierarchii

            // SelectMany zplostuje vystup (pomoci klicovych slov to nejde zapsat)

            var sHierarchii1 = lide.Select(c => c.Jmena);

            var sHierarchii2 = from c in lide select c.Jmena;

            foreach (string[] jmena in sHierarchii1)

                foreach (string jmeno in jmena)

                    Console.WriteLine(jmeno);

            var bezHierarchie1 = lide.SelectMany(c => c.Jmena);

            foreach (string jmeno in bezHierarchie1)

                    Console.WriteLine(jmeno);

 

            // Restrikce - Where

            var jednaPodminka1 = lide.Where(c => c.Vek > 50).Select(c => c.CeleJmeno);

            var jednaPodminka2 = from c in lide where c.Vek>50 select c.CeleJmeno;

            var dvePodminky1 = lide.Where(c => (c.Vek > 50) && c.MaVousy).Select(c => c.CeleJmeno);

            var dvePodminky2 = from c in lide where (c.Vek > 50) && c.MaVousy select c.CeleJmeno;

 

            // Trideni - SortBy, ThenBy

            var jednoTrideni1 = lide.OrderBy(c => c.Vek).Select(c => c.CeleJmeno);

            var jednoTrideni2 = from c in lide orderby c.Vek select c.CeleJmeno;

            var jednoTrideniSestupne1 = lide.OrderByDescending(c => c.Vek).Select(c => c.CeleJmeno);

            var jednoTrideniSestupne2 = from c in lide orderby c.Vek descending select c.CeleJmeno;

            var dveTrideni1 = lide.OrderBy(c => c.MaVousy).ThenBy(c => c.Vek).Select(c => c.CeleJmeno);

            var dveTrideni2 = from c in lide orderby c.MaVousy,c.Vek select c.CeleJmeno;

           

            // Seskupovani - GroupBy

            // Jednoduche seskupeni pouze rozhazi kolekci do jednotlivych "prihradek"

            var jednoducheSeskupeni1 = lide.GroupBy(c => c.MaVousy);

            var jednoducheSeskupneni2 = from c in lide group c by c.MaVousy;

            // Slozitejsi seskupeni s projekci (pouze jmeno) a tridenim

            var sloziteSeskupeni1 = lide.OrderBy(c => c.Vek).GroupBy(c => c.MaVousy, c => c.CeleJmeno);

            var sloziteSeskupneni2 = from c in lide orderby c.Vek group c.CeleJmeno by c.MaVousy;

 

            // Funkci je ve skutecnosti mnohem vic, tak jenom zaverecna exhibice

            // Toto vybere lidi s nadprumernym vekem, setridi je podle veku a vousu sestupne, a pote vybere druhy az ctvrty vysledek

            // Kompletni seznam funkci je v dokumentaci

            var divocina = lide.Where(c => c.Vek > lide.Average(c1 => c1.Vek)).OrderBy(c => c.Vek).ThenByDescending(c => c.MaVousy).Skip(1).Take(3);

            Debugger.Break();

 

 

    class Clovek

    {

        public string CeleJmeno { get; set; }

        public int Vek { get; set; }

        public bool MaVousy { get; set; }

 

        public string[] Jmena

        {

            get { return CeleJmeno.Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries); }

        }

    }