LINQ(Language Integrated Query) .NET 3.5 ve Visual Studio 2008 ile hayatımıza girdi. C# ile kod yazarken SQL'de sorgu yazıyor gibi sorgu yazmak bir çok kişinin hoşuna gitti. "LINQ hoş ama daha dinamik sorgu yazabilsek ..." dediğimiz zamanda imdadadımıza Dynamic Expression API(DynamicLINQ) yetişti ve bize string tabanlı sorgu yazma imkanı, başka bir açıdan, kolaylığı sundu.Bu makalemde DynamicLINQ nedir?, nasıl kullanılır? ve biz yazılımcıları DynamicLINQ kullanmaya itebilecek senaryoları soruları üzerine konuşuyor olacağız.
Not : Sınıfın asıl adı Dynamic Expression fakat ben sınıftan makale boyunca DynamicLINQ olarak bahsedeceğim.
DynamicLINQ nedir?
LINQ'da sorgu çekerken ya syntax içindeki operatörleri ya da Lambda extension metodlarını kullanırız değil mi? İşte DynamicLINQ'da işler biraz değişiyor :
[C#] Klasik LINQ
private static DBDataContext DB = new DBDataContext();
var kayit = from x in DB.Kisis where x.Ad == "Mehmet" && x.Soyad == "Karadayı" select x;
Console.WriteLine(kayit.FirstOrDefault().Tip);
[C#] Dynamic LINQ
private static DBDataContext DB = new DBDataContext();
var kayit = DB.Kisis.Where("Ad=@0 and Soyad=@1", new object[] { "Mehmet", "Karadayı" });
Console.WriteLine(kayit.FirstOrDefault().Tip);
Gördüğünüz gibi Dynamic LINQ da "Where" koşulunu "String" bir değer olarak verdik. Her iki sorguyu da SQL Profiler ile takip ettiğimiz zaman SQL'e aslında aynı sorgunun gittiğini göreceğiz yani geri dönen değerlerde de hiçbir farklılık olmayacaktır.
Yukarıdaki basit sorgu örneğini de göz önüne alırsak, kısaca DynamicLINQ'yu sorguların String tabanlı yapıldığı genişletme(extension) foksiyonları içeren bir kütüphane olarak tanımlayabilirz.
Nasıl Kullanılır?
Daha önce de belirttiğim gibi DynamicLINQ aslında genişletme(extension) metodları içeren bir kütüphane.
[DATACONTEXTDeğişkeni].[TABLOADI]. dediğimiz zaman System.LINQ Namespace'i ile birikte gelmeyen, ek metodlar gözümüze çarpıcaktır.Bu metodlar aşağıda belirttiğim gibidir :
public static IQueryable Where(this IQueryable source, string predicate, params object[] values);
public static IQueryable Where(this IQueryable source, string predicate, params object[] values);
public static IQueryable Select(this IQueryable source, string selector, params object[] values);
public static IQueryable OrderBy(this IQueryable source, string ordering, params object[] values);
public static IQueryable OrderBy(this IQueryable source, string ordering, params object[] values);
public static IQueryable Take(this IQueryable source, int count);
public static IQueryable Skip(this IQueryable source, int count);
Dynamic Expression API'ın içerdiği extension metodların listesi
Gördüğünüz üze re metodlar, string değerler ve object dizileri alan metodlar.
İşte DynamicLINQ'nun altında yatan felsefe de bu : yazma zamanlı değil çalışma zamanlı sorgular oluşturmak.Bu sayede sorgulayacağımız kıstasların değişkenliğini tolere etmiş oluyoruz.
Lafı fazla dolandırmadan metod kullanımlarıyla ilgili örneklere geçelim isterseniz.
-> WHERE Koşulunun Kullanımı
var kayit = DB.Kisis.Where("Ad==@0", "Mehmet");
Console.WriteLine(kayit.FirstOrDefault().Soyad);
Yukardaki kod parçasında "Kisi" tablosunun "Ad" kolonundaki değeri "Mehmet" olan kayıt veya kayıtlardan ilkinin "Soyadını" ekrana yazdırdık.
Sorgularda parametre tanımlama tarzı aslında aşikar olduğumuz bir tarz :
Ad==@0
Parametreler @0,@1 gibi yani aynı SQL'deki gibi tanımlanıyor.
,"Mehmet "
YA DA
,new object[]{"Mehmet"}
Parametre değerleri ise ya virgülden sonra direk veriliyor ya da bir obje listesi olarak atanıyor.Aslında aralarında hiç bir fark yok.
-> OrderBy Kullanımı
var kayit = DB.Kisis.OrderBy("ID");
foreach(Kisi per in kayit)
{
Console.WriteLine("ID : {0} - Ad : {1} - Soyad : {2} ", per.ID,per.Ad, per.Soyad);
}
Yukarıda da gördüğünüz gibi DynamicLINQ da herşey String tabanlı. Çok da zor değil değil mi? Hangi kolona göre sıralama yapmak istiyorsak onu OrderBy metoduna string olarak veriyoruz ve iş bitiyor.
-> Select Kullanımı
var kayit = DB.Kisis.Select("new(Ad,Tip)");
foreach (var per in kayit)
{
Console.WriteLine(per.ToString());
}
"Select" kullanımı da gayet basit : "new(kolon1,kolon2)" tarzında istediğiniz kolonu çekebiliyorsunuz. Hatta şöyle de kullanabiliriz :
var kayit = DB.Kisis.Select("new(Ad as AdSoyad,Tip)");
foreach (var per in kayit)
{
Console.WriteLine(per.ToString());
}
Yukardaki kodu derlediğimiz zaman çıkan sonuç
Gördüğünüz gibi as deyimi ile yeni oluşturduğumuz dinamik sınıfımızın alan etiketlerini de değiştirebiliyoruz.
-> GroupBy Kullanımı
var kayit = DB.Kisis.GroupBy("Tip","new(Tip)");
GroupBy kullanımı ise yukarıdaki örnekteki gibidir.Gruplama yapılacak kolon adı ilk değişkene, gruplama sırasında oluşturulacak dinamik sınıfın alan adını da ikinci değişkene atıyoruz.Bu sayede tablodak kayıtları bir ağaç haline getirmişiz gibi düşünülebilir.
Herşey görüldüğü üzere süper, herşey dinamik. Peki, yazdığımız sorgu yanlış ise ne olucak?
Linq da yazdığımız sorgu hatalı ise hatanın hemen farkına varırız; çünkü, yaptığımız aslında syntax hatasıdır ve yorumlayıcı bunun hemen farkına varır.
DynamicLINQ da ise sorgu olarak gönderdiğimiz string değerin içinde bir hata olup olmadığı ancak çalışma zamanında belli olur.Bu yüzden DynamicLINQ ile birlikte yeni bir hata sınıfı geliyor : ParseException
Hemen nasıl kullanıldığına bir göz atalım.
try
{
var kayit = DB.Kisis.Select("new(Ad AdSoyad,Tip)");
foreach (var per in kayit)
{
Console.WriteLine(per.ToString());
}
}
catch(ParseException ex)
{
Console.WriteLine("Pozisyon : {0} \nHata : {1} ", ex.Position ,ex.Message);
}
Ad ve AdSoyad arasındaki as ifadesini sildim farkettiyseniz ve işte sonuç :
Hata çıktısı
->Take ve Skip ifadelerinin kullanımı
Take(int count) ve Skip(int count) metodları zaten tanıdığımız metodlar ama ben bir örnek vermek istiyorum.
try
{
var kayit = DB.Kisis.Where("Tip=@0", new object[] { 'A' });
Console.WriteLine(kayit.Skip(1).Take(1).FirstOrDefault().Ad);
}
catch (ParseException ex)
{
Console.WriteLine("Pozisyon : {0} {2}Hata : {1} ", ex.Position, ex.Message, "\n");
}
Yaptığımız işlem, geri dönen kayıtlar arasında ilkini atlayıp sonrakiler arasından ilkini seçmek sonra da o kayıdın Ad değerini göstermek.Take ve Skip metodlarında bir sıkıntınız olacağını düşünmüyorum bu yüzden kısaca geçtim.
Neden DynamicLINQ kullanalım ki?
Hala kafasında bu konu hakkında soru işareti olanlarımız varsa başımdan geçen bir olayı anlatmak isterim.
Bir form ekranı hayal edin öyle ki üzerinde bir datagrid üç combobox ve bir de datetimepicker olsun.
Comboboxlardan ve DatePickerdan gelen verilerle filtreleme işlemi yapıp (filtreleme işleminde tüm kombinasyonlar olacak örn:2 combobox + datepicker dan gelen veriye göre filtreleme...) bunu gride yansıtmanız istense LINQ Expressionları kullanarak nasıl yaparsınız?
Bu senaryoda aklıma yirmi dört adet fonksiyon yazmaktan başka bir fikir gelmiyor.
İşte tam bu sırada DynamicLINQ yardımıma yetişti ve aşağıdaki kod ortaya çıktı :
if (cmbMusteri.SelectedItem.Value != "")
{
CID = DataReader.GetInt32(cmbMusteri.SelectedItem.Value);
if(str!=0)
{
Sort += " && ";
}
Sort += string.Format(" CustomerID={0} ", CID);
str += 1;
}
if (cmbHizmet.SelectedItem.Value != "")
{
HID = DataReader.GetInt32(cmbHizmet.SelectedItem.Value);
if (str != 0)
{
Sort += " && ";
}
Sort += string.Format(" workID={0} ", HID);
str += 1;
}
if (DataReader.GetDateTime(dtTarih.Value) != DateTime.Parse("01.01.0001"))
{
tarih = DataReader.GetDateTime(dtTarih.Value);
if (str != 0)
{
Sort += " && ";
}
Sort += string.Format(" date=@1 ");
str += 1;
}
if(cmbStatus.SelectedItem.Value!="")
{
SID = DataReader.GetInt32(cmbStatus.SelectedItem.Value);
if (str != 0)
{
Sort += " && ";
}
Sort += string.Format(" state_of={0} ", SID);
str += 1;
}
Not : DataReader. lara çok takılmayın o fonksiyonlar sadece Convert.To'lar :)
Bu işlemler sonucu oluşan string değişkeni, yani yukarıdaki örnekde SORT, verin bir metoda gerisini DynamicLINQ halleder.
Böylece DynamicLINQ'nun ana hatlarının üstünden geçtik.Daha derine inmek isteyen arkadaşlar olursa makalenin ekindeki projelerin içinde System.Linq.Dynamic projesini inceleyebilirler.Bu proje kütüphanenin açık kaynak kodunu içerir.
İsteyenler linkten Microsoft'un yayınladığı örnekleri indirip inceleyebilirler.
Sona sakladığım çok güzel bir dipnot: Sorgulama için oluşturduğunuz string değeri ister C# ister VB.NET syntax'ı kullanarak yazabilirsiniz.
[C#]
var kayit = DB.Kisis.Where("Ad==@0 && Soyad==@1", new object[] { "Mehmet","Karadayı" });
[VB.NET]
Dim kayit = DB.Kisis.Where("Ad=@0 and Soyad=@1", New Object() {"Mehmet","Karadayı"});
Bu makalemizin sonuna geldik. Başka bir makalede görüşmek üzer herkese iyi kodlamalar diliyorum.