LINQ’e Genel Bakış

LINQ nedir? :

LINQ, program ve veri arasındaki ilişkiyi başka bir boyuta taşıyan devrimsel bir programlama metodolojisidir. LINQ, bir programalama arayüzü sunar. C# diline getirdiği eklentilerle SQL benzeri tek bir söz dizimi ile farklı tiplerdeki verilerin sorgulanmasını sağlar. C# ile sorgu yazmak, tip güvenli çalışma, ifadelerin otomatik olarak tamamlanması ve IntelliSense gibi özelliklerle geliştiricinin üretkenliğini artırmayı sağlar.

Farklı LINQ uygulamaları mevcuttur. Bunlardan en temel olanlar şöyle özetlenebilir.
• Bellek üzerindeki nesnelerden oluşan koleksiyonları sorgulamak (LINQ to Objects),
• SQL Server veritabanlarındaki tabloları sorgulamak (LINQ to SQL),
• ADO.NET DataSet tiplerini sorgulamak (LINQ to DataSets),
• Xml verilerini sorgulamak (LINQ to XML)
• ADO.NET Entity Framework tarafından oluşturulan varlıkları sorgulamak (LINQ to Entity)

Bu sayılan LINQ uygulamaları aynı söz dizimini kullanır ancak farklı veri kümelerini hedeflemektedir.
Çoğu SQL komutlarına benzer yada aynı olan C# anahtar kelimelerinden oluşan ifadeler ile .NET koleksiyonlarına LINQ sorguları yazarsın. Bu anahtar kelimeler, LINQ Standart Sorgu Operatörleri olarak anılır (LINQ Standart Query Operators). Bu operatörler, System.Linq isim alanı altında yer alan Enumerable sınıf tarafından tanımlanır. C# derleyicisi (compiler), sorgu ifadelerini, çalıştırılmak üzere CIL ortak ara diline dönüştürür.

Aşağıda temel bir LINQ sorgusunun genel yapısını inceleyebilirsin :
var sorgu = from eleman in koleksiyon
where kriterler
orderby sıralamaKıstası [ascending|descending]
select [takmaAd = ]kolonIfadesi [ , [takmaAd2 = ] kolonIfadesi2]

var anahtar kelimesi, yukarıdaki LINQ sorgu sonucunun atandığı sorgu isimli lokal değişken için tip çıkarsaması yapar. var, bir veri tipi değildir. var, eşitliğin sağ tarafındaki veriye ait tipin, derleyici tarafından otomatik olarak tespit edilmesini ve CIL koduna yazılmasını sağlar.

linq_şema

LINQ Sorguları :

Bir LINQ sorgusu, veri kaynağından istenen veriyi elde etmek için kullanılır. Bir LINQ sorgu süreci 3 ayrı parçadan oluşmaktadır.

  1. Veri kaynağını elde et.
  2. Sorguyu oluştur.
  3.  Sorguyu çalıştır.

Aşağıdaki örneği bu ayrımı göz önünde bulundurularak incele. Örnekte veri kaynağı olarak bir int dizisi kullanılmıştır.

// Veri Kaynağı

int[] sayilar = new int[5] { 1, 2, 3, 4, 5 };

//Sorgu Oluşturma

var tekSayilar = from s in sayilar where (s % 2) == 1 select s;

//  Sorgunun Çalıştırılması.

foreach (int s in tekSayilar)

{

Console.WriteLine(s);

}

LINQ mimarisinde sorgunun çalıştırılması, sorgunun kendisinden ayrı bir süreçtir. Yani sorgu oluşturulurken, veri elde edilmez.

 Veri  Kaynağı

Örnekteki veri kaynağı int dizisi, IEnumerable<T> generic tipini desteklemektedir. Dolayısıyla sorgulanabilir bir tiptir. Bir sorgu foreach döngüsü ile çalıştırılır ve foreach IEnumerable yada IEnumerable<T> arayüzlerini uygulamış tiplere ihtiyaç duyar.

Sorgu

Sorgu, veri kaynağı yada kaynaklarından hangi bilgilerin elde edileceğini belirtir. Dilenirse bir sorgu aynı zamanda veri kaynağından elde edilen bilginin nasıl sıralanacağını, gruplanacağını ve elde edilmeden önce nasıl bir forma gireceğini belirtebilir. Bir sorgu, sorgu değişkeninde saklanır. Burada önemli olan nokta, sorguyu saklayan değişken tek başına her hangi bir aksiyon alamaz ve üzerinde veri saklamaz. Sadece ileri bir noktada sorgu çalıştırıldığında sonucu üretmek için gereken bilgileri saklar.

 Sorgunun  Çalıştırılması

Sorgunun çalıştırması, sorguyu saklayan değişkenin foreach döngüsüne sokulması ile gerçekleşir. Bu konsepte ertelenmiş çalıştırma (deffered execution) adı verilir.

Örnekte foreach döngüsü ile sorgu kullanıldığında, sorgu çalıştırılmış ve veri kaynağında yer alan tek sayılar filtrelenmiştir.

Bir sorgunun hemen çalıştırılması ve sonuç kümesinin elde edilmesi istenirse sorgu cümleleri ToList() yada ToArray() metotları ile birlikte kullanılabilir.

List<int> tekSayilar = (from s in sayilar where (s % 2) == 1

select s).ToList();

Bu durumda hem sorgu cümlesinin hemen çalıştırtılması sağlanır; hem de sonuç kümesi bellekte bir koleksiyon üzerinde saklanmış olur.

Generic Koleksiyonlar İle LINQ :

LINQ  sorgu  değişkenleri  IEnumerable<T>  arayüzünden  türeyen  bir  tip  olmalıdır.  Örneğin IEnumerable<Ogrenci> tipinde bir sorgu değişkeni gördüğün zaman şu anlama gelir : Bu sorgu çalıştırıldığında sıfır yada daha fazla sayıda Ogrenci sınıfından nesne üretecektir.

Örnek için öncelikle basit bir Ogrenci sınıfı kodla.

public class Ogrenci

{

public string Adi;

public DateTime DogumTarihi;

public int Numarasi;

public Ogrenci(string adi, DateTime dogumTarihi, int numarasi)

{

this.Adi = adi;

this.DogumTarihi = dogumTarihi;

this.Numarasi = numarasi;

}

}

Main metodu içerisine 4 tane Ogrenci nesnesi içeren bir List<T> koleksiyonu oluştur.

Ogrenci o1 = new Ogrenci(“Ali Yılmaz”,new DateTime(1982, 2 ,2),7823482); Ogrenci o2 = new Ogrenci(“Kerim Ak”, new DateTime(1975, 10, 23), 9288763); Ogrenci o3 = new Ogrenci(“Can Çelik”, new DateTime(1990, 7, 30), 1372426); Ogrenci o4 = new Ogrenci(“Halil Ata”, new DateTime(1987, 1, 13), 9126343);

List<Ogrenci> ogrenciler = new List<Ogrenci>() { o1, o2, o3, o4 };

Şimdi LINQ sorgusu ile bu koleksiyonda arama yapalım : 1985 yılı öncesinde doğan öğrencilerin isimleri ve numaraları, aralarında “/” işareti olacak şekilde yan yana tek bir string tipli veri olarak elde edilsin. Aşağıdaki sorgu bu işi görecektir :

var sorgu = from o in ogrenciler

where o.DogumTarihi.Year <= 1985

select o.Adi + ” / ” + o.Numarasi.ToString();

Bu  sorgu  cümlesinde dikkat  çekmesi  gereken  1-2  önemli  nokta  vardır.  Birincisi  generic  koleksiyon üzerinden LINQ sorgusu yazarken Visual Studio editörünün otomatik kod tamamlayıcı özelliği Intellisense sana yardım eder. Örneğin where operatöründen sonra “o.”  yazdığında Ogrenci sınıfının public üyeleri listelenir ve sen  filtrelemede  kullanmak  istediğin  DogumTarihi  alanını  listeden  seçebilirsin.  Böylece  hızlı  ve  minimum hatayla sorgu yazarsın.

Sorgu sonucunu görmek için sorguyu çalıştırman lazım; dolayısıyla foreach döngüsü yazman gerekiyor.

foreach (var o in sorgu)

{

Console.WriteLine(o);

}

Kod parçası çalıştırıldığında aşağıdaki çıktı elde edilir :

 var  An ah ta r  Kelim e si  :  Der ley i cinin  Tip  Çıka rsa ması  Yap ma sı

Örnekte hem sorgu değişkeni olarak hem de foreach döngüsünde döngü değişkeni olarak var anahtar kelimesi kullanılmıştır.

sorgu isimli sonuç kümesi değişkeni incelendiğinde, generic koleksiyonun içerdiği nesnelerin veri tipine bakarak çıkarsama yapılır ve CIL koduna veri tipi olarak IEnumerable<Ogrenci> yazılır.

Benzer şekilde foreach döngüsünde var ile tanımlanan döngü değişkeninin veri tipi, derleyici tarafından sorgu değişkeninin elemanlarından tespit edilir. Böylece çalışma zamanında işlemler Ogrenci tipinde gerçekleştirilir.

LINQ to SQL Dizayn Ekranı :

LINQ to SQL Classes, Visual Studio ile açılan bir C# projesindeki şablonun adıdır.

Projene yeni bir LINQ to SQL Classes eklediğinde, boş bir O/R dizayn ekranı açılır. Bu, veritabanından ekleyeceğin sql nesneleri ile ilgili metadataları tutan xml tabanlı ve dbml uzantılı bir dosyadır.

linq_2

Soldaki büyük alana tablo ve view, soldaki küçük alana ise stored procedure ve fonksiyon ekleyebilirsin. Veritabanına bağlanıp sql nesnelerini görüntülemeni sağalayacak araç ise “Server Explorer”dır. Bu araca menüden View à Server Explorer sekmelerinden erişebilirsin. Bu araçta yer alan Data Connections kalemine sağ tıklayıp Add Connection seçilerek bağlanılacak veritabanı ile ilgili bilgiler girilebilir. Sonucunda veritabanındaki bütün sql nesneleri önüne listelenecektir. Yapman gereken Server Explorer aracındaki listeden dizayn ekranına  ihtiyaç  duyduğun  tabloları  sürükleyip bırakmak.  Bu  konudaki  örneklerde, daha önce  SQL konusunda kullandığın Adventureworks veritabanını kullanabilirsin.

linq_3

Entity olarak dizayn ekranı tarafından üretilen sınıfları incelemek istersen Solution Explorer penceresinde *.designer.cs dosyasına bakmalısın.

Nesneleri Ekleme:

LINQ  to  SQL,  projene  veri  erişim  katmanı  uygulamak  için  çok  mükemmel  bir  adaydır.  LINQ  to  SQL  ile kullanacağın merkezi nesne  DataContext sınıfıdır. Bu sınıf, veritabanları ile uygulama arasındaki bağlantıyı yönetir. Aynı zamanda ürettiğin entity sınıflarına ait veritabanındaki kayıtları içeren Table<TEntity> tipinde koleksiyonları saklar. Bu koleksiyonlar sayesinde kod tarafında, bir tablodaki bütün verilere kolayca erişebilir ve ASP.NET gibi bir arayüzde gösterebilirsin. Örneğin Musteris koleksiyonunu ele alalım. Bu, içinde Musteri tipinde nesneler barındıran Table<Musteri> tipinde bir koleksiyondur.

Veritabanına doğru çalıştırılacak bir LINQ sorgusu hazırlamak için Table<TEntity> tipindeki koleksiyon ile çalışmalısın. Bu nesne üzerinden gerçekleştirdiğin manipülasyonlar, C# derleyicisi tarafından önce dinamik T- SQL sorgularına dönüştürülür; ardından veritabanına doğru bağlantı açılarak çalıştırılır. DataContext, SqlClient sağlayıcısı üzerinden veritabanına bağlantı açılması ve kapatılması süreçlerini otomatik olarak yönetir. Senin ekstra kod yazmana gerek yoktur.

 Yeni  Kayıt  Eklemek

LINQ to SQL kullanarak veritabanındaki bir tabloya yeni kayıt eklemek için Table<TEntity> koleksiyonuna yeni bir kayıt eklemen yeterlidir. Bu kayıt, veri eklemek istediğin tablo için dizayn ekranından ürettiğin entity sınıfı tipinde olmalıdır.

DataContext.TabloAdı.Add(YeniNesne)

Nesneyi  hazırlayıp  koleksiyona  ekledikten  sonra  DataContext.SubmitChanges()  metodu  çağrılır.  Bu metot, yeni nesnenin veritabanında saklanması için gerekli T-SQL insert sorgusunun hazırlanması ve çalıştırılmasını sağlar.

Ayrıca bütün veritabanı işlemlerinde mutlaka System.Data.Linq.DataContext tipinden türeyen projene özel DataContext nesnesine ihtiyacın vardır.

NesneModeliDataContext ctx = new NesneModeliDataContext(); Department yeniDep = new Department();

yeniDep.Name = “Yazılım”; yeniDep.GroupName = “Bilgi Teknolojileri”; yeniDep.ModifiedDate = DateTime.Now;

ctx.Departments.InsertOnSubmit(yeniDep);

ctx.SubmitChanges();

Dilenirse InsertOnSubmit metodu yerine InsertAllOnSubmit metodu kullanılarak bir kayıt değil önceden hazırlanmış bir koleksiyon içindeki bütün kayıtların eklenmesi sağlanabilir.

Nesne Güncelleme:

Kayıt güncellemek için DataContext üzerindeki koleksiyondan güncellenmesi istenen bir yada birden fazla nesnenin elde edilmesi gerekir. Bunun için where ifadesi ile filtrelenmiş bir sorgu yazılması gerekir. Sorgu sonucunda elde edilen nesnelerin istenen özelliklerinde (property) gerekli değişiklikler yapıldıktan sonra DataContext.SubmitChanges() metodu çağrılır. Bu metot kayıtların veritabanında güncellenmesi için gerekli T- SQL update sorgusunun hazırlanması ve çalıştırılmasını sağlar.

NesneModeliDataContext ctx = new NesneModeliDataContext(); Department depGuncellenecek = (from d in ctx.Departments

where d.DepartmentID == 1

selectd).SingleOrDefault();

if (depGuncellenecek != null)

{

depGuncellenecek.GroupName = “Finansal Yönetim”;

ctx.SubmitChanges();

}

Bu örnekte DepartmentId değeri 1 olan tek bir kayıt olacağı için sorgu sonucunda SingleOrDefault metodu çağrılmıştır. Böylece eşitliğin sol tarafında tek bir Department nesnesi elde edilmiş ve o nesnenin GroupName kolonu güncellenmiştir. Eğer birden fazla nesne üzerinde aynı anda güncelleme yapmak durumunda kalırsan aşağıdaki kod parçası sana yol gösterici olacaktır.

NesneModeliDataContext ctx = new NesneModeliDataContext();

var depGuncellenecek = from d in ctx.Departments where d.DepartmentID < 10

select d;

foreach (var d in depGuncellenecek)

{

d.GroupName = “Finansal Yönetim”;

}

ctx.SubmitChanges();

 Bu kod parçasında LINQ sorgusu sonucunda 9 kayıt döner. Bu 9 kayıt üzerinde güncelleme yapmak için foreach döngüsünden faydalanılır. Döngünün sonunda SubmitChanges metodu çağrılarak bütün güncellemeler tek bir seferde veritabanına yansıtılmış olur.

Nesne Silme:

Veri tabanından tek bir kayıt yada birden fazla kayıt silmek için DataContext üzerindeki koleksiyondan nesnelerin silinmesi gerekir.

DataContext.TabloAdı.Remove(SilinecekNesne)

Bunun  için  where  ifadesi  ile  filtrelenmiş bir  sorgu  yazılması  gerekir.  Sorgu  sonucunda elde  edilen nesneler koleksiyondan silindikten sonra DataContext.SubmitChanges() metodu çağrılır. Bu metot kayıtların veritabanından silinmesi için gerekli T-SQL delete sorgusunun hazırlanması ve çalıştırılmasını sağlar.

NesneModeliDataContext ctx = new NesneModeliDataContext();

var depSilinecek =   (from d in ctx.Departments where d.DepartmentID == 3

select d).SingleOrDefault();

if (depSilinecek != null)

{

ctx.Departments.DeleteOnSubmit(depSilinecek);

ctx.SubmitChanges();

}

 Dilenirse DeleteOnSubmit metodu yerine DeleteAllOnSubmit metodu kullanılarak bir kayıt değil bir koleksiyon içindeki bütün kayıtların silinmesi sağlanabilir.

Bu yazı www.acikakademi.com ders notlarından derlenmişdir.

Bir cevap yazın

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