C#Yazılım

C# Serisi 0x06 – LINQ Nedir? Neden Kullanılır?

Merhaba,

C# serisinin bu bölümünde veritabanı sorguları için daha güçlü ve efektif bir kullanım sunan LINQ Kütüphanesini inceleyeceğiz. Günümüzde yazılım projelerinin çoğunluğunda veritabanı sistemleri kullanılmakta ve projeler içerisinde genellikle veritabanı programlama süreçleri bulunmaktadır. E-Ticaret sistemlerinde, araç kiralama uygulamalarında, otel ve tatil rezarvasyon sistemlerinde ve daha bir çok veri depolanması gerektiren sistemlerde veritabanlarıyla entegre bir şekilde çalışılması gerekmektedir. Bu durumda yazılım ile veritabanı arasında (Business katmanı ve Data Access katmanı arasında) fazlasıyla sorgu yapılmakta ve cevap döndürülmektedir. En basit şekilde e-ticaret sitelerinin hemen hemen tamamında kullanılan FİLTRELEME işlevini örnek verebiliriz. Bir filtreme işlemi içerisinde örneğin fiyatı 5000 TL’Den yüksek ve mevcut stok miktarı 10 üzeri olan ürünlerin listelenmesi gibi bir filtreleme yapılabilmektedir. Yada Ürün rengi mavi ve markası X olan ürünlerin gösterilmesi gibi bir filtreleme yapılabilmektedir. Gördüğünüz gibi filtr seçenekleri ve kombinasyonları epey çoğaltılabilmektedir. Durum böyle olunca yazılım içerisinde karmaşık kod satırları meydana gelebilir. Bir filtre sorgusu foreach döngüsü içerisine if şartı koyularak bu şekilde filtre elemanları kontrol edilebilir ve uygun ürünler bir listeye eklnerek kullanıcıya döndürülebilir. Fakat az önce bahsettiğimiz gibi filtre sayısı ve kombinasyonları arttıkça kod kalabalığı artacaktır. Tamda bu noktada LINQ kütüphanesi sayesinde işlemlerimizi bir satır kod ile yapabileceğiz.  Birazdan bunları uygulayarak detaylı olarak göreceğiz.

Her zaman olduğu gibi kodların tam çalıştırılabilir halini Githubta paylaşıyor olacağım. Github linkini ise blogun en altına ekleyeceğim. Umarım tam manasıyla istifade edebilirsiniz.

 

LINQ Nedir?
  • Açılımı “Language Integrated Query” dir.
  • Farklı veri kaynaklarından veri sorgulamak için kullanılan güçlü bir sorgu dilidir.
  • C# vb. programlama dillerinde kullanılmaktadır. Fakat bir çok programlama dilinde yoktur.
  • En güçlü olarak C# dili içerisinde kullanılmaktadır.
  • Karmaşık veri işlemleri için farklı veri kaynaklarından tek satırlık sorgular ile çalışmaktadır. (Kaydet, Güncelle, Sil, Ekle, Filtrele, vb.)
  • Programlama dili (C#) ile veri tabanı arasındaki bağlantıyı kurmaktadır.
  • Nesneler ile uyumlu olarak çalışabilmektedir.
  • SQL, Object, DataSet, Xml gibi veri sağlayıcılarıylada sorgular yürütebilmektedir.

 

LINQ Faydaları Nelerdir?
  • Karmaşık sorguların daha okunabilir bir şekilde yazılmasını sağlar.
  • Kod okunabilirliğini artırır.
  • Farklı veritabanlarıyla uyum içerisinde çalışabilir.
  • Derleme sırasında nesne verilerinin kontrolünün yapılmasını sağlar.
  • Verileri farklı koleksiyonlara dönüştürebilir. (Örneğin Where ifadesiyle dizi oluşturabilir ve bunu toList() ile bir listeye dönüştürebiliriz.)
  • Karmaşık sorguların daha az kod yazılarak yapılmasını sağlar. (10 satırlık bir kodun 1 satırda yazılması gibi.. )

 

LINQ NASIL KULLANILIR ?

LINQ kullanımını yeni bir C# projesi oluşturarak göstereceğim. Öncelikle Solution üzerinde sağ tıklayarak Add>New Project seçeneğini seçerek yeni bir Console App oluşturalım ve ismini LinqProject olarak belirleyelim. Burada isimlendirme noktasına dikkat ediniz aksi halde bazı sorunlar yaşayabilirsiniz. Linq olarak yapılan isimlendirmeler sorun çıkarabilicektir. Bunun sebebi C#’ta  Linq adında bir namespace olmasıdır. Bu yüzden projemizin adını LinqProject yada farklı bir isimle kaydedebilirsiniz.

Projeyi oluşturduktan blogun en başında gerçek hayattan kısaca senaryolaştırdığımız şekilde bir e-ticaret sistemini simüle ederek devam edelim. E-ticaret sistemlerinde bulunan ürün ve kategori nesnelerini kullanarak sorgumuzu oluşturabiliriz. E-Ticaret sistemlerini kodlanırken kategori isimlerinin her bir ürün niteliğinde tek tek belirtilmesi tercih edilmez. Bunun yerine ilişkisel olarak kategori sınıfı içerisinde bir isimlendirme yapılır. (Burada simülasyon için sınıf içerisinde atamaları yaptık.Normalde veritabanında ayrı bir tabloda bu karşılıklar tutulur. “CategoryId = CategoryName” şeklinde saklanır.) Biz burada her bir ürüne yalnızca ilgili kategorinin ID’sini yazarız. Bunun sağladığı avantaj şudur: ilerleyen zamanlarda kategori isminin değiştirilmesi söz konusu olduğunda tek tek tüm ürünleri düzenlememiz gerekmeyecektir. Bunu yerine kategori tablosundaki kategori Id değerinin karşılığındaki tek bir ifadeyi güncellememiz yeterli olacaktır. Bir e-ticaret sisteminde binlerce ürünün yer aldığını düşünürsek bu kullanım bize çok avantaj sağlayakcatır. Aksi halde iş içinden çıkılmaz bir duruma sürüklenecektir.

Projemizi oluşturduk basitçe program.cs içerisinde Product ve Category adında iki tane sınıfımızı oluşturduk. Bu sınıflar şu şekildedir.

Product Sınıfı:

class Product
    {
        public int ProductId { get; set; }
        public int CategoryId { get; set; }
        public string ProductName { get; set; }
        public string QuantityPerUnit { get; set; }
        public decimal UnitPrice { get; set; }
        public int UnitInStock { get; set; }
    }

 

Category Sınıfı:

class Category
    {
        public int CategoryId { get; set; }
        public string CategoryName { get; set; }
    }

Oluşturduğumuz sınıflar veritabanı içerisindeki iki farklı tabloyu simüle etmektedir. Görüldüğü gibi ilişkisel bir durum vardır. Her iki tablodada yer alan “CategoryId” değeri bulunmaktadır.

Sınıflarımızı oluşturduktan sonra 2 adet farklı kategori ekleyelim ve birkaç tane yeni ürün tanımlayalım. Burada ürümlerimizi ve kategorilerimizi istediğimiz gibi seçebilir ve düzenleyebiliriz. Ben burada Bilgisayar ve Telefon kategorisi olarak 2 adet kategori ekledim.

 

Kategorilerin oluşturulması:

List<Category> categories = new List<Category>
            {
                new Category{CategoryId=1, CategoryName="Bilgisayar"},
                new Category{CategoryId=2, CategoryName="Telefon"},
            };

 

Ürünlerin tanımlanması:

  List<Product> products = new List<Product>
            {
                new Product{ProductId=1, CategoryId=1, ProductName="MSI Laptop", QuantityPerUnit="32 GB RAM", UnitPrice=12560,UnitInStock=10},
                new Product{ProductId=2, CategoryId=1, ProductName="Lenovo Laptop", QuantityPerUnit="8 GB RAM", UnitPrice=8060,UnitInStock=7},
                new Product{ProductId=3, CategoryId=1, ProductName="Asus Laptop", QuantityPerUnit="16 GB RAM", UnitPrice=5000,UnitInStock=2},
                new Product{ProductId=4, CategoryId=2, ProductName="Samsung Telefon", QuantityPerUnit="4 GB RAM", UnitPrice=3500,UnitInStock=15},
                new Product{ProductId=5, CategoryId=2, ProductName="Apple Telefon", QuantityPerUnit="4 GB RAM", UnitPrice=8000,UnitInStock=0},
            };

 

Şuana kadar Product ve Category sınıflarını (tablo simülasyonu) oluşturduk ve daha sonra 2 adet kategori tanımlayarak (nesne oluşturduk) 5 farklı ürün tanımladık. Bu noktada LINQ kullanımını burada çok daha iyi anlayacağız. Az önce e-ticaret sistemlerindeki filtreleme işlemlerinden bahsetmiştim. Şimdi burada bu filtreleme işlemini simüle ederek 2 farklı ölçüte göre ürünleri müşteriye gösterilmesini sağlayalım. İlk olarak uzun yöntem ile yani LINQ kullanmadan foreach döngüsü içerisinde ölçütleri if bloğuyla kontrol ederek şartları sağlayan ürünleri  simülasyon için ekrana yazdıralım.

 

Program içerisinde kontrolün yapılması:

foreach (var product in products)
            {
                if (product.UnitPrice > 5000 && product.UnitInStock > 3)
                {
                    Console.WriteLine(product.ProductName);
                }

            }

Evet şuan foreach döngüsü ile tüm ürünleri tek tek gezerek ürün fiyatı 5000 üzeri ve ürün stok miktarı 5 üzeri olanların gösterilmesini sağladık. Ve bu şartlara uyan ürünleri konsola yazdırdık. Şimdi bu ifadeyi gerçek hayatta olduğu gibi bir fonksiyon içerisinde yazalım ve ürünleri return etmesini sağlayalım. Bu noktada kod karmaşıklığının basit bir ifade için bile ne kadar arttığını göreceksiniz.

 

Fonksiyon ile kontrolün yapılması:

static List<Product> GetProducts(List<Product> products)
        {
            List<Product> filteredProducts = new List<Product>();

            foreach (var product in products)
            {
                if (product.UnitPrice > 5000 && product.UnitInStock > 3)
                {
                    filteredProducts.Add(product);
                }
            }

            return filteredProducts;
        }

 

Bu fonksyion ile az önceki işlemi biraz daha gerçek hayata uyarlamış olduk. Burada görüldüğü gibi yaklaşık 10 satırlık bir kod ile kontrol yapıldı ve şartı sağlayan ürünler filteredProducts listesine eklendi daha sonrada müşteriye gösterilmek üzere return edildi. Evet buraya kadar eski yöntemle tabiri caizse amelyus yöntemiyle filtreleme yaptık. Şimdi konumuza gelelim ve bu filtreleme işlemini LINQ yapısıyla bir satırda yazalım ve farkı gözlerimizle görelim. LINQ yapısı içinde önce program içerisinde doğrulama yapacağım daha sonrasında fonksiyondan return edilmesini göstereceğim. Bu sayede her iki farkıda rahatça analiz edebilirsiniz.

 

Program içerisinde kontrolün yapılması:

var result = products.Where(product => product.UnitPrice > 5000 && product.UnitInStock>5);  //Tek satırlık sorgu.
            foreach (var product in result)
            {
                Console.WriteLine(product.ProductName);
            }

 

Az önce foreach ile tek tek kontrol yaptığımız kodu şimdi tek satır içerisinde daha okunabilir bir şekilde yazmış olduk. Bu noktada her bir product için belirtilen kontrol yapılacaktır. Anlaşılabilir olması için product olarak yazdım. Normalde syntax olarak tek kelime olanlarda yalnızca kelimenin baş harfi yani p olarak yazılır. Eğer iki kelime ise her kelimenni baş harfi kullanılır. Şimdi LINQ yapısı bir fonksiyon içerisine yerleştirelim ve önceki foreach ve if yapısıyla kıyaslayalım.

 

Fonksiyon ile kontrolün yapılması:

static List<Product> GetProductsLinq(List<Product> products)
        {
            return products.Where(product => product.UnitPrice > 5000 && product.UnitInStock > 3).ToList(); 
        }

 

Şuan LINQ yapısının sağladığı kolaylığı çok daha açık bir şekilde görebiliyoruz. Burada yazdığımız tek satırlık kod arka planda bir döngü gerçekleştirir ve yeni bir array oluşturur. Bu döngüde belirtilen şartları sağlayan nesneleri array içerisine ekler. Daha sonra bunları return eder. Kodun en son kısmında ToList() ifadesini kullandık. Çünkü Where ifadesi bize bir array (dizi) döndürecektir. Biz ise burada bir liste döndürülmesini istediğimiz için ToList() ifadesiyle array’i listeye dönüştürdük ve liste olarak return ettik.

 

Bu noktaya kadar her şeyi tam olarak okudunuz ve incelediyseniz LINQ yapısının temel kullanımını anlamış olmalısınız. Burada daha iyi kıyaslama yapılabilmesi açısından hem program içerisinden hemde bir fonksiyon içerisinden kontrolün yapılmasını LINQ kullanmadan ve LINQ kullanarak ayrı ayrı yazdım ve ekledim. Bu sayede “Neden LINQ Kullanmalıyız ? ” sorusunuda cevaplamış olduğumuza inanıyorum. LINQ sorguları ve kullanımı ile ilgili tabikide ileri uygulamar söz konusudur. Bunun için daha çok araştırma ve uygulama yaparak LINQ yapısını iyice benimsemeliyiz. Buradan çıkarabiliceğimiz sonuç şudur: LINQ yapısı ile daha az kod yazarak aynı işi yapabileceğimizi ve temiz bir kod yazabileceğimizdir.

Okuduğunuz için teşekkür ederim. Programın tam çlıştırılabilir halini github prolime ekledim. Burayada linki bırakıyorum. Programı çalıştırarak çıktılarını inceleyebilirsiniz. Bu şekilde daha iyi anlayabileceğinize inanıyorum. Mutlaka kodları kendiniz yazarak uygulama yapınız.Sizde eklemek istediklerinizi ve sorularınızı yorum olarak yazarsanız hem ben yeni bilgiler edinmiş olurum hemde aynı soruları merak edenler bu sayede sorularına cevap alabilirler.

Serinin sonraki bölümlerinde görüşmek üzere.

Sağlıklı ve iyi günler dilerim.

Teşekkür ederim.

 


Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir