Galin Iliev

Software Architecture & Development

Âúâåäåíèå â LINQ è îñíîâíè âúçìîæíîñòè

Êàêòî ñïîìåíàõìå ïî-ãîðå çàä àáðåâèàòóðàòà LINQ ñòîè Language INtegrated Query. Òîâà å ïðîåêò íà Ìàéêðîñîôò çà äîáàâÿíå ñèíòàêñèñ çà çàÿâêè, õàðàêòåðåí çà T-SQL, â åçèöèòå îò .NET Framework. Ïúðâîíà÷àëíî òîçè ñèíòàêñèñ ùå ñå ïîääúðæà îò C# è Visual Basic, íî ñå î÷àêâà äà áúäå âúâåäåí è â îñòàíàëèòå åçèöè íà ïî-êúñåí åòàï.

LINQ äåôèíèðà ñòàíäàðòíè îïåðàòîðè çà çàÿâêè, êîèòî ïîçâîëÿâàò íà åçèöèòå ñ ïîääðúæêà íà LINQ, äà ôèëòðèðàò, èçáðîÿâàò è ñúçäàâàò ïðîåêöèè íà íÿêîëêî òèïà êîëåêöèè, êàòî èçïîëçâàò åäèíåí ñèíòàêñèñ. Îñíîâíèòå èçòî÷íèöè íà äàííè êúì ìîìåíòà ñà ìàñèâè, èçáðîèìè êëàñîâå ( èìïëåìåíòèðàùè èíòåðôåéñèòå ICollection è IEnumerable ), XML, DataSet-îâå îò ðåëàöèîííè áàçè äàííè. Êàêòî â ïîâå÷åòî ñè ïðîäóêòè Ìàéêðîñîôò ñà îñèãóðèëè API (Application Program Interface), ÷ðåç êîéòî òðåòè ñòðàíè ìîãàò äà îñèãóðÿò ïîääðúæêà íà ñâîè èçòî÷íèöè íà äàííè â LINQ.

Çà äà ñòàíå ïî-ÿñíî êàêâè ñà ïîëçèòå îò LINQ è ùå íàïðàâèì ïàðàëåë ñúñ SQL çàÿâêèòå.

Ïðåäïîëàãàì ïîâå÷åòî îò âàñ ñà çàïîçíàòè ñúñ çàÿâêèòå â SQL, íî è äà ãðåøà òîâà íÿìà äà ïîïðå÷è äà ðàçáåðåòå èçâúðøâàíèòå îïåðàöèè.

Ôèëòðèðàíå â SQL

Íåêà èìàìå òàáëèöà Contact â MS SQL Server ñúñ ñëåäíèòå êîëîíè è äàííè:

Contact

ID

ContactName

1

Galcho

2

George

3

Trifon

4

Kiril

5

Shumi

Çà äà èçâåäåì âñè÷êè çàïèñè çàïèñè îò êîëîíàòà ContactName, êîèòî çàïî÷âàò çàïî÷âàò ñ áóêâàòà “G” èçïîëçâàìå ñëåäíàòà çàÿâêà:

SELECT [ContactName]

FROM [Contact]

WHERE [ContactName] LIKE ‘G%’

È êàòî ðåçóëòàò ïîëó÷àâàìå:

Galcho

George

Äîñòàòú÷íî ïðîñòî, íàëè?

Íî òîâà å êîãàòî èçïúëíÿâàìå çàÿâêèòå íà SQL ñúðâúð. À êàêâî ñå ïîëó÷àâà, êîãàòî âå÷å ñìå èçâëåêëè äàííèòå îò ñúðâúðà è èñêàìå ñàìî äà ïðèëîæèì äîïúëíèòåëåí ôèëòúð!? Òîãàâà âàðèàíòèòå ñà:

·         Èçïúëíÿâàìå íîâà çàÿâêà êúì SQL ñúðâúðà çàåäíî ñ íîâèòå óñëîâèÿ çà ôèëòðèðàíå

·         Ïèøåì äîïúëíèòåëåí êîä çà ôèëòðèðàíå íà ðåçóëòàòèòå.

Óëîâêàòà òóê å, ÷å íå âèíàãè ñå íàëàãà äà ôèëòðèðàìå äàííè, êîèòî ñà âçåòè îò SQL ñúðâúð. Çàòîâà íåêà ðàçãëåäàìå ôèëòðèðàíåòî íà ìàñèâè è êîëåêöèè â äîòóê ïîçíàòèòå íè âåðñèè íà C#.

Çà âñåêè ïðèìåð ùå ôèëòðèðàìå òîçè ìàñèâ:

string[] contacts = { "Galcho", "George", "Trifon", "Kiril", "Shumi" };

êàòî ùå èçïîëçâàìå ñúùîòî óñëîâèå – èìåíàòà äà çàïî÷âàò ñ áóêâàòà ‘G’

Ôèëòðèðàíå â C# 1.1

Çà äà âúðíåì ñúùèÿò ðåçóëòàò å íåîáõîäèìî äà èçïúëíèì ñëåäíèÿò êîä:

private void FilterWithCSharp1_1()

{

    string[] contacts = { "Galcho", "George", "Trifon", "Kiril", "Shumi" };

  

    //display result

    foreach (string name in FilterResults(contacts))

    {

          Console.WriteLine(name);

    }

}

  

private IEnumerable FilterResults(string[] Data)

{

    //result collection

    StringCollection results = new StringCollection();

  

    //filter array

    foreach (string name in Data)

    {

        if (name.StartsWith("G"))

            results.Add(name);

    }

    return results;

}

 òîçè êîä ñëåä äåôèíèöèÿòà íà âõîäíèòå äàííè èìàìå ñàìî åäíà foreach êîíñòðóêöèÿ, êîÿòî èçâåæäà ïîñëåäîâàòåëíî âñåêè åëåìåíò âúðíàò îò ôóíêöèÿòà FilterResults, íà êîÿòî ïîäàâàìå êàòî ïàðàìåòúð ìàñèâà ñ âõîäíè äàííè.

 íåÿ äåêëàðèðàìå è èíèöèàëèçèðàìå ïðîìåíëèâà results îò òèï StringCollection.Ñëåä òîâà ñ ïîìîùòà íà foreach êîíñòðóêöèÿ, ïðîâåðÿâàìå âñåêè åëåìåíò äàëè îòãîâàðÿ íà çàäàäåíîòî óñëîâèå ( çàïî÷âà ñ áóêâà “G” ) è àêî îòãîâàðÿ ãî äîáàâÿìå â êîëåêöèÿòà ñ ðåçóëòàòèòå results. Íàêðàÿ âðúùàìå results êàòî ðåçóëòàò.

Ôèëòðèðàíå â C# 2.0

Íåêà äà ðàçãëåäàìå êàê áèõìå ðåàëèçèðàëè òàçè ôóíêöèîíàëíîñò ñúñ ñðåäñòâàòà, êîèòî ïðåäîñòàâÿ C# 2.0

private void FilterWithCSharp2()

{

    string[] contacts = { "Galcho", "George", "Trifon", "Kiril", "Shumi" };

  

    //display result

    foreach (string name in FilterResults2(contacts))

    {

        Console.WriteLine(name);

    }

}

private IEnumerable FilterResults2(string[] Data)

{

    //filter array

    foreach (string name in Data)

    {

        if (name.StartsWith("G"))

            yield return name;

    }

}

 òîçè êîä, ïîäîáíî íà ïðåäèøíèÿò ïðèìåð, èìàìå ñàìî åäíà foreach êîíñòðóêöèÿ, êîÿòî èçâåæäà ïîñëåäîâàòåëíî âñåêè åëåìåíò âúðíàò îò ôóíêöèÿòà FilterResults2, íà êîÿòî ïîäàâàìå êàòî ïàðàìåòúð ìàñèâà ñ âõîäíè äàííè.

Òúíêèÿò ìîìåíò å âúâ ôóíêöèÿòà FilterResults2. Òàì ñå îñúùåñòâÿâà ôèëòðèðàíåòî íà åëåìåíòèòå è àêî îòãîâàðÿò íà óñëîâèåòî ñå èçïúëíÿâà îïåðàòîðà yield return, êîéòî âðúùà óïðàâëåíèåòî íà èçâèêâàùàòà ôóíêöèÿ çà âñåêè çàïèñ ïî îòäåëíî.

Çàáåëåæêà: Ïðåïîðú÷âàì äà îáõîäèòå äâàòà ïðèìåðà â Debug Mode è ñòúïêà ïî ñòúïêà è äà îáðúíåòå âíèìàíèå, ÷å âúâ ôóíêöèÿòà FilterResults ïúðâî ñå îáõîæäàò âñè÷êè åëåìåíòè, à ñëåä òîâà çàïî÷âà îòïå÷àòâàíåòî íà åêðàí. Êàêòî ïîñî÷èõìå âúâ ôóíêöèÿòà FilterResults2 íå å òàêà. Òîâà îêàçâà ãîëÿìî âëèÿíèå ïðè îáðàáîòêà íà ãîëåìè êîëåêöèè, òúé íå å íåîáõîäèìî äà ñå èç÷àêâà îáðàáîòêàòà íà âñè÷êè åëåìåíòè ïðåäè äà ïðîäúëæè ðàáîòàòà.

Ïîâå÷å çà îïåðàòîðà yield ìîæå äà ïðî÷åòåòå íà àäðåñ http://msdn2.microsoft.com/en-us/library/9k7k7cf0.aspx

Ôèëòðèðàíå ñ LINQ è C# 3.0

Êàê ñòàâà òîâà â C# 3.0? Íåêà ðàçãëåäàìå ñëåäâàùèÿò êîä:

private static void FilterWithCSharp3() {

    string[] contacts = { "Galcho", "George", "Trifon", "Kiril", "Shumi" };

  

    var result = from s in contacts

                where s.StartsWith("G")

                select s;

  

    //display result

    foreach (string name in result) {

        Console.WriteLine(name);

    }

}

Ñëåä ïúðâîíà÷àëíî íåîáè÷àéíèÿ ñèíòàñèñ èçãëåæäà ìíîãî åëåãàíòíî, íàëè?! Ìíîãî ïðèëè÷à íà SQL çàÿâêàòà.

Ñúùèÿò ðåçóëòàò ìîæå äà áúäå ïîñòèãíàò è ïî àëòåðíàòèâåí íà÷èí êàòî èçïîëçâàìå íîâèòå ìåòîäè äåôèíèðàíè â LINQ àñåìáëèòàòà.

private static void FilterWithCSharp3_2() {

    string[] contacts = { "Galcho", "George", "Trifon", "Kiril", "Shumi" };

  

    var result = contacts.Where<string>(x => x.StartsWith("G"));

  

    //display result

    foreach (string name in result) {

        Console.WriteLine(name);

    }

}

Ïî òîçè íà÷èí ñòàâà äîðè îùå ïî-åëåãàíòíî.

Òîçè ïðèìåð å äîñòàòú÷íî êðàòúê, çà äà ïîêàæå èçìåíåíèÿòà â ñèíòàêñèñà, îáåìà êîä è ÷èòàåìîñòòà. Ïîêàçàõìå ñàìî èçïîëçâàíåòî ñàìî íà åäèí îò îïåðàòîðèòå çà çàÿâêè – where.

Íåêà äà íàïðàâèì íåùàòà ìàëêî ïî-ñëîæíè è äà ãðóïèðàìå äàííèòå.

Ãðóïèðàíå ñ LINQ è C# 3.0

Çà äà ãðóïèðàìå äàííè ùå èìàìå íóæäà îò ïî-ñëîæíè äàííè. Íåêà äà äåôèíèðàìå êëàñ Employee ïî ñëåäíèÿò íà÷èí:

public class Employee {

    public string Name;

    public string Department;

    public double Salary;

}

Îáúðíåòå âíèìàíèå, ÷å íå äåôèíèðàìå êîíñòðóêòîð è êîìïèëàòîðà äîáàâÿ êîíñòðóêòîð ïî ïîäðàçáèðàíå (áåç ïàðàìåòðè).

Çàáåëåæêà: Â ðåàëíè ïðîåêòè å äîáðå äà äåôèíèðàìå ñâîéñòâà, êîèòî äà äîñòúïâàò òåçè ïîëåòà, íî ñ öåë îïðîñòÿâàíå íà êîäà ñåãà ãè ïðîïóñêàìå.

Äåôèíèðàìå è èíèöèàëèçèðàìå êîëåêöèÿ îò îáåêòè îò òèï Employee ñúñ ñëåäíèÿò êîä:

Employee[] employees = {

    new Employee{Name="Joe", Department="IT", Salary=1800d},

    new Employee{Name="Peter", Department="IT", Salary=2000d},

    new Employee{Name="Jana", Department="Sales", Salary=900d},

    new Employee{Name="Schumacher", Department="Motor Sport", Salary=1000000d},

    new Employee{Name="Mary", Department="Sales", Salary=700d},

    new Employee{Name="July", Department="Marketing", Salary=2800d},

};

 ñëåäâàùèÿò êîä ùå ãðóïèðàìå ñëóæèòåëèòå ñïîðåä òåõíèÿò îòäåë è çà äà íàïðàâèì íåùàòà ïî-èíòåðåñíè, ùå ñóìèðàìå çàïëàòèòå íà âñè÷êè ñëóæèòåëè â îòäåëà. Âå÷å íå å òîëêîâà ëåñíî äà ãî íàïðàâèì ñúñ ñðåäñòâàòà íà C# 2.0, íàëè?!

 

var result = from e in employees

            group e by e.Department into g

            select new {

                    Department = g.Key,

                    Employees = g,

                    SumSalaries = g.Sum(e => e.Salary)};

  

//display result

foreach (var dept in result) {

    //display department name

    Console.WriteLine("Department Name:{0} Sum of Salaries:{1}",
dept.Department, dept.SumSalaries);

    //display employees in current department

    foreach (Employee empl in dept.Employees) {

        Console.CursorLeft = 4;

        Console.WriteLine("Name:{0}, Salary:{1}", empl.Name, empl.Salary);

    }

}

 òàçè çàÿâêà ñå èçïúëíÿâàò äâå îñíîâíè ôóíêöèè:

·         Ãðóïèðàíå íà ñëóæèòåëèòå ïî îòäåëè è

·         Ñóìèðàíå íà çàïëàòèòå íà ñëóæèòåëèòå çà âñåêè îòäåë.

Êàòî ðåçóëòàò ñå âðúùà êîëåêöèÿ îò íîâ òèï îáåêòè ñúñ ñâîéñòâà:

·         Department - òåêñòîâ íèç ñ èìåòî íà îòäåëà

·         Employees - êîëåêöèÿ îò îáåêòè òèï ñúñ ñëóæèòåëèòå çà êîíêðåòíèÿò îòäåë

·         SumSalaries - ÷èñëî ñúäúðæàùî ñóìèòå íà çàïëàòèòå íà ñëóæèòåëèòå â îòäåëà

Ïîíåæå èìàìå äâå (âëîæåíè) êîëåêöèè, çà äà âèçóàëèçèðàìå ðåçóëòàòà òðÿáâà äà ðåàëèçèðàìå äâà öèêúëà (ñúùî âëîæåíè). È åòî êàêúâ å èçõîäà íà åêðàí:

Department Name:IT Sum of Salaries:3800

    Name:Joe, Salary:1800

    Name:Peter, Salary:2000

Department Name:Sales Sum of Salaries:1600

    Name:Jana, Salary:900

    Name:Mary, Salary:700

Department Name:Motor Sport Sum of Salaries:1000000

    Name:Schumacher, Salary:1000000

Department Name:Marketing Sum of Salaries:2800

    Name:July, Salary:2800

Ñúâåò: Ïðåìèíåòå ïðåç êîäà ñòúïêà ïî ñòúïêà êàòî ïðîâåðÿâàòå ñúñòîÿíèåòî íà ïðîìåíëèâèòå. Ïî òîçè íà÷èí ùå âíèêíåòå â äåòàéëè êàêâî ñòàâà íà âñåêè ðåä îò êîäà.

Ìíîãî å âåðîÿòíî äà ñå ÷óâñòâàòå îáúðêàíè îò òîçè ñèíòàêñèñ.  ñëåäâàùèòå ÷àñòè ùå ñå îáÿñíèì íîâèòå åçèêîâè êîíñòðóêöèè è ùå ñå âúðíåì êúì çàäúëáî÷åíî îáÿñíåíèå íà LINQ ñèíòàêñèñà êàòî ùå ðàçãëåäàìå îùå ïðèìåðè.

Äî ìîìåíòà ðàçãëåäàõìå äâà îò îïåðàòîðèòå çà çàÿâêè - where è group by, À èìà îùå äîñòàòú÷íî ìíîãî (order, join, union, distinct, except, intersect), ÷èåòî èìïëåìåíòèðàíå â .NET 1.1 è .NET 2.0 íå å òîëêîâà ëåñíî. Òÿõíîòî èçïîëçâàíå ùå ïîêàæåì ïî-íàòàòúê â òîâà ðúêîâîäñòâî. Íåêà ñåãà äà ñå âúðíåì ìàëêî íàçàä è äà ðàçãëåäàìå ðàçøèðåíèÿòà â ñèíòàêñèñà íà C#, êîèòî ïðàâÿò LINQ âúçìîæíî.

Ïðîåêòè ñ ïðèìåðèòå

Ïðîåêò çà VS 2005 + LINQ May 2006 Preview (301KB)

Ïðîåêò çà VS 2008 (34.3KB)

 

Ñëåäâàùà ÷àñò: Èíèöèàëèçèðàíå íà îáåêòè è êîëåêöèè

Content