Makale Özeti

Bu makalemizde DLINQ(Linq To Sql) konusuna değineceğiz. Dlinq kullanarak sorgu yapmayı, CRUD işlemlerini ve stored procedure kullanımını göreceğiz.

Makale

Merhaba herkese. Bu makalemizde DLINQ(Linq To Sql) konusuna değineceğiz. Dlinq kullanarak sorgu yapmayı, CRUD işlemlerini ve stored procedure kullanımını göreceğiz.

Linq To Sql’e geçmeden önce isterseniz biraz daha başa dönerek LINQ kavramından bahsedelim ki LINQ konusunda fikri olmayanlar makaleden kopmasınlar.

Linq; yani Language Integrated Query. Türkçe’ye çevirirsek “dil ile bütünleşmiş sorgu”. Ne demek peki bu? Sql sentaksına yakın ifadelerle Visual Studio ortamında nesneler üzerinde sorgu yapabilmektir en basit ifadeyle. Yazılımcının veri tabanından ziyade kod ile uğraşmasını sağlar. Gerçekten de veri tabanı başlı başına bir alandır, uzmanlık gerektirir.

Yazılımcılar ise genelde trigger, stored procedure, table, database vs. kullanırlar. Linq ile tüm bunları IDE’nizde rahatlıkla yapabilirsiniz.

2005’te bir proje(C-omega) ile alt yapısı oluşturulmaya başlanan Linq Kasım 2007’de .Net Framework 3.5 ile çıktı. Ancak Microsoft bunu çıkarmayı çok önceden planlıyor olacak ki C# 2.0’da Generics, Anonymous Methods gibi yenilikleri Linq’e taban oluşturmak için çıkardı. C# 3.0’dan hemen sonra C#3.5 çıktı ki bu yeni bir dil değildi sadece Linq eklentisi vardı.

Peki LINQ hangi veri kaynakları üzerine uygulanabilir?
1. Linq To Objects
2.Linq To XML[XLINQ]
3. Linq To Sql[DLINQ]

Bu üç ana kaynağa uygulansa da LINQ’in esnekliğinden ötürü bunlar genişlemektedir. (Ör: Linq To Entities, Linq To Google[GLINQ])

Linq konusunda uygulama yapmayacağım çünkü ana konumuz DLINQ. Örnek uygulamamıza başlayalım.



Formumuza bir adet dataGridView sürükleyelim.( Ben dataGridView’ın Dock özelliğini Fill yaptım, bu size kalmış.)

Projemize sağ tıklayıp Add --> New Item diyelim. Çıkan ekranda Linq To Sql Classes seçelim. Ben ismini Northwind verdim.(Northwind kullanacağım çünkü. Farklı isim de verebilirsiniz.)



Northwind.dbml diye oluşturduk. (.dbml uzantısı: database markup language) Karşımıza bir adet designer ekranı geldi 2 parçadan oluşan. Şimdi sol tarafa Server Explorer’dan Northwind veri tabanının Employees ve Orders tablolarını sürükleyeceğim.



Ben en bilindik veri tabanı olmasından dolayı Northwind kullandım örneğimde, istediğiniz kullanabilirsiniz tabi ki. Hazır veri tabanı için bu böyledir, isterseniz designer ekranına, yani tabloları sürüklediğimiz yere, sağ tıklayıp add class ve add property diyerek de oluşturabilirdiniz.

Dikkat ettiyseniz biz Employees ve Orders sürüklememize rağmen ekranda Employee ve Order gözüküyor. Bu .net isimlendirmelerinin tutarlı olması içindir(standartlaşma meselesi).

Yaptıklarımızı kaydettikten sonra Solution Explorer’a bakarsak şayet, bizim için arka planda kod üretildiğini görürüz. (Northwind.designer.cs). İçine baktığımızda bizim için DataContext class’ından türetilmiş NorthwindDataContext class’ının yazıldığını görürüz. DataContext veri tabanımızı temsil etmektedir. Tahmin edeceğiniz üzere de NorthwindDataContext de Northwind veri tabanını belirtmektedir. Bir satır üstünde attribute bilgisinde hangi veri tabanını temsil ettiği görülmektedir.
Altta ise dikkat etmemiz gereken property’ler mevcuttur.


public System.Data.Linq.Table Employees
{
   get
   {
     return this.GetTable();
   }
}
public System.Data.Linq.Table Orders
{
   get
   {
     return this.GetTable();
   }
}


Gördüğünüz gibi Employees property’si Northwind’in Employees tablosunun tutmakta.(Keza bunlar Orders tablosu için de aynıdır.) Kodun alt kısımlarında ise Employee ve Order class’ları vardır. Bunlar da tablolarda işlemler yapabilmek için kullanacağımız sınıflardır.

Artık bir sorgu ile işe koyulalım isterseniz. Forma çift tıklayarak Load olayına girelim. Ve alttaki kodu yazalım.

NorthwindDataContext db = new NorthwindDataContext();
var calisanlar = from emp in db.Employees
                  where emp.TitleOfCourtesy == "Mr."
                  select new {emp.HomePhone,emp.FirstName };
dataGridView1.DataSource = calisanlar;

İlk satırda Northwind veri tabanımızı kullanmak için ilgili sınıfın instance’ını aldık. Bunu her işlemde kullanacağımızdan bu satır hep kalsın projemizde.

Alt satırda var kullanarak bir değişken tanımladık. Sağ taraftaki ifadeden ne döneceğini bilemediğimz için bunu kullanmak en iyisidir.

from anahtar sözcüğü SQL’de hangi tablo üstünde işlem yapacağımızı gösteriyordu. Burada ise Employees tablosunda gezecek değişkendir.

Şimdi biraz dikkatinizi verdiyseniz şu soruyu sorabilirsiniz: neden from db.Employees demedik de from emp in db.Employees dedik..? Bunun cevabını almak için alttaki iki satıra bakmak kafidir. Where ve select komutlarında emp değişkeninin nasıl işe yaradığı barizdir.

Bir diğer husus da, select’i en son yazmamdır.Bunun sebebi Intellisense desteğini sağlamaktır. Kaynağı from ifadesinde veririz ki select ona göre çalışsın. Select emp. Dersek Employee tablosunun tüm alanları görünür. Eğer direkt select deseniz, neyi select edecek ki; hangi tablodan hangi kolonu??? Dolayısıyla select ifadesi en son ifade olmuştur.

Çalıştırdığınızda bunun gibi bir sonuç göreceksiniz.(Verilerinize bağlı olarak.)


Gördüğünüz gibi tüm “Mr.”lar geldi.


Şimdi farklı bir sorgu yapalım. Employees ve Orders tablolarını birleştirerek EmployeeID,AdSoyad,Country,TitleOfCourtesy ve Freight alanlarının ilk 10 tanesini çekelim.

var calisanlar = (from emp in db.Employees
                  from ord in db.Orders
                  where emp.EmployeeID == ord.EmployeeID
                  select new { ord.EmployeeID, NameSurname = emp.FirstName + " " + emp.LastName, emp.Country, emp.TitleOfCourtesy, ord.Freight }).Take(10);
dataGridView1.DataSource = calisanlar;

SQL ifadelerine benzer olduğu için anlamakta zorluk çekceğinizi sanmıyorum. Select işlemine kadar sadece tablo birleşimi mevcut. Select ifadesinde yeni bir şeyler var. C# 3.5 ile gelen Anonim Tipler kullanılmış. Select emp deseydik tüm kolonları çekmiş olurduk. Ancak bu şekilde sadece istediğimiz kolonları çekeriz ve hatta yukarıda yaptığım gibi bir kolon yaratabiliriz(AdSoyad). İstediğimiz kolonlardan ilk 10 tanesini istedik Take(10) ile. Bu ifadeyi çalıştırısanız aşağıdaki gibi bir sonuç alırsınız.




CRUD işlemlerine geçelim artık.

Insert ile başlayalım isterseniz.

Employee emp = new Employee();
emp.FirstName = "Gurkan";
emp.LastName = "Alkan";
emp.TitleOfCourtesy = "Mr.";
emp.City = "Vice City";

yazalım öncelikle. Burada bir önceki makalemde bahsettiğim Employee sınıfını kullanarak bir nesne yarattım ve buna bazı değerler atadım. Daha önce bunlara değindiğim için tekrar girmiyorum. Şu anda herhangi bir insert işlemi yapmadık. Kodumuza devam edelim.
db.Employees.InsertOnSubmit(emp);

Evet, gördüğünüz gibi InsertOnSubmit() metodu ile bu nesneyi parametre olarak gönderdik. Peki bu kayıt etti mi? Hayır etmedi arkadaşlar. Bu komut veri tabanına kayıt edilmek üzere emp nesnesini bir kuyruğa gönderdi. Yani emp nesnesi şu anda kuyrukta bekliyor, bir işlem yapıp onu, o kuyruktan çekip veri tabanına götürelim ve kesin kayıt yapmış olalım.
db.SubmitChanges();
Artık insert işlemi olmuştur arkadaşlar. Bu komut ile kuyrukta insert edilmek için bekleyen nesnemizi veri tabanına işlemiş olduk. Yani işlemin yapılabilmesi için SubmitChanges() metotunun çalışması gerekmektedir.

Şimdi de ekranda gösterelim yaptıklarımızı.



Gördüğünüz gibi kayıt etmiş. (ID’ye takılmayın çünkü benim önceki işlem ve hatalarımdan kalma farklılıklardır. Truncate yapmadığım için ID uçmuş haliyle.)

Insert işlemini sonlandırmadan önce bir şey daha göstermek istiyorum. Kuyrukta bekleyen nesneleri istersek görebiliriz. Bunun için GetChangeSet() metodunun Inserts(aynı şekilde Updates ve Deletes de var.) özelliğini kullanıyoruz. Dikkat edilmesi gerek husus bu kod parçasının SubmitChanges()’den önce yazılması gerektiğidir. Submit edilmiş bir şey zaten kuyrukta kalmaz, dolayısıyla bir sonuç elde edemezsiniz.

var bekleyenInsertler = db.GetChangeSet().Inserts;
foreach (Employee item in bekleyenInsertler)
{
   MessageBox.Show(item.FirstName);
}
Bir önceki kodun arasına yazdığınızda ekranda Gurkan yazması gerekiyor.

Şimdi de update işlemine geçelim.
2 farklı yolla yapacağım. İlk olarak Lambda Expression kullanacağım.

Employee emp = db.Employees.First(bjk => bjk.LastName == "Alkan");
emp.FirstName = "Gürkan";
db.SubmitChanges();
var calisanlar = from empl in db.Employees
                      select empl;
dataGridView1.DataSource = calisanlar;

Yarattığımız emp’ye LastName’i Alkan olan ilk kaydı verdik. Az önce Gurkan diye kayıt etmiştim; şimdi onu Gürkan diye değiştirdim. Tabi bunun kesin olarak veri tabanına yansıması için gene SubmitChanges() çalıştırıyoruz. Daha sonra da bildiğiniz ekranda göstermek için işlemlerimizi yapıyoruz.

Kısaca Lambda Expression’a değinelim. Yukarıdaki ifadenin karşılığını şöyle yazayım:
delegate (string bjk){return bjk.LastName==”Alkan”;}
Bununla eş değerdir. Bu Lambda İfadelerini anlamak için C# ‘ ın daha önceki versiyonlarından itibaren incelemek gerekir ancak makale konumuz olmadığı için hiç girmeyeceğim. => ifadesinin sağ tarafı bool türünde bir ifade döner. Sağ tarafı ise delegenin parametresidir.( First() metodu da parametre olarak Predicate temsilcisini alır). Bunu en basit olarak matematikteki limitler gibi düşünebilirsiniz. Bjk giderken bjk.LastName’e gibi.



Lambda bilmeyenler için üstteki yol karmaşık gelebilir. Telaş yapmayın, bir başka yol daha var.
var guncellenecekler = from emp in db.Employees
                       where emp.EmployeeID == 22
                       select emp;
foreach (Employee item in guncellenecekler)
{
   item.FirstName = "P. Nouma";
}
db.SubmitChanges();
var calisanlar = from emp in db.Employees
                       select emp;
dataGridView1.DataSource = calisanlar;

Güncellenecek olan kaydı ID’si yardımıyla alıyoruz. Sonra buna P. Nouma değeri atıyoruz.(Pascal Nouma vs. uzun şeyler yazarsanız truncated olduğuna dair hata alırsınız, bunun nedeni veri tabanında bu alan için açılan boyutu aşmanızdır. Gidip veri tabanında Employees tablosunun FisrtName kolonunu büyütürseniz sorun da kalmaz.)

Daha sonra yaptıklarımız gene aynı. Değişikliklerin kesin kaydını yapıyoruz ve ekranda gösteriyoruz.



Update işleminde de tıpkı insert’deki gibi kuyruktakileri görülebiliriz. Bunun için şu kodu SubmitChanges()’den önce yazarak deneyebilirsiniz.

var bekleyenguncellemeler = db.GetChangeSet().Updates;
foreach (Employee item in bekleyenguncellemeler)
{
   MessageBox.Show(item.FirstName);
}

Şimdi de birden fazla kaydı güncellemek suretiyle farklı bir güncelleme yapmış olalım(Tabi ki aynı şey ama birden fazla kaydın da güncellenebileceğini göstermek istedim).

var calisanlar = from empl in db.Employees
                         select empl;
foreach (Employee item in calisanlar)
{
   item.Region = "NoWomanNoCry";
}
db.SubmitChanges();
dataGridView1.DataSource = calisanlar;

Gördüğünüz gibi tüm tabloyu calisanlar değişkenime çektim ve onun da Region’ına NoWomanNoCry (Bob Marley klasiği :) ) yazdırdım. Veri tabanıma kaydettim ve bunu ekranda gösterdim. Veri tabanıma kaydettikten sonra tekrar sorgu yazmmadım çünkü zaten calisanlar değişkeninde tüm tablom mevcut. Çıktı da şu şekilde olmalı:



Yukarıda da gördüğünüz üzere tüm tablonun Region kolonunu doldurduk. Insert olarak düşünebilirsiniz ama null olan değeri update ederek NoWomanNoCry yaptık aslında.

Update’de Submit etmeden önce herhangi bir metot kullanmadığımıza dikkat edin. InsertOnSubmit() gibi bir metot yok bunda. Mantıklı da. Update işlemi yeni bir işlem sayılamaz. Daha ziyade var olanın değerini değiştiriyoruz. Dolayısıyla kullanılmaması da çok doğal.

Artık delete işlemimize geçebiliriz..

var silinecek = from empl in db.Employees
                         where empl.FirstName == "P. Nouma" && empl.LastName == "Alkan"
                         select empl;
db.Employees.DeleteAllOnSubmit(silinecek);
db.SubmitChanges();
var calisanlar = from emp in db.Employees
                         select emp;
dataGridView1.DataSource = calisanlar;
Artık anladığınızı düşünüyorum. Burada da FirstName’i P. Nouma ve LastName’i Alkan olan kaydı silinecek değişkenine alıyoruz. Daha sonra DeleteAllOnSubmit() metotuna parametre olarak gönderiyoruz ve silinmek üzere kuyruğa doğru yolculuğa çıkarıyoruz. Bir alt satırda da SubmitChanges() diyerek kesin olarak veri tabanımızdan siliyoruz. En sonda da her zaman olduğu gibi ekranda gösteriyoruz:



Gördüğünüz gibi 22 numaralı kaydımız artık yok. Ben crud işlemlerini burada noktalamak istiyorum. İlgilenenler olursa DLINQ’te “Optimistic Concurrency” konusunu biraz araştırsın. UpdateCheck attribute’u ile where kriterini nasıl kontrol edebileceğimizi incelemenizi tavsiye ederim.

Buraya kadar geldik, tüm işlerimizi yaptık. Peki yaptıklarımızdan vazgeçersek ne olacak? Tabi ki Submit edilmemiş olanlardan vazgeçebilirsiniz; kuyruktakilerden vazgeçebilirsiniz yani. Eğer veri tabanına kayıt edilmişse artık ondan vazgeçme gibi bir lüksünüz yok. Bu noktada şimdiye kadar gördüklerimizden aklınıza gelebilecek en doğal yol: GetChangeSet() ile Inserts, Updates ve Deletes ‘i alıp onları Clear(), Remove() ya da Delete() gibi bir metotla kuyruktan kaldırmak. Ancak bu şekilde olmuyor. Bunlar read-only olduklarından bu şansımız yok maalesef. Bu noktada imdadımıza GetOriginalEntityState() metotu koşuyor.Bu metot, paremetre olarak verdiğin nesnenin değiştirlmeden önceki halini döndürür. İsterseniz hemen kodumuza bakalım..

var guncellenecekler = from emp in db.Employees
                         where emp.EmployeeID == 8
                         select emp;
foreach (Employee item in guncellenecekler)
{
   item.FirstName = "TTK-TDK";
}
var bekleyenGuncellemeler = db.GetChangeSet().Updates;
Employee empl = (Employee)bekleyenGuncellemeler[0];
this.Text = empl.FirstName;
Employee asil = db.Employees.GetOriginalEntityState((Employee)bekleyenGuncellemeler[0]);
this.Text = this.Text + " " + asil.FirstName;

FirstName’i Türk Tarih Kurumu ve Türk Dil Kurumu’nun kısaltmalarından oluşturdum. Bunlar kuyruğa gitti haliyle. Kuyruktaki ilk eleman olduklarından bunu aldırdım ve metota paramete olarak göndererek asıl değerini buldurdum. Bunu da direkt Formun Text’ine yazdırdım.



Gördüğünüz gibi verdiğimiz değer ve orijinal değeri formun tepesinde yazıyor. :)

Sıra geldi stored procedure yazmaya.

Bir adet sp yazalım.

Create proc GetEmployeeByID(@ID int)
As
Select * from Employees where EmployeeID=@ID

şeklinde bir sp’miz olsun. Server Explorer’dan bunu sürükleyelim design ekranına.

Design’a(sol kısım) sürüklememe rağmen sp’miz sağ tarafta gözüktü. Dikkatinizi çekerim yanındaki işarete. Metot işareti!! Evet arkadaşlar, stored procedure’leri metot gibi kullanabilmektesiniz.

var calisanlar = db.GetEmployeeByID(2);
dataGridView1.DataSource = calisanlar;

Gördüğünüz gibi metot gibi kullandım. 2 numaralı kaydın bilgilerini ekrana getirmesi gerekiyor.



İşte 2 numaralı kayıt..
Geldik bir makalemizin daha sonuna. Umarım Linq To Sql dendiğinde artık sizler için bu konu bir şeyler ifade ediyordur. Herkese iyi çalışmalar dilerim.

Gürkan Alkan
İstanbul Üniversitesi Bilgisayar Mühendisliği
grkn.alkan@gmail.com