C# ile örneklerle derinlemesine LINQ sorgu işlemleri


Merhabalar,

Bu yazıda LINQ üzerinden yapabileceğimiz çeşitli sorgu yapılarını anlatacağım. Bir önceki yazıda sadece lambda ifadelerine değinmiştim. Bu lambda ifadelerinin nereden geldikleriyle ilgili ayrıntı bilgi vermiştim. Ben genellikle lambda ifadelerini kulanarak, LINQ üzerinde tanımlı extension metodlar ile işlemler yaparım. Ancak LINQ’in kullanımı sadece bu şekilde değildir. C# üzerinden, tıpkı sql sorgusu yazar gibi (tabi ki biraz değişiklikleri var) LINQ sorgusu da yazabiliriz. Temel olarak anahtar kelimeler ile extension metodlarının isimleri hemen hemen aynı. Eğer sql biliyorsanız da pek yabancılık çekeceğinizi sanmıyorum.

Konuya bir örnekle başlayalım ve bu örnekle bir önceki yazıda nelerden bahsettiğimizi hatırlarken nelerden bahsetmediğimiz hakkındaki ön bilgiyi örnek üzerinde inceleyelim.

  1. using System;
  2. using System.Linq;
  3. using System.Collections.Generic;
  4. namespace helvacikoylu
  5. {
  6.     internal class Program
  7.     {
  8.         private static void Main(string[] args)
  9.         {
  10.             List<string> liste = new List<string>
  11.                                      {
  12.                                          “helvacıköylü”,
  13.                                          “mehmet”,
  14.                                          “ertuğrul”,
  15.                                          “C#”,
  16.                                          “Framework”,
  17.                                          “dot Net”
  18.                                      };
  19.             IEnumerable<string> sonuc1 = liste.Where(x => x.Contains(“a”));
  20.             IEnumerable<string> sonuc2 = from x in liste where x.Contains(“a”) select x;
  21.             Console.WriteLine(“# sonuç 1 #”);
  22.             foreach (string s in sonuc1)
  23.                 Console.WriteLine(“-> ” + s);
  24.             Console.WriteLine(“\n# sonuç 2 #”);
  25.             foreach (string s in sonuc2)
  26.                 Console.WriteLine(“-> ” + s);
  27.             Console.ReadLine();
  28.         }
  29.     }
  30. }

Örneğimizdeki dizimiz (koleksiyonumuz) 6 elemana sahip bir string dizi. Dizimiz üzerinde iki LINQ sorgu işlemi yaptık. İlkine bir önceki yazıdan aşinasınız Where metodu ile yaptığımız bu sorgu, dizi elemanları içinde, içinde “a” geçen elemanları IEnumerable olarak döndürüyor. Zaten kodun ilerleyen kısımlarında ekrana “# sonuç 1 #” başlığı altında bu sorgudan gelen sonuçları ekrana yazdırıyoruz. Ikinci sorgumuz ise bu yazıda ele alacağımız sorgu tipi. Daha önceden böyle bir şey görmediyseniz, C#’da böyle bir yapının çalışacağından, syntax’ın nasıl tanınabileceğinden dolayı şaşırmış olabilirsiniz. Örnekteki iki sorgu da aynı işi yapar. Programı çalıştırıp kendiniz görün.

Genelde ilk sorgudaki, yani daha önceden de bahsettiğim direkt olarak extension metoda ulaşma şekli kullanılır. Zaten bu söz dizimi C# ‘a daha aşinadır. Örneğimizdeki kullanımı da diğerine göre daha kısa ve pratiktir. Ancak birden fazla tablonun birbirine bağlanacağı, tablolardan belirli verilerin çekileceği, diğerlerinin çekilmeyeceği gibi yapılarda 1. sorgu tipi, yani lambda ifadeli sorgu tipinde sıkıntı yaşama ihtimaliniz daha yüksektir. Metod’dan dönen değeri başka bir metoda, ondan döneni başka bir metoda şeklinde, union, join, where gibi metodların arka arkaya sıralanması hem okunabilirlik açısından hem de yazılabilirlik açısından epeyce zorlaşır. Yazının sonlarına doğru böyle karışık sorgulara örnekler vereceğim. O zaman daha iyi anlayacaksınız. Ancak öncesinde LINQ üzerinde kullanılan bazı yapılardan bahsedelim.

LINQ Metodları

Min, dizideki en küçük elemanın değerini döndürür, elemanın IComparable arayüzünden implement edilmiş olması gereklidir.
Max, dizideki en büyük elemanın değerini döndürür. Min ile aynı kullanımı sahiptir.
Sum, dizideki tüm elemanların toplamını döndürür.
Count, dizideki eleman sayısını verir. Toplam eleman sayısını verebileceği gibi, belirli kriterlerin uygulandığı eleman sayısının toplamını da verebilir.
FirstOrDefault, kritere uyan ilk elemanı döndürür. Herhangi bir kriter belirlenmezse dizideki ilk elemanı döndürür.
Any, dizide kritere uygun eleman olup olmadığını bool cinsinden döndürür, kriter girilmezse dizide eleman olup olmadığını kontrol eder.
Skip, girilen parametre kadar (int) dizinin başından o kadar elemanı atlar.
Take, girilen parametre kadar (int) diziden o kadar elemanı seçer.
OrderBy, dizideki elemanları girilen kritere göre A’dan Z’ye sıralar
OrderByDescending, dizideki elemanları girilen kritere göre Z’den A’ya sıralar.
Distinct, dizideki elemanları seçer ancak aynı değere sahip elemanları tek alır, dizide 5 adet elemanın değeri 2 ise, 1 adet 2 döner.
Select, her bir dizi elemanı içinden sadece belirli tipleri seçmeyi sağlar, “new” anahtar kelimesi altında çoklu alan seçilirse dönen değer anonimdir ve “var” olarak tanımlanmalıdır.
ToDictionary, çift parametreli olarak diziyi Dictionary tipine aktarır.
ToArray, IEnumerable<tip> olarak dönen diziyi tip[] şeklinde diziye çevirir.
ToList, IEnumerable<tip> olarak dönen diziyi List<tip> şeklinde diziye çevirir.
Except, dizinin, parametre olarak girilen 2. dizide olmayan elemanlarını listeler.
Union, diziye, parametre olarak girilen 2. dizinin elemanlarını ekler.
Intersect, dizinin, paramatre olarak girilen 2. dizi ile olan ortak elemanlarını listeler.

Yukarıdaki kullanımlardaki yapılan genel olarak koleksiyonlardır ancak ben hepsini dizi olarak anlatıyorum. Bu açıdan sıkıntı çıkacağını düşünmüyorum, dizi demeye devam 🙂

Şimdi tüm bunları geniş bir örnekle görelim. Örneğin kodlarını kopyalayıp yapıştırın ve çalıştırarak inceleyin. Daha iyi anlarsınız.

Örneğin daha opsiyonel olması açısından ve veritabanı işlemlerine benzerlik teşkil etmesi açısından dizi elemanlarını “Eleman” isimli bir sınıf olarak atadım. Ayrıca elemanları teker teker yukarıda tanımladıktan sonra diziye ekledim. Intersect, Union gibi işlemler için bunu böyle yapmam gerekiyordu. Çünkü iki elemanın eşitliği karşılaştırmasında iki sınıfın da heap bellekte aynı yerde bulunması önemliydi. Neyse kafanızı fazla karıştırmayım, örneği inceleyin 🙂

  1. using System;
  2. using System.Linq;
  3. using System.Collections.Generic;
  4. namespace helvacikoylu
  5. {
  6.     public class Eleman
  7.     {
  8.         public int Id { get; set; }
  9.         public string Isim { get; set; }
  10.         public byte Tip { get; set; }
  11.     }
  12.     internal class Program
  13.     {
  14.         static void Main(string[] args)
  15.         {
  16.             Eleman a1 = new Eleman {Id = 1, Isim = “C”, Tip = 1};
  17.             Eleman a2 = new Eleman {Id = 2, Isim = “C++”, Tip = 1};
  18.             Eleman a3 = new Eleman {Id = 3, Isim = “C#”, Tip = 1};
  19.             Eleman a4 = new Eleman {Id = 4, Isim = “Visual Basic”, Tip = 2};
  20.             Eleman a5 = new Eleman {Id = 5, Isim = “VB.NET”, Tip = 2};
  21.             Eleman a6 = new Eleman {Id = 6, Isim = “F#”, Tip = 3};
  22.             Eleman a7 = new Eleman {Id = 7, Isim = “ASP.NET”, Tip = 4};
  23.             Eleman a8 = new Eleman {Id = 8, Isim = “MVC”, Tip = 4};
  24.             Eleman b1 = new Eleman {Id = 11, Isim = “Pascal”, Tip = 1};
  25.             Eleman b2 = new Eleman {Id = 12, Isim = “Delphi”, Tip = 1};
  26.             Eleman b3 = new Eleman {Id = 13, Isim = “Java”, Tip = 2};
  27.             Eleman b4 = new Eleman {Id = 14, Isim = “Quick Basic”, Tip = 4};
  28.             List<Eleman> liste = new List<Eleman> {a1, a2, a3, a4, a5, a6, a7, a8};
  29.             List<Eleman> liste2 = new List<Eleman> {a1, a2, a5, a8};
  30.             List<Eleman> liste3 = new List<Eleman> {b1, b2, b3, b4};
  31.             //Min, Max, Sum, Count
  32.             Console.WriteLine(“-> en küçük Tip değeri : ” + liste.Min(x => x.Tip));
  33.             Console.WriteLine(“-> en büyük Tip değeri : ” + liste.Max(x => x.Tip));
  34.             Console.WriteLine(“-> Id değerlerinin toplamı : ” + liste.Sum(x => x.Id));
  35.             Console.WriteLine(“-> eleman sayısı : ” + liste.Count());
  36.             Console.WriteLine(“-> Tip değeri 1 olan eleman sayısı : ” + liste.Count(x => x.Tip == 1));
  37.             //FirstOrDefault, (First)
  38.             Console.WriteLine(“-> Tip değeri 2’den büyük elemanlardan ilkinin adı : ” + liste.FirstOrDefault(x => x.Tip > 2).Isim);
  39.             //Any
  40.             Console.WriteLine(“-> dizide Tip’i 4’ten büyük eleman var mı : ” + liste.Any(x => x.Tip > 4));
  41.             //Skip
  42.             Console.WriteLine(“-> ilk 4 eleman atlandıktan sonraki ilk elemanın adı : ” + liste.Skip(4).FirstOrDefault().Isim);
  43.             //OrderBy ve OrderByDescending
  44.             Console.WriteLine(“-> Isime göre A->Z ilk eleman : ” + liste.OrderBy(x => x.Isim).FirstOrDefault().Isim);
  45.             Console.WriteLine(“-> Isime göre Z->A ilk eleman : ” + liste.OrderByDescending(x => x.Isim).FirstOrDefault().Isim);
  46.             //Distinct
  47.             Console.WriteLine(“-> dizide kaç farklı tip değeri var : ” + liste.Select(x => x.Tip).Distinct().Count());
  48.             //Select
  49.             var e0 = liste.Select(x => new {Ad = x.Isim, IslemTip = x.Tip});
  50.             foreach (var e in e0)
  51.                 Console.WriteLine(“x-> Isim : ” + e.Ad + ” ve Tip : ” + e.IslemTip);
  52.             //ToDictionary
  53.             Dictionary<int, string> dict = liste.ToDictionary(x => x.Id, x => x.Isim);
  54.             Console.WriteLine(“-> Id’si 3 olan elemanın adı : ” + dict[3]);
  55.             //ToArray
  56.             Eleman[] a = liste.ToArray();
  57.             Console.WriteLine(“-> 5. elemanın adı : ” + a[5].Isim);
  58.             //Take
  59.             Console.WriteLine(“\n### dizideki ilk 4 elemanın isimleri:”);
  60.             IEnumerable<Eleman> e1 = liste.Take(4);
  61.             foreach (Eleman e in e1)
  62.                 Console.WriteLine(“-> eleman adı : ” + e.Isim);
  63.             Console.WriteLine(“#\n”);
  64.             //Except
  65.             Console.WriteLine(“\n### dizinin liste2’de olmayan elemanları:”);
  66.             IEnumerable<Eleman> e2 = liste.Except(liste2);
  67.             foreach (Eleman e in e2)
  68.                 Console.WriteLine(“-> eleman adı : ” + e.Isim);
  69.             Console.WriteLine(“#\n”);
  70.             //Union
  71.             Console.WriteLine(“-> liste ve liste3’ün birleşimindeki elemanlar:”);
  72.             IEnumerable<Eleman> e3 = liste.Union(liste3);
  73.             foreach(Eleman e in e3)
  74.                 Console.WriteLine(“-> eleman adı : ” + e.Isim);
  75.             Console.WriteLine(“#\n”);
  76.             //Intersect
  77.             Console.WriteLine(“-> liste ve liste2’nin kesişim elemanları:”);
  78.             IEnumerable<Eleman> e4 = liste.Intersect(liste2);
  79.             foreach(Eleman e in e4)
  80.                 Console.WriteLine(“-> eleman adı : ” + e.Isim);
  81.             Console.WriteLine(“#\n”);
  82.             Console.ReadLine();
  83.         }
  84.     }
  85. }

LINQ Sorguları

Yukarıdaki açıklama ve örneklerle artık LINQ üzerinde birçok metodun nasıl kullanıldığını ne ne işe yaradığını görmüş oldunuz. Dikkatinizi çekmek istediğim nokta, Select kullanımındaki örnektir. Select kullanımının sonucunda elimize dönen değer yine bir IEnumerable arayüzünü implement almış bir dizi evet, ancak dizideki her bir eleman Ad ve IslemTip isimli değişkenlere sahip. Fakat biz böyle değişkenlere sahip herhangi bir sınıf tanımlamadık. Zaten bu yüzden IEnumerable<Eleman> benzeri bir tanımlama yerine “var” tanımamasını kullandık. Yani anonim tipli bir yapı oldu bu. Bununla ilgli ayrıntılı bilgi başka bir yazının konusudur. Burada sadece bu kadarını görün yeter.

LINQ üzerindeki sorgular, bir cümle gibi okunaklığı bakımından daha iyidir. Ancak C# genel söz diziminden biraz daha uzaktır. Bunun yanısıra, bazı yerlerde kullanılması daha uzun ve karmaşık olurken, bazı kompleks sorgularda lambda ifadelerle içinden çıkamayacağınız sorguları çok rahat yapmanıza olanak sağlar. LINQ sorgularında, “Select” metodunu içinde kullanmak zorunda olmanız, size sadece gerekli alanları çekmenizi sağlamada bir uyarıcı etkendir. Çünkü lambda ifadeler ile çalışırken genellikle Select metodu çok nadir çağrılır ve kullanılmayan gereksiz bazı alanların da çekilmesine neden olan sorgular gerçekleştirilir.

Şimdi basit birkaç LINQ sorgusuna örneğimiz üzerinde göz atalım.

  1. using System;
  2. using System.Linq;
  3. using System.Collections.Generic;
  4. namespace helvacikoylu
  5. {
  6.     public class Eleman
  7.     {
  8.         public int Id { get; set; }
  9.         public string Isim { get; set; }
  10.         public byte Tip { get; set; }
  11.     }
  12.     internal class Program
  13.     {
  14.         static void Main(string[] args)
  15.         {
  16.             Eleman a1 = new Eleman {Id = 1, Isim = “C”, Tip = 1};
  17.             Eleman a2 = new Eleman {Id = 2, Isim = “C++”, Tip = 1};
  18.             Eleman a3 = new Eleman {Id = 3, Isim = “C#”, Tip = 1};
  19.             Eleman a4 = new Eleman {Id = 4, Isim = “Visual Basic”, Tip = 2};
  20.             Eleman a5 = new Eleman {Id = 5, Isim = “VB.NET”, Tip = 2};
  21.             Eleman a6 = new Eleman {Id = 6, Isim = “F#”, Tip = 3};
  22.             Eleman a7 = new Eleman {Id = 7, Isim = “ASP.NET”, Tip = 4};
  23.             Eleman a8 = new Eleman {Id = 8, Isim = “MVC”, Tip = 4};
  24.             Eleman b1 = new Eleman {Id = 11, Isim = “Pascal”, Tip = 1};
  25.             Eleman b2 = new Eleman {Id = 12, Isim = “Delphi”, Tip = 1};
  26.             Eleman b3 = new Eleman {Id = 13, Isim = “Java”, Tip = 2};
  27.             Eleman b4 = new Eleman {Id = 14, Isim = “Quick Basic”, Tip = 4};
  28.             List<Eleman> liste = new List<Eleman> {a1, a2, a3, a4, a5, a6, a7, a8};
  29.             List<Eleman> liste2 = new List<Eleman> {a1, a2, a5, a8};
  30.             List<Eleman> liste3 = new List<Eleman> {b1, b2, b3, b4};
  31.             IEnumerable<Eleman> sorgu1 = from x in liste where x.Tip > 2 select x;
  32.             var sorgu2 = from x in liste where x.Id > x.Tip select new {x.Id, x.Isim};
  33.             var sorgu3 = from x in liste
  34.                          from z in liste3
  35.                          where x.Tip == z.Tip
  36.                          select new
  37.                                     {
  38.                                         xIsim = x.Isim,
  39.                                         xTip = x.Tip,
  40.                                         zIsim = z.Isim,
  41.                                         zTip = z.Tip
  42.                                     };
  43.             Console.WriteLine(“# Sorgu 1 sonuçları :”);
  44.             Console.WriteLine(“Id\tIsim\tTip”);
  45.             foreach (var x in sorgu1)
  46.                 Console.WriteLine(x.Id + “\t” + x.Isim + “\t” + x.Tip);
  47.             Console.WriteLine(“\n\n\n”);
  48.             Console.WriteLine(“# Sorgu 2 sonuçları :”);
  49.             Console.WriteLine(“Id\tIsim”);
  50.             foreach (var x in sorgu2)
  51.                 Console.WriteLine(x.Id + “\t” + x.Isim);
  52.             Console.WriteLine(“\n\n\n”);
  53.             Console.WriteLine(“# Sorgu 3 sonuçları :”);
  54.             Console.WriteLine(“xIsim\txTip\tzIsim\tzTip”);
  55.             foreach (var x in sorgu3)
  56.                 Console.WriteLine(x.xIsim + “\t” + x.xTip + “\t” + x.zIsim + “\t” + x.zTip);
  57.             Console.ReadLine();
  58.         }
  59.     }
  60. }

Örneğimizde 3 sorgumuz mevcut. İlk sorgu lambda ifadelerle çok basit şekilde yazılabilecek bir sorgudur. Lambda ifadesi şuna denk gelir:

liste.Where(x => x.Tip > 2);

İkinci sorgumuz where ile seçimi yapıldıktan sonra listeden belirli kayıtların çekildiği sorgudur. Lambda ifadesi iki metodlu olarak yazılabilinir. Şuna benzer bir yapıda olur:

liste.Where(x => x.Id > x.Tip).Select(x => new {x.Id, x.Isim});

Üçüncü sorgumuz çok daha komplike bir sorgudur. Sorguda iki adet dizi birleştirilmiştir ve her bir diziye sırasıyla x,z isimleri atanmıştır. daha sonra where ile koşul olarak birinci dizinin tipiyle ikinci dizinin tipi eşitlenmiştir. Kısacası burada sadece tipleri birbirine eşit olan kayıtlar alınacaktır ve sonuç olarak iki diziden de veriler içeren bir anonim tip döndürülmüştür. Ben bunu lambda ifadelerle yazabilir miyim bilmiyorum. Birkaç dakika uğraştıktan sonra pes ettim. Ne gerek var ki 🙂

İşte LINQ sorgularının, lambda ifadelerine göre daha avantajlı olduğu bir yapıdır bu üçüncü sorgumuz. Genel olarak yazımın üçüncü sorgudaki gibi olmasını tavsiye ederim. İlk iki sorguda koşullar seçimler yanyana geldiğinden uzayan sorgularda okunabilirlik karışabilir. Ancak üçüncü sorgu gayet basittir. Bu sorguyu hemen satır satır inceleyelim.

1. satırda “from x in liste” bildirimi ile, sorgulanacak her kayıttan, x olanları liste dizisinin elemanını temsil edeceği ifade ediliyor.
2. satırda “from z in liste3” bildirimi ile, sorgulanacak her kayıttan, z olanları liste3 dizisinin elemanını temsil edecei ifade ediliyor.
3. satırda “where x.Tip == z.Tip” bildirimi ile, sadece x’lerin Tip değeriyle z’lerin Tip değeri eşit olan kayıtları seçileceği ifade ediliyor.
4. ve diğer satırlarda dönecek anonim tipin içinde hangi değişkenlerin hangi isimlerle olması gerektiği bildiriliyor.

Bu yazıya daha da fazla uzatmayalım, şimdilik hoşçakalın.

Share Button

Comments 0

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

* Copy This Password *

* Type Or Paste Password Here *

12.860 Spam Comments Blocked so far by Spam Free Wordpress

More From: Asp.NET

DON'T MISS