уторак, 17. април 2018.

Uvod u LINQ

Zvanično ili formalno LINQ – Language Intergrated Query – Upit integrisan u jezik; jeste jezik struktuiranih upita za pretraživanje i lokalnih kolekcija objekta ali i udaljenih izvora podataka na takav način da ne narušava bezbednost podataka. Za mene lično LINQ je jezik struktuiranih upita koji omogućava skraćeno pisanje code-a u radu sa bilo kakvim kolekcijama i dinamičkim sastavljanjem upita. LINQ  vam omogućava da radite sa različitim izvorima podataka dok koristite isti način kodiranja. Takođe pored C# programskog jezika, LINQ koriste i drugi programski jezici koji podržavaju .Net Framework 3.5 ili noviji. Znam da vam sve ovo zvuči kompleksno i sami po sebi LINQ upiti jesu kompleksni i neophodno je da se na neki način dosta isprakticirate u korišćenju istih kako bi vam bilo jasno kolika je prednost korišćenja LINQ-a u vašem kodiranju. LINQ isključivo radi sa sekvencama i elementima. Sekvenca vam je svaki objekat koji implementira interfejs IEnumerable<T> dok je elemenat svaka stavka sekvence. To takođe znači da se LINQ upiti mogu primenjivati nad bilo kojim izvorom podataka koji primenjuje interfejs IEnumerable<T>. Međutim LINQ se takođe deli na mnogo potrebnih i opcionih delova poput LINQ provajdera, proširenih metoda, lambda izraza i sintakse za razumevanje LINQ upita.


( LINQ Arhitektura )

LINQ provajderi, poput LINQ to Objects, LINQ to Entities, LINQ to XML itd. su komponente između LINQ upita i izvora podataka koje konvertuju standardne LINQ operacije u specifične komande koje izvori podataka mogu razumeti. Npr. LINQ to SQL provajder konvertuje LINQ upite u T-SQL tako da baze podataka mogu razumeti iste. Tako da vi ne morate pisati T-SQL komande u vašem code-u kako bi ste manipulisali podacima iz baze podataka. Proširene metode koje LINQ koristi su već izgrađene metode poput Where, Select, OrderBy itd. Klazula Where se zove i operator restrikcije jer ograničava podatke u rezultatu izvršavanju upita dok klazula Select specifira podatke koji će se pojaviti kao rezultat. Lambda izrazi se opciono koriste umesto imenovanih metoda i pojednostavljuju kodiranje. Lambda izraz je kratak i koncizan način pisanja anonimnih metoda, znači metoda koji nemaju ime; koji se koriste za izvršavanje upita. Lambda operator se piše ovako => Sintakse za razumevanje LINQ  upita su opcione C# ključne reči poput from, in, where, select, desceding itd. Oni pojednostavljuju upite koje pišete.

Kako je nastao LINQ ?


Nastanak LINQ-a se uglavnom prepisuje izlaskom C# programskog jezika verzije 3.0 i  .Net Framework 3.5; 19 novembra 2007; tretirajući LINQ kao najveću promenu istog. Međutim, ukoliko pogledate od čega se sastoji neki LINQ upit, kao npr. u LINQ upitu vi možete da koristite Lambda izraze ali i ne morate. Kao što znamo Lambda izraz se sastoji od anonimnih metoda, ali takođe znamo da su anonimne metode nastale u C# programskom jeziku verziji 2.0. Jednostavno neke promene koje su došle sa C# programskim jezikom verzijom 2.0 su ostale poprilično nezapažene da bi sa LINQ upitima u C# programskom jeziku verziji 3.0 doživele ogromnu ekspanziju. Razmotrimo i podsetimo se nekih stvari koje će vam olakšati razumevanje LINQ upita.




( C# 3.0 je doneo ekspanziju i C# 2.0 poboljšanjima u C# programskom jeziku )

C# programski jezik u verziji 3.0 uvodi ključnu reč var što vas oslobađa potrebe dodeljivanja tipa promenjivih. Ovo implicitno dodeljivanje tipa zadržalo je svoje strogo tipiziranje što je oduševilo programere.

var text1 = "We learn LINQ queries!";
string text2 = text1;

Ali cena toga je da vi ne možete koristiti ključnu reč var u nekim situacijama:

  • Ne možete deklarisati promenjivu bez inicijalizacije:


var text;

  • Ne možete deklarisati višestruke deklaracije: 

var broj1 = 50, broj2 = 100;

  • Ali ako inicijalizujete kolekciju, takođe ćete dobiti grešku:


var brojevi = { 0, 1, 2, 3, 4, 5 };

Međutim ovo je dozvoljeno:

var brojevi = new int[] { 0, 1, 2, 3, 4, 5 };

  • Promenjiva ne može imati vrednost null:


var text = null;

  • Promenjiva deklarisana sa var ne može biti član klase:


public var broj = 10;

  • Sa ključnom rečju var ne možete deklarisati konstantu:


var const mile = 1.60934;

Anonimni tipovi su takođe dali svoj veliki doprinos. Anonymous types – neimenovani ili anonimni tipovi su tipovi podataka koji nemaju nikakvo ime. U C# programskom jeziku vam je dozvoljeno da kreirate objekat sa ključnom rečju new bez definisanja njegove klase. Implicitno dodeljivanje tipa uz ključnu reč var je nosilac reference anonimnih tipova. Tako da vam je anonimni tip jednostavno klasa koju kompajler pravi u hodu koja se koristi za čuvanje nekog skupa vrednosti. Npr.

var employee = new { Name = "Manuel", Salary = 4000 };

Prilikom kompajliranja, kompajler će ovo prevesti u sledeće:

internal class AnonymousGeneratedTypeName
    {
        private string name;
        private int salary;

        public AnonymousGeneratedTypeName (string name, int salary)
        {
            this.name = name;
            this.salary = salary;
        }

        public string Name { get { return name; } }
        public int Salary { get { return salary; } }

        ...
    }

Ove tri tačkice znači da nije ispisan ceo code… već da se code nastavlja jer ova privremena klasa sadrži i metode Equals, GetHashCode i ToString() koji se redefinišu. Ako uzmete u obzir da korišćenje neimenovanih klasa ima ograničenja, npr. ne možete vratiti objekt takve klase kao povratnu vrednost iz metode, ne može se nasleđivati, instancirati; ali zato od C# programskog jezika verzije 3.0 možete inicijalizovati objekte i kolekcije. Zatim jedno od interesantnih poboljšanja C# programskog jezika verzije 2.0; Extension Methods - metode proširenja; u vidu proširenja klasa, interfejsa i struktura dodavanjem novih metoda, omogućava da se isti prošire novim metodama bez menjanja definicije izvornog postojanja. Metoda proširenja je statička metoda u statičkoj klasi i njena vidljivost ima isti doseg kao i klasa. Metodi se kod prvog argumenta mora navesti ključna reč this.

public static class MyExtensions
    {
        public static int WordCount(this String str)
        {
            return str.Split(new char[] { ' ', '.', '?', '!' },
                             StringSplitOptions.RemoveEmptyEntries).Length;
        }
    }

Što se tiče Lambda Expression – lambda izraza, oni su uvedeni u C#  programski jezik verziji 3.0 i totalno su promenili definisanje delegata u obliku neimenovanih metoda. Lambda izraz je bezimena metoda napisana na mestu instance delegata. Npr. pretpostavite da je numbers neki niz brojeva koji trebate poslagati od manjeg prema većeg. Kao kad izvučete brojeve od lotto-a i trebate iste složiti. U tom slučaju možete umesto pisanja metode koja će to uraditi napisati lambda izraz:

var firstSmallNumbers = numbers.TakeWhile((n, index) => n >= index);

Lambda izrazi jednostavno pojednostavljuju i skraćuju pisanje code-a. S obzirom da tek počinjemo sa LINQ-om nebi vas sada samo još dodatno zbunjivao sa Expression Tree – stablom izraza, Auto-Implement Properties – automatskim implementiranim svojstvima , Partial Methods – parcijalnim metodama i mnogim drugim bitnim stvarima koji takođe imaju svoj doprinos u nastajanju LINQ-a, nego najbolje da što pre pređete na praktičniji nivo i postepeno se upoznajete kroz praksu sa jednostavnošću korišćenja LINQ-a i kod najkompleksniji zadataka gde ćemo usput se baviti i sa gore navedenim temama kada malo više upoznate LINQ.

Jedan jednostavan LINQ primer

Pokrenite Microsoft Visual Studio .Net, izaberite Console App (.Net Framework) šablon i program nazovite SimpleLINQQuery. Otkucajte sledeći code:

using System;
using static System.Console;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SimpleLINQQuery
{
    class Program
    {
        static void Main(string[] args)
        {
            var employee = new { Name = "Manuel", Salary = 4000 };

            int[] numbers = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
          
            Write("Numbers: ");

            foreach (var item in numbers)
            {
                Write(item.ToString() + ", ");

            }

            // A simple LINQ Query

            var query = from num in numbers
                        where num % 2 == 1
                        select num;

            Write(Environment.NewLine + "Odd numbers: ");

            foreach (var item in query)
            {
                Write(item.ToString() + ", ");

            }

            WriteLine();

            WriteLine(Environment.NewLine + "Press any key to continue...");
            ReadKey();

        }
    }
}

U ovom primeru nismo čak ni koristili lambda izraz da bi vam LINQ upit bio što razumniji. Kad pokrenete navedeni code dobićete sledeći rezultat.

Numbers: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
Odd numbers: 1, 3, 5, 7, 9,

Press any key to continue...

Kako se kodira prethodni primer, možete pogledati i na video-u:


 ( LINQ - 1. A Simple LINQ Query )