30 Aralık 2012 Pazar

C# ile LINQ Kullanarak Verilere Sorgu Yazma

Merhaba Webkolog ailesi!

Bugün, C# .NET Framework 4'ün en güçlü ve en çok sevilen özelliklerinden biri olan LINQ (Language Integrated Query) konusuna dalış yapacağız. Microsoft Visual Studio 2010 ile başladığımız bu kodlama macerasında, LINQ'in verilerle etkileşim şeklimizi nasıl kökten değiştirdiğini ve kodlarımızı nasıl daha okunabilir, anlaşılır ve verimli hale getirdiğini adım adım keşfedeceğiz. Eğer verileri filtrelemek, sıralamak, gruplamak veya birleştirmek için hala geleneksel döngüler ve koşullar yazıyorsanız, LINQ ile tanıştığınıza çok sevineceksiniz!

LINQ Nedir ve Neden Hayatımızı Kolaylaştırır?

Geleneksel olarak, veritabanlarından (SQL), XML dosyalarından veya koleksiyonlardan (bellek içi nesneler) veri çekmek ve işlemek için her bir veri kaynağına özgü farklı sorgulama dilleri veya API'ler kullanırdık. Bu durum, kod tekrarına ve farklı veri kaynakları arasında geçiş yaparken öğrenme eğrilerine neden oluyordu.

LINQ, .NET Framework 3.5 ile tanıtılan ve C# 3.0 ile birlikte gelen devrim niteliğinde bir özelliktir. Temel amacı, her türlü veri kaynağına (bellek içi nesneler, veritabanları, XML belgeleri vb.) standart, SQL benzeri bir sorgulama sözdizimi ile erişim sağlamaktır. Yani, ister bir List<T> üzerinde çalışın, ister bir SQL Server veritabanından veri çekin, aynı LINQ sorgu sözdizimini kullanabilirsiniz. Bu, geliştirici verimliliğini inanılmaz derecede artırır ve kodunuzu çok daha temiz ve okunaklı hale getirir.

LINQ Operatörleri: Veri İşleme Araç Kutunuz

LINQ, veriler üzerinde çeşitli işlemler yapmanızı sağlayan bir dizi standart sorgu operatörü sunar. Bu operatörler iki ana şekilde kullanılabilir: Sorgu Sözdizimi (Query Syntax) ve Metot Sözdizimi (Method Syntax / Extension Methods).

1. Kısıtlayıcı (Restriction) Operatörler

  • Where: Belirli bir koşula uyan elemanları filtrelemek için kullanılır.
    string[] sehirler = { "Ankara", "İstanbul", "İzmir", "Adana", "Antalya"};
    // Sorgu sözdizimivaruzunSehirlerQS = froms insehirler wheres.Length> 6selects;
    // Metot sözdizimivaruzunSehirlerMS = sehirler.Where(s => s.Length> 6);
    
    // Birden fazla koşulvarAileBaslayanUzunSehirler = sehirler.Where(s => s.StartsWith("A") && s.Length> 5);
    

2. İzdüşüm (Projection) Operatörler

  • Select: Her bir elemandan belirli özellikleri seçmek veya yeni bir anonim tip oluşturmak için kullanılır.
    // Sadece elemanın kendisini seçme (genellikle filtreleme sonrası kullanılır)vartumSehirler = froms insehirler selects; // veya sehirler.Select(s => s);// Her bir şehir için uzunluğunu almavarsehirUzunluklari = sehirler.Select(s => s.Length);
    
    // Yeni bir anonim tip oluşturma (örneğin, şehir adı ve uzunluğu)varsehirBilgileri = sehirler.Select(s => new{ Ad = s, KarakterSayisi = s.Length });
    
    foreach(varitem insehirBilgileri)
    {
        Console.WriteLine("Şehir:" + item.Ad}, Karakter Sayısı:" + item.KarakterSayisi);
    }
    

3. Sıralama (Ordering) Operatörler

  • OrderBy, ThenBy, OrderByDescending, ThenByDescending: Elemanları belirli bir kritere göre artan veya azalan sırada sıralamak için kullanılır.
    // Alfabetik olarak sıralamavarsiraliSehirler = froms insehirler orderbys selects;
    // Metot sözdizimivarsiraliSehirlerMS = sehirler.OrderBy(s => s);
    
    // Tersten alfabetik sıralamavarterstenSiraliSehirler = froms insehirler orderbys descendingselects;
    // Metot sözdizimivarterstenSiraliSehirlerMS = sehirler.OrderByDescending(s => s);
    
    // Çoklu kritere göre sıralama (Örneğin, önce uzunluğa göre, sonra alfabetik)varcokluSiraliSehirler = sehirler.OrderBy(s => s.Length).ThenBy(s => s);
    

4. Gruplama (Grouping) Operatörler

  • Group By: Elemanları belirli bir anahtara göre gruplamak için kullanılır.
    varsehirlerGrubu = froms insehirler
                        groups bys[0] intog // İlk harfe göre gruplaselectnew{ IlkHarf = g.Key, Sehirler = g };
    
    foreach(vargrup insehirlerGrubu)
    {
        Console.WriteLine("İlk harf:" + grup.IlkHarf);
        foreach(varsehir ingrup.Sehirler)
        {
            Console.WriteLine("\t- {sehir);
        }
    }
    

5. Bölümleme (Partitioning) Operatörler

  • Take, Skip: Bir koleksiyonun başından veya belirli bir noktadan itibaren belirli sayıda eleman almak veya atlamak için kullanılır.
    // İlk 3 şehri alvarilkUcSehir = sehirler.Take(3);
    // İlk 2 şehri atla, kalanları alvarilkIkiyiAtla = sehirler.Skip(2);
    

6. Küme (Set) Operatörleri

  • Distinct, Union, Intersect, Except: Küme işlemleri (benzersiz elemanlar, birleşim, kesişim, fark) için kullanılır.
    List sayilar1 = newList { 1, 2, 3, 4, 5, 2};
    List sayilar2 = newList { 4, 5, 6, 7};
    
    varbenzersizSayilar = sayilar1.Distinct(); // 1, 2, 3, 4, 5varbirlesim = sayilar1.Union(sayilar2);    // 1, 2, 3, 4, 5, 6, 7varkesisim = sayilar1.Intersect(sayilar2); // 4, 5varfark = sayilar1.Except(sayilar2);      // 1, 2, 3
    

7. Toplama (Aggregate) Operatörler

  • Count, Sum, Min, Max, Average: Koleksiyonlar üzerinde toplama işlemleri yapmak için kullanılır.
    int[] notlar = { 85, 92, 78, 65, 95};
    Console.WriteLine("Toplam not:" + notlar.Sum());
    Console.WriteLine("Ortalama not:" + notlar.Average());
    Console.WriteLine("En yüksek not:" + notlar.Max());
    Console.WriteLine("Geçen öğrenci sayısı (>=70):" + notlar.Count(n => n>= 70));
    

8. Niteleyici (Quantifier) Operatörler

  • Any, All: Koleksiyondaki elemanların belirli bir koşulu sağlayıp sağlamadığını kontrol eder. bool dönerler.
    Console.WriteLine("100'den küçük herhangi bir sayı var mı? {notlar.Any(n => n < 100)); // TrueConsole.WriteLine("Tüm sayılar 50'den büyük mü? {notlar.All(n => n > 50)); // True

9. Eleman (Element) Operatörler

  • First, Last, FirstOrDefault, LastOrDefault: Koleksiyondan belirli bir elemanı almak için kullanılır. *OrDefault versiyonları, eleman bulunamazsa varsayılan değeri (null veya 0 gibi) döndürür, hata fırlatmaz.
    Console.WriteLine("İlk sayı:" + notlar.First()); // 85
    Console.WriteLine("Son sayı:" + notlar.Last());  // 95
    Console.WriteLine("60'tan büyük ilk sayı:" + notlar.FirstOrDefault(n => n> 60)); // 85
    Console.WriteLine("100'den büyük son sayı:" + notlar.LastOrDefault(n => n> 100)); // 0 (int için varsayılan değer)

10. Dönüştürme (Conversion) Operatörler

  • ToArray, ToList, ToDictionary, OfType: Sorgu sonuçlarını farklı koleksiyon tiplerine dönüştürmek için kullanılır.
    Sorgu sonucunu List<int> olarak almaList<int> buyukSayilar = sayilar1.Where(s => s > 3).ToList();

LINQ Sorgu Sözdizimi ve Metot Sözdizimi Karşılaştırması

LINQ'i kullanmanın iki yolu vardır:

  • Sorgu Sözdizimi (Query Syntax): SQL'e daha yakın ve daha okunabilir bir yapı sunar. from ... where ... select ... yapısını kullanır.
    varsonucQS = froms insehirler
                  wheres.Contains("a") && s.Length> 5orderbys
                  selects;
    
  • Metot Sözdizimi (Method Syntax / Extension Methods): Daha esnektir ve nokta notasyonu (.) ile zincirleme metot çağrıları şeklinde kullanılır. Lambdalarla birlikte çok güçlüdür.
    varsonucMS = sehirler
                  .Where(s => s.Contains("a") && s.Length> 5)
                  .OrderBy(s => s)
                  .Select(s => s);
    

Her ikisi de aynı işlevi görür ve genellikle kişisel tercihe veya projenin kodlama standartlarına bağlı olarak seçilir. Ancak iç içe veya karmaşık sorgularda Metot Sözdizimi daha güçlü olabilir.

LINQ to Objects, LINQ to Entities (SQL), LINQ to XML

LINQ'in güzelliği, farklı veri kaynaklarına uygulanabilmesidir:

1. LINQ to Objects

Bellek içi koleksiyonlar (List<T>, diziler vb.) üzerinde sorgulama yapmak için kullanılır. Yukarıdaki tüm örnekler LINQ to Objects kapsamındadır.

2. LINQ to Entities (SQL)

Veritabanları ile etkileşim kurmak için kullanılır. Entity Framework veya LINQ to SQL gibi ORM (Object-Relational Mapping) araçları aracılığıyla LINQ sorgularınızı SQL sorgularına dönüştürür ve veritabanında çalıştırır. Microsoft Visual Studio 2010'da bir "Entity Data Model" oluşturarak başlayabilirsiniz.

// Örnek Entity Framework Kullanımı (DatabaseEntities = veri tabanı context'iniz)// using System.Data.Objects; (SQL sorgusuna çevirmek için)DatabaseEntities de = newDatabaseEntities();
// Musteriler tablosundan MusteriId'si 1 olanı seçvarmusteri = fromm inde.Musteriler wherem.MusteriId == 1selectm;
// Tablo2'sinde Giriş değeri 275'ten büyük olan Tablo1 kayıtlarını getirvartbl = fromt1 inde.Tablo
          wheret1.Tablo2.Any(t2 => t2.Giris> 275) // İç içe tablo ve Any kullanımıselectt1;
// Sorguyu SQL'e çevirme (debugging veya loglama için faydalı)// string sorguSQL = ((System.Data.Objects.ObjectQuery)musteri).ToTraceString();// Console.WriteLine(sorguSQL);// Kayıt EklemeMusteri yeniMusteri = newMusteri(); // Musteri EF tarafından oluşturulan bir sınıfyeniMusteri.Adi = "Ali";
yeniMusteri.Soyadi = "Mantar";
yeniMusteri.Yas = 29;
de.Musteriler.AddObject(yeniMusteri); // Veya de.AddToMusteri(yeniMusteri);de.SaveChanges(); // Değişiklikleri veritabanına kaydet// Kayıt GüncellemevarguncellenecekMusteri = (fromm inde.Musteriler wherem.MusteriId == 1selectm).FirstOrDefault();
if(guncellenecekMusteri != null)
{
    guncellenecekMusteri.Adi = "Veli";
    de.SaveChanges();
}
// Kayıt SilmevarsilinecekMusteri = (fromm inde.Musteriler wherem.MusteriId == 2selectm).SingleOrDefault();
if(silinecekMusteri != null)
{
    de.Musteriler.DeleteObject(silinecekMusteri);
    de.SaveChanges();
}

3. LINQ to XML

XML belgelerini sorgulamak ve manipüle etmek için kullanılır. System.Xml.Linq namespace'i ile çalışır.

usingSystem.Xml.Linq;
// Örnek bir Satislar sınıfı (daha önceki makalelerimizden)publicclassSatis{
    publicDateTime Tarih { get; set; }
    publicList Urunler { get; set; }
}
publicclassUrun{
    publicstringUrunAdi { get; set; }
    publicintMiktar { get; set; }
    publicdecimalFiyat { get; set; }
}
List satislar = newList
{
    newSatis { Tarih = newDateTime(2023, 1, 10), Urunler = newList { newUrun { UrunAdi = "Elma", Miktar = 5, Fiyat = 2.5m } } },
    newSatis { Tarih = newDateTime(2023, 1, 15), Urunler = newList { newUrun { UrunAdi = "Armut", Miktar = 3, Fiyat = 3.0m }, newUrun { UrunAdi = "Elma", Miktar = 2, Fiyat = 2.5m } } }
};
// Satış listesini XML'e dönüştürmevarsatisXML = newXElement("Satislar",
    froms insatislar
    selectnewXElement("Satis",
        newXAttribute("SatisTarih", s.Tarih.ToShortDateString()),
        newXAttribute("ToplamMiktar", s.Urunler.Sum(i => i.Miktar)),
        newXElement("Urunler",
            fromu ins.Urunler
            selectnewXElement("Urun",
                newXAttribute("Ad", u.UrunAdi),
                newXAttribute("Miktar", u.Miktar)
            )
        )
    )
);
Console.WriteLine(satisXML.ToString());
/* Çıktı örneği:

  
    
      
    
  
  
    
      
      
    
  

*/

LINQ ile Join İşlemleri

LINQ, farklı veri kaynaklarını birleştirmek için güçlü join operatörleri de sunar.

// Varsayımsal SatisListesi ve Urunler sınıflarıpublicclassSatisBilgisi{
    publicintSatisID { get; set; }
    publicDateTime Tarih { get; set; }
    publicdecimalSatisTutari { get; set; }
    publicList Urunler { get; set; }
}
publicclassSatisUrun{
    publicintUrunID { get; set; }
    publicstringUrunAdi { get; set; }
    publicintMiktar { get; set; }
}
publicclassUrunDetay{
    publicintUrunID { get; set; }
    publicstringUrunAdi { get; set; }
    publicdecimalUrunFiyat { get; set; }
}
List satisListesi = newList { /* Veriler burada doldurulur */};
List urunler = newList { /* Veriler burada doldurulur */};
// İç içe koleksiyonları tek bir liste gibi sorgulama (SelectMany)vartumSatisUrunleri = satisListesi.SelectMany(s => s.Urunler);
// Geleneksel Join Kullanımı (Sorgu Sözdizimi)varsatisUrunleriDetayli = froms insatisListesi
                           fromsui ins.Urunler // SatisBilgisi içindeki Urunler'e erişimjoinu inurunler onsui.UrunAdi equalsu.UrunAdi
                           selectnew{ s.SatisID, sui.UrunAdi, sui.Miktar, u.UrunFiyat, Toplam = sui.Miktar * u.UrunFiyat };
// Join Metot Sözdizimi (SelectMany ile daha esnek)varsatisUrunDetayliMS = satisListesi.SelectMany(s => s.Urunler, (satis, urunBilgisi) => new{ satis, urunBilgisi })
                                     .Join(urunler,
                                           satUrun => satUrun.urunBilgisi.UrunAdi,
                                           uDetay => uDetay.UrunAdi,
                                           (satUrun, uDetay) => new{ satUrun.satis.SatisID, satUrun.urunBilgisi.UrunAdi, satUrun.urunBilgisi.Miktar, uDetay.UrunFiyat });
// Group Join (ilişkili verileri gruplama)varurunlerVeSatislari = urunler.GroupJoin(
    satisListesi.SelectMany(s => s.Urunler), // Tüm satış ürünlerini tek bir liste gibi düşünurunDetay => urunDetay.UrunAdi, // Birleştirme anahtarı (Ürünler listesinden)satisUrun => satisUrun.UrunAdi, // Birleştirme anahtarı (Satış Ürünleri listesinden)(urunDetay, ilgiliSatisUrunleri) => new{ UrunAdi = urunDetay.UrunAdi, Satislar = ilgiliSatisUrunleri.ToList() }
);
foreach(varurunGrup inurunlerVeSatislari)
{
    Console.WriteLine("Ürün: " + urunGrup.UrunAdi);
    if(urunGrup.Satislar.Any())
    {
        foreach(varsatis inurunGrup.Satislar)
        {
            Console.WriteLine("\t- Satış Miktarı: " + satis.Miktar);
        }
    }
    else{
        Console.WriteLine("\t- Bu ürüne ait satış bulunmamaktadır.");
    }
}

Sonuç

LINQ, .NET geliştiricileri için gerçekten oyunun kurallarını değiştiren bir özelliktir. Veri manipülasyonunu ve sorgulamayı inanılmaz derecede kolaylaştırır, kodunuzu hem daha kısa hem de daha anlaşılır hale getirir. İster bellek içi koleksiyonlarla çalışın, ister veritabanlarıyla, ister XML belgeleriyle, LINQ size tutarlı ve güçlü bir sorgulama dili sunar. Başlangıçta sorgu sözdizimi size SQL'e daha yakın gelebilirken, metod sözdizimi lambda ifadeleriyle birlikte daha fazla esneklik ve güç sağlayabilir.

LINQ'i ne kadar çok kullanırsanız, veriyle çalışırken o kadar üretken olursunuz. Her yeni projede ve mevcut projelerinizde LINQ'i kullanmaya başlamanızı şiddetle tavsiye ederim. Verilere bir döngüyle erişmeden önce, "Bunu LINQ ile nasıl yapabilirim?" diye düşünmeyi alışkanlık haline getirin!

Webkolog'u takipte kalın!

Hepinize bol kodlu ve başarılı projeler dilerim!

0 yorum:

Yorum Gönder