Selamlar,
Veri işleme süreçlerinde, veri koleksiyonları üzerinde işlem yapmak ve veritabanlarından veri almak, modern yazılım geliştirmenin önemli bir parçasıdır. Bu süreçlerde, IEnumerable ve IQueryable gibi arabirimler sıklıkla karşılaşılır. Bu iki arabirim, veri manipülasyonunda önemli farklılıklar taşır ve doğru seçimin yapılmadığı durumlarda performans ve verimlilik açısından büyük etkileri olabilir. Bu yazıda, IEnumerable ve IQueryable arasındaki temel farkları anlamak ve hangi durumlarda hangisini kullanmanın daha uygun olabileceğini inceleyeceğiz.
IEnumerable
IEnumerable, System.Collections namespace i altında bulunan bir arayüz tipidir. Bir IEnumerable nesnesi, bir koleksiyon üzerinde iterable olduğunu belirtir. Yani, koleksiyonun elemanlarına birer birer erişilebilir. IEnumerable arayüzü, sadece GetEnumerator() adında bir metot tanımlar. Bu metot, koleksiyonun elemanlarını döndüren bir IEnumerator nesnesi verir. IEnumerator arayüzü ise, Current adında bir özellik ve MoveNext() ve Reset() adında iki metot tanımlar. Current özelliği, koleksiyonun o anki elemanını verir. MoveNext() metodu, koleksiyonun sonraki elemanına geçer ve eğer varsa true, yoksa false döndürür. Reset() metodu ise, koleksiyonun başına döner. Bu sayede, koleksiyonun elemanları üzerinde yinelenebilir.
IEnumerable arayüzünün bir de IEnumerable<T> adında bir alt arayüzü vardır. Bu arayüz, generic bir arayüzdür. Yani, koleksiyonun elemanlarının tipini belirtir. Örneğin, IEnumerable<string> bir string koleksiyonu, IEnumerable<int> bir int koleksiyonu olduğunu belirtir. IEnumerable<T> arayüzü, IEnumerable arayüzünden kalıtım alır. Bu sayede, IEnumerable<T> arayüzüne sahip olan bir nesne, IEnumerable arayüzüne sahip olan bir nesne gibi davranabilir.
IEnumerable arayüzünün avantajı, koleksiyonlar üzerinde yinelenebilirlik sağlaması, koleksiyonun elemanlarına erişim imkanı vermesi, koleksiyonun elemanlarını filtreleme, sıralama, gruplama gibi işlemler yapabilmesidir. Örneğin, bir array, bir list, bir dictionary gibi koleksiyonlar IEnumerable arayüzünü uygular. Ayrıca, LINQ (Language Integrated Query) ile bu koleksiyonlar üzerinde SQL benzeri sorgulamalar yapılabilir. LINQ, IEnumerable arayüzüne sahip olan herhangi bir koleksiyon üzerinde çalışabilir.
IEnumerable arayüzünün dezavantajı ise, koleksiyon üzerinde yapılan sorgulamaların, bellekte (in-memory) gerçekleşmesidir. Yani, koleksiyonun tamamı belleğe yüklenir ve sonra sorgulama yapılır. Bu da, performansı ve bellek kullanımını olumsuz etkileyebilir. Özellikle, büyük boyutlu veya uzak bir veri kaynağından (örneğin, bir veritabanı veya bir web servisi) gelen koleksiyonlar üzerinde sorgulama yapmak istenirse, IEnumerable arayüzü yetersiz kalabilir. Bu durumlarda, IQueryable arayüzü kullanılabilir.
Şimdi IEnumerable ı kod üzerinden inceleyelim.
using (var context = new StudentDBContext())
{
IEnumerable<Student> students = context.Students;
students = students.Take(2);
foreach (Student student in students)
{
Console.WriteLine("ID = {0}, Name = {1} {2}, Gender = {3}", student.ID, student.FirstName, student.LastName, student.Gender);
}
}
Yukarıdaki kodda Student verilerini IEnumerable bir değişkene atıyoruz, daha sonra gelen veri üzerinde Take(2) ile ilk 2 kaydı elde ediyoruz. Son olarak bu 2 kaydı console a yazdırıyoruz. Bu işlemler sonrası oluşan sql sorgusunu;
SELECT [ID], [FirstName], [LastName], [Gender]
FROM [Student]
Görüldüğü gibi, SQL sorgusu veritabanından tüm öğrencileri çekiyor ve filtrelemeyi yapmıyor. Bu, gereksiz veri transferine ve bellek kullanımına neden oluyor. Take(2) ile ilk iki veriyi alma işlemi ise memory de yapılıyor.
IQueryable
IQueryable, System.Linq namespace i altında bulunan bir arayüz tipidir. IQueryable arayüzü, IEnumerable arayüzünden kalıtım alır. Bu sayede, IQueryable arayüzüne sahip olan bir nesne, IEnumerable arayüzüne sahip olan bir nesne gibi davranabilir. Ancak, IQueryable arayüzü, IEnumerable arayüzünün aksine, koleksiyon üzerinde yapılan sorgulamaları, bellekte değil, veri kaynağında gerçekleştirir. Yani, koleksiyonun tamamı belleğe yüklenmez, sadece sorgulama sonucu belleğe yüklenir. Bu da, performansı ve bellek kullanımını olumlu etkiler.
IQueryable arayüzünün de tıpkı IEnumerable da olduğu gibi IQueryable<T> gibi generic kullanıma imkan sağlayan arayüzü vardır.
IQueryable arayüzünün avantajı, koleksiyonlar üzerinde sorgulanabilirlik sağlaması, koleksiyonun elemanlarına erişim imkanı vermesi, koleksiyonun elemanlarını filtreleme, sıralama, gruplama gibi işlemler yapabilmesidir. Ayrıca, koleksiyon üzerinde yapılan sorgulamaların, veri kaynağında gerçekleşmesidir. Yani, koleksiyonun tamamı belleğe yüklenmez, sadece sorgulama sonucu belleğe yüklenir. Bu da, performansı ve bellek kullanımını artırır. Özellikle, büyük boyutlu veya uzak bir veri kaynağından gelen koleksiyonlar üzerinde sorgulama yapmak istenirse, IQueryable arayüzü tercih edilmelidir.
IQueryable arayüzünün dezavantajı ise, koleksiyon üzerinde yapılan sorgulamaların, veri kaynağı tarafından desteklenmesi gerektiğidir. Yani, koleksiyonun geldiği veri kaynağı, sorgulama mantığını anlayabilecek bir yapıda olmalıdır. Örneğin, bir veritabanı veya bir web servisi gibi. Aksi takdirde, sorgulama işlemi başarısız olabilir veya beklenen sonucu vermeyebilir. Ayrıca, IQueryable arayüzü, IEnumerable arayüzüne göre daha karmaşık bir yapıdadır. IQueryable arayüzü, koleksiyon üzerinde yapılan sorgulamaları, bir Expression ağacı olarak tutar. Bu Expression ağacı, veri kaynağına gönderilir ve veri kaynağı tarafından yorumlanır. Bu yorumlama işlemi, veri kaynağına göre değişiklik gösterebilir. Bu da, IQueryable arayüzünün kullanımını zorlaştırabilir.
IQueryable ile yapılan kod örneği ise aşağıda verilmiştir.
using (var context = new StudentDBContext())
{
IIQueryable<Student> students = context.Students;
students = students.Take(2);
foreach (Student student in students)
{
Console.WriteLine("ID = {0}, Name = {1} {2}, Gender = {3}", student.ID, student.FirstName, student.LastName, student.Gender);
}
}
SELECT TOP 2 [ID], [FirstName], [LastName], [Gender]
FROM [Student]
Bu örnekte, IQueryable arayüzü kullanmanın avantajı, veritabanından veri çekerken, sadece sorgulama sonucunu belleğe yüklememizdir. Bu sayede, performansı ve bellek kullanımını artırmış oluruz. Ayrıca, IQueryable arayüzü, IEnumerable arayüzünü uyguladığı için, LINQ sorgulaması yapabilmemizi sağlar.
Sonuç
IEnumerable arayüzü, koleksiyonlar üzerinde yinelenebilirlik sağlar, koleksiyonun elemanlarına erişim imkanı verir, koleksiyonun elemanlarını filtreleme, sıralama, gruplama gibi işlemler yapabilir. Ancak, koleksiyon üzerinde yapılan sorgulamalar, bellekte gerçekleşir. Yani, koleksiyonun tamamı belleğe yüklenir ve sonra sorgulama yapılır. Bu da, performansı ve bellek kullanımını olumsuz etkiler. IQueryable arayüzü, koleksiyonlar üzerinde sorgulanabilirlik sağlar, koleksiyonun elemanlarına erişim imkanı verir, koleksiyonun elemanlarını filtreleme, sıralama, gruplama gibi işlemler yapabilir. Ayrıca, koleksiyon üzerinde yapılan sorgulamalar, veri kaynağında gerçekleşir. Yani, koleksiyonun tamamı belleğe yüklenmez, sadece sorgulama sonucu belleğe yüklenir. Bu da, performansı ve bellek kullanımını artırır. Her iki arayüz de, LINQ sorgulaması yapmamızı sağlar. IEnumerable ı istemci tarafında bir veri koleksiyonu üzerinde işlem yapmak istediğimizde kullanmak daha uygundur. IQueryable ise sunucu tarafında bir veri kaynağına sorgu göndermek istediğimizde daha uygundur.
Bu yazıda, C# dilinde koleksiyonlar üzerinde sorgulama yapmak için kullanılan IEnumerable ve IQueryable arayüzlerini, çalışma biçimlerini, avantaj ve dezavantajlarını ve kullanım senaryolarını inceledik.
İyi çalışmalar.