Makale Özeti

Language Integrated Query sözcüklerinin kısaltmasıdır LINQ. Bu ilk yazgeliştir makalemde LINQ teknolojisini ve kullanmak için gerekenleri tanıtacağım.

Makale

Giriş

LINQ, yukardaki kısaltmadan da çıkarılacağı gibi, programlama dilleri ile bütünleşik sorgular yapılmasına imkan sağlayan bir teknolojidir. C# 2.0 ın son kullanıcıya ulaşması ile birlikte, Microsoft Research çalışma gruplarından bir tanesi COmega adını verdikleri bir C# türevi üzerinde çalışmalarını yayınlamıştı. COmega projesinin ürünü olan dilde hafızadaki koleksiyonları (Array, List, Collection.. vs.) SQL komutlarına benzer bir sözdizimi ile sorgulayabiliyordunuz. İlk gördüğümde çok şaşırmıştım. Ama yakında görebileceğimiz C# 3.0 için bir özellik olabileceğini de tahmin etmiyordum açıkçası. Fazla lafı uzatmadan ufak bir örnek ile olayı netleştirmek istiyorum :

var numbers = new int[] { 1, 4, 2, 7, 8, 9 };
var evenNumbers = 
	from p in numbers
	where (p % 2) == 0
	select p;
 

Bu satırlarda yapılan işlemi SQL diline aşina olanlarınız anlamıştır hemen. İşte LINQ bize en temel manada, uygulama kodumuzdaki veriyi sorgulamak için güçlü dil özellikleri sağlar.

LINQ projesi beraberinde DLINQ ve XLINQ olmak üzere iki alt projeyi de bizlere sunuyor. D database'den X ise XML den geliyor. XLINQ hakkında güzel bir makale bu sitede de yayınlanmıştı. Ben sizlere bu yazımda DLINQ ten bahsetmek istiyorum.

DLINQ, veritabanımızdaki veriyi LINQ in sağlamış olduğu bütünleşik sorgulama rahatlğı ile yönetmemizi sağlıyor. Bu aslında LINQ tanımından uzaklaşmak gibi görünse de değil. Olaya basit bir açıdan şöyle bakabiliriz:

DLINQ, yazılım geliştiriciye, örneğin Database Access Layer'ı için kullanacağı, veritabanındaki entitylerin(tabloların diyelim) programlama ortamında soyutlandığı sınıfları sağlar. Bu birincisi. Sizin adınıza veritananı erişim kodlarını bu sınıflara müdaheleniz gerekmeden implemente eder. Yani sizi veritabanı kodundan soyutlar. İkinci olarak da LINQ in getirdiği sorgulama yeteneklerini sınıflarınız ile işlem yaparken kullanmanızı sağlar. Aslında kısaca :

Sizi çoook büyük bir yükten kurtarır.

Düşünsenize, uygulamanızın tasarımını ortaya koymuş, buna uygun bir veritabanı prototipi de hazırlamışsınız. Sonra birisi elinde sihirli bir değnek ile, balkabağını, veritabanı şemanızı işleyen C# veya Visual Basic kodlarına çeviriyor. İşin en güzel yanı saat sınırı yok bu sihrin. :) Bir de üstüne en favori IDE niz Visual Studio da güzel güzel sorgulama imkânı sunuyor.

Bu anlattıklarımı yapan başka araçlar yok mu? Var. Bu sitede bir makalede bahsedilmiş birkaçından.  Ama genelde sadece sınıfları oluşturuyor bu araçlar. LINQ (beraberindeki D ve X ile birlikte) ise geliştiricilere kod jeneratöründen çok daha farklı bir yazılım anlayışının haberciliğini yapıyor.

Şimdi biraz hareketlenelim.

http://msdn.microsoft.com/data/ref/linq/ adresine gidelim. "Çok sevdim, çok çok detaylı okumak istiyorum" diyenler bu sitede yeterli kaynak bulabilirler. Tabi ne yazık ki ingilizce. Teknoloji alanında Türkçe kaynağın artması dileği ile biz devam edelim.

http://www.microsoft.com/downloads/details.aspx?fa... adresinden LINQ projesinin Mayıs CTP sini indirip kurabilirsiniz. Bu makalede anlatmaya çalışacağım herşeyin, bu sürüm ile gerçekleştirilmesi gerekir (bilirsiniz pre-release sürümler birbirine uyumsuz olabilir genelde).

ÇOK ÖNEMLİ NOT : LINQ yüklediğiniz makinada Visual Studio ile iş amaçlı geliştirme yapıyorsanız diğer projelerinizde çalışırken sıkıntı çekebilirsiniz. Bu projenin bir parçası da C# 3.0 ön izleme dil servisleri. Teoride eski projelerinizin derlenmesinde vs sıkıntı çıkmayabilir. Ama özellikle IDE nizin davranışında beklenmeyen durumlar ortaya çıkabilir (Bazı Smart Tag lar vs bozulabiliyor. Örneğin ImpementInterface smart tagı). C# 3.0 dil özelliklerini kaldırmak ve tekrar kurmak için iki script hazırlayıp kurulum klasörüne atmışlar evet. Ama smart taglarınızı geri vermiyor bunlar. Özetle LINQ projesi release olana kadar bazen, biraz baş ağrıtabilir. Yine de yüklemeye değer.

Yukardaki notu da okuduysanız, makalenin devamını LINQ yüklenmiş bir ortam için yazmaya devam edeceğim.

Uygulama

Herhangi bir projemizde LINQ kullanmaya başlamadan önce temel kural : tüm veritabanı geliştirme aşamalarının sona erdirilmiş olmasıdır. Yoksa oluşturduğunuz DLINQ sınıfları güncelliğini yitirebilir.

Ben bu makaleyi hazırlamak için basit bir konu seçtim. Basit bir blog uygulaması. Bu uygulama 4 tablodan oluşan bir veritabanını kullanacak :

Entrys : Bizim blog girdilerimizi tutacak tablo.
Comments : Girdilerimize yapılacak yorumları tutacak tablo.;
Tags : Blog girdilerimiz için atayacağımız konu etiketlerinin tanımlandığı tablo.
EntryTags : Girdilerin etiketler ile eşleneceği tablo (bir girdinin birden çok etiketi olabilir, ve bir etiket birden çok girdide olabilir mantığı ile)

Bu tabloları yaratalım :

    Önce LinqBlog adında bir veritabanı oluşturun. (örneğin Microsoft Sql Server Manager'ı kullanarak) Daha sonra bu veritabanı seçili iken yeni bir sorgu penceresi açın. (veritabanına sağ tıklayıp new query'yi seçin). Bu açılan sorguya yandaki linkte verdiğim text dosyadaki SQL ifadelerini kopyalayıp yapıştırın ve çalıştırın. Böylece örnek veritabanımız hazır. (SQL İfadeleri için tıklayın)

    Şimdi sırada DLINQ in sahneye çıkması var. 

    Visual Studio 2005'i açıp File|New menüsünden Web site'ı seçelim. Karşımıza kullanıma hazır web projesi şablonları gelecektir. Bu şablonların içinden LINQ ASP.NET Web Site Template isimli olanı seçin. Projeye bir isim verip (örneğin LinqBlog) OK diyelim.

    Karşımıza şu anda normalden pek de farklı görünmeyen bir web sitesi geldi. Fakat bu web sitesi yazılan LINQ kodları ile derlenmeye hazır. Bunun için web.config dosyasına göz atabilirsiniz.

    Şimdi veritabanı erişim metodlarımızı hazırlayalım. Adım adım yapmamız gerekenleri yazıyorum:

    RTM öncesi aşamalarda doğal eksikliklerden biri web projelerinde dlinq dosyalarının otomatik oluşturulamaması. Ama biz bu eksikliği solution'a yeni bir linq class library ekleyerek aşabiliriz. File|New menüsünden Project'i seçelim. Açılan pencerede c# dalını genişletip LINQ preview'ı seçelim. Sağdaki proje şablonlarından LINQ Library yazanı seçip, bir isim verip (örneğin DB) OK diyelim.

    Yeni eklenen projeye sağ tıklayıp Add new item ı seçelim. Açılan pencerede DLINQ Objects isimli şablonu seçip OK diyelim. Karşımıza bu nesnenin tasarım formu gelecektir. Bu formda DLINQ nesnelerini elimizle de oluşturabiliriz. Ama bunun bizim gibi basit uygulamalar için daha kolay bir yolu var. Server Explorer da yeni bir bağlantı ekleyelim. Bu bildiğimiz bir veritabanı bağlantısı olacak. Tabiki makalenin başında hazırladığımız veritabanına.

    Bağlantıyı oluşturduktan sonra bağlantının içinden tabloları ortaya çıkarıp tüm tabloları CTRL basılı olarak seçip, DLINQ object designer a sürükleyelim. Birkaç saniye içinde tasarım ortamı bizim için veritabanı ilişkilerimizle birlikte DLINQ sınıflarımızı ve metodlarımızı oluşturdu. Bu sınıfların yapısını ve çalışma prensiplerini sizlere bırakıyorum. Temelde veritabanındaki nesnelerimizi .NET sınıfları olarak soyutladı. Biz şimdi nasıl kullanıldıklarına bakalım.

    Web sayfası projemize gelelim. Solution Explorer da projeye sağ tıklayıp add referance diyelim. Burada project sekmesine gelip eklediğimiz DB isimli LINQ library projesini refere edelim.

    Blog sayfamızda birşeyler göstermek için öncelikle blogumuzda birkaç entry olmalı. Gelin ilk önce entryleri girebileceğimiz bir sayfa yapalım. Blog sayfalarını hazırlarken erişim ve güvenlik konularını gözardı edeceğim, konumuzun dışındalar çünkü. Gerçek bir uygulamada tabiki birincil derecede ele alınmalılar.

    Yeni bir sayfa yaratalım. Adı EntryEkleme.aspx olsun. Sayfaya bir entry girmek için gerekli 3 bilgiyi alacağımız alanlar ve bir buton yerleştirelim :

    <body>
        <form id="form1" runat="server">
        <div>
            <strong>YENİ ENTRY<br 
    
    />
                <br />
            </strong>
            <asp:Label ID="Label1" runat="server" Text="Ba?lyk"></asp:Label>&nbsp;
            <asp:TextBox ID="txtBaslik" 
    
    runat="server" Width="221px"></asp:TextBox><br />
            <asp:Label ID="Label2" runat="server" Text="Yçerik"></asp:Label>&nbsp;
            <asp:TextBox ID="txtIcerik" 
    
    runat="server" Height="66px" TextMode="MultiLine" Width="223px"></asp:TextBox><br />
            <br />
            <asp:Label ID="Label3" runat="server" Text="Etiketler"></asp:Label>&nbsp;
            <asp:TextBox ID="txtTags" 
    
    runat="server"></asp:TextBox>
            <asp:Label ID="Label4" runat="server" Text="Yaşam, Sanat, C# gibi."></asp:Label><br />
            <br />
            <asp:Button ID="Button1" 
    
    runat="server" Text="Ekle"/><br />
            <br />
            <asp:Label ID="lblMesaj" 
    
    runat="server"></asp:Label></div>
        </form>
    </body>

    Sayfanın kaynak görünümünde body tagı ve içeriği buna benzer olmalı. Yerleştirdiğimiz butona çift tıklayıp on_click olayını düzenleyelim şimdi. Yapacağımız işlem :

    Yeni bir Entry nesnesi oluşturmak, Entry nesnesi için gerekli atamaları yapmak. Kullanıcının virgülle ayrılmış şekilde yazdığı etiketler veritabanında var ise bunları Entry nesnemize bağlamak. Yoksa Veritabanına kaydedip Entry'ye bağlamak. Ve tüm bu işlemleri LINQ nesneleri ile yapmak. 

     

            //LINQ iþlemleri için veritabanımızın LINQ Data Context 
    
    nesnesini oluþturalım.
            DB.LinqBlogDataContext lbDB = new DB.LinqBlogDataContext();
    
            //Yeni bir Entry sınıfı oluþturup özellikleri textboxlardan alalım.
            DB.Entry mEntry = new DB.Entry();
            mEntry.EntryTitle = txtBaslik.Text;
            mEntry.EntryText = txtIcerik.Text;
            mEntry.EntryDate = DateTime.Now;
            
            lbDB.Entries.Add(mEntry);
    
            //Kullanıcıcın virgülle girdiği Etiketleri bir diziye alalım.
            string[] userTags = txtTags.Text.Split(new string[] { "," }, 
    
    StringSplitOptions.RemoveEmptyEntries);              
    
            //kullanıcının yazdığı her bir tag için :
            foreach (string tagname in userTags)
            {
                
                DB.Tag secilenTag;
                try
                {
                    // veritabanında var mı yok mu bakalım.
                    
                    secilenTag = (from t in lbDB.Tags
                                    where t.TagName == tagname.Trim()
                                    select t).First();
                }
                catch(InvalidOperationException invOpEx)
                {
                    //etiket yokken oluşan exception u yakaladık, bunu veritabanına 
    
    kaydediyoruz.
                    secilenTag = new DB.Tag();
                    secilenTag.TagName = tagname.Trim();
                    lbDB.Tags.Add(secilenTag);
                    lbDB.SubmitChanges();                
                    secilenTag = (from t in lbDB.Tags
                                    where t.TagName == tagname.Trim()
                                    select t).First();
                }            
    
                // Bu satıra geldiğimizde Elimizde bir tag var. Bu tagı girdiğimiz 
    
    entry ile
                // ilişkilendiriyoruz.
                DB.EntryTag et = new DB.EntryTag();            
                et.TagId = secilenTag.TagId;
                mEntry.EntryTags.Add(et);
                lbDB.SubmitChanges();
            }
    

    Yukardaki kodlar kalabalık gibi görünse de aslında yazması çok kısa sürüyor. Yani aynı işi Connection ve Command nesneleri ile direk yapmaya çalışmaktan daha kısa. Ayrıca sorgulama mantığı kodda olduğu için okunabilirlik de çok artıyor.

    Şimdi gelelim bu girilen entrylerin gösterilmesine. Blog uygulamamızda anasayfada son girilen 10 entry'nin gösterileceğini düşünelim. Default.aspx sayfamıza bir DataList koyup adını dlSon10Baslik yapalım. Daha sonra sayfaya çift tıklayıp page_load olayını aşağıdaki kod ile dolduralım :

            
    
    if (!Page.IsPostBack)
            {
                // Data Context nesnemizi oluşturuyoruz.
                LinqBlogDataContext lbDB = new LinqBlogDataContext();
                //İlk 10 Entry yi çekiyoruz.
                var mEntries = (from en in lbDB.Entries
                                orderby en.EntryDate descending
                                select en).Take(10);
                // Kilit metod : ToDataTable()  
                // bu metod sayesinde linq sorgulanabilir nesnelerini bildiğimiz 
    
    DataTable formatına çevirebiliyoruz.
                // benzer bir çevrim DataTable dan linq sorgulanabilir nesnelerine 
    
    çevrim için de var. (ToQueryable)
                dlSon10Baslik.DataSource = mEntries.ToDataTable();            
                dlSon10Baslik.DataBind();
            }

    Bu kodda Take(10) komutu ile yapılan sorgudan ilk 10 unu almış olduk.  Parantez içinde de yine SQL benzeri bir sözdizimi ile verimizi EntryDate alanına göre azalan sırada getirdik.

    Koyduğumuz DataList in Item Template'i aşağıdaki gibi olabilir :

                <ItemTemplate>
                    <asp:Label ID="lblBaslik" runat="server" Font-Bold="True" Text='<%# Eval("EntryTitle") %>'></asp:Label><br />
                    <asp:Label ID="lblIcerik" runat="server" Text='<%# 
    
    Eval("EntryText") %>'></asp:Label>
                    <br />
                    <asp:Label ID="lblTarih" runat="server" Text='<%# 
    
    Eval("EntryDate") %>'></asp:Label><br />
                    <br />
                </ItemTemplate>

    Blog yazılımlarında bir de aylara göre arşiv diye bir kavram vardır bilirsiniz. Şimdi aylara göre entry sayılarını derleyen bir sorgu tasarlayıp bir başka DataList'e de bunu getirelim. Bunu bir user control şeklinde tasarlayalım. Yeni bir Web User Control ekleyelim. Adı ArsivCont.ascx olsun. Tasarım görünümünde, kontrolümüze bahsettiğimiz gibi bir DataList ekleyelim. DataList'imizin adı dlArsiv olsun. User Control'ümüzün kaynak görünümü aşağıdaki gibi olsun. Gördüğünüz gibi Item Template i için Basit bir Hyperlink şablonu oluşturduk.

    <%@ Control 
    
    Language="C#" AutoEventWireup="true" CodeFile="ArsivCont.ascx.cs" Inherits="ArsivCont" %>
    <asp:DataList ID="dlArsiv" 
    
    runat="server">
        <ItemTemplate>
            <asp:HyperLink ID="HyperLink1" runat="server" NavigateUrl='<%# 
    
    "AyaGoreEntryler.aspx?Ay="+Eval("Ay")+"&Yil="+Eval("Yil") %>' Text='<%# AyIsmi(Eval("Ay").ToString()) +"-(" + Eval("Yil")  + 
    
    ") : " + Eval("EntrySayisi") %>'></asp:HyperLink>
        </ItemTemplate>    
    </asp:DataList>
    

    Item template deki HyperLink kontrolümüzün NavigateUrl özelliği gördüğünüz gibi AyaGoreEntryler.aspx isimli bir sayfayı gösteriyor. Önce dlArsiv isimli DataList'imizi kontrolümüzün Page_Load olayında dolduralım. Bir de sorgudan gelen ayın ismini getirmek için ufak bir fonksiyon yazacağız. (Yukarda kullandık bu fonksiyonu) Bu kodlar aşağıdaki gibi olacak :

        protected void Page_Load(object sender, EventArgs e)
        {
            LinqBlogDataContext lbDB = new LinqBlogDataContext();        
    
            // Bu cümlede group by ifadesini görüyoruz. Yıla ve Aya göre veriyi 
    
    grupluyoruz. 
            // İki alana göre grupladığımız için new { ec.EntryDate.Year, 
    
    ec.EntryDate.Month } 
            // diyerek bu iki alanı tek bir alan gibi (bileşik bir alan gibi 
    
    düşünelim)  kullanıyoruz.
            // Tek bir alana göre gruplasaydık, mesela Tarihe göre, o zaman sadece 
    
    ec.EntryDate yazmamız yeterli olurdu.
            var Arsiv = from ec in lbDB.Entries                     
                        group ec by new { ec.EntryDate.Year, ec.EntryDate.Month 
    
    } into g                    
                        orderby g.Key.Year,g.Key.Month descending
                        select new { Ay = g.Key.Month, Yil = g.Key.Year 
    
    ,EntrySayisi=g.Count()};
    dlArsiv.DataSource = Arsiv.ToDataTable(); dlArsiv.DataBind(); } public string AyIsmi(string Month) { DateTime m = new DateTime(1, int.Parse(Month), 1); return m.ToString("MMMM"); }


    AyaGoreEntryler.aspx sayfası için de yapacaklarımız basit. Default.aspx sayfasındaki DataList'in aynısını (Item Template i ile) alalım. Bu DataList'e QueryString ile gönderdiğimiz ay ve yılda yayımlanan Entryleri getireceğiz. Bunun için Page_Load olayındaki kodumuz aşağıdaki gibi olacak :

     

            LinqBlogDataContext lbDB = new 
    
    LinqBlogDataContext();
    
            DateTime SecilenTarih;
    
            //QueryString den yanlýþ bir tarih gelirse (yani tarih oluþturulamazsa) 
    
    bulunduðumuz aydaki Entryleri getirmek için
            //tarihi bugüne atýyoruz.
            try
            {
                SecilenTarih = new DateTime(int.Parse(Request.QueryString["Yil"]),int.Parse(Request.QueryString["Ay"]),1);            
            }
            catch (Exception ex)
            {
                SecilenTarih = DateTime.Now;
            }
    
            // Basit bir where þartýný uyguluyoruz.
            var AylikEntry = from en in lbDB.Entries
                             where (en.EntryDate.Month==SecilenTarih.Month) && 
    
    (en.EntryDate.Year==SecilenTarih.Year)
                             select en;
    
            dlAyaGoreEntryler.DataSource = AylikEntry.ToDataTable();
            dlAyaGoreEntryler.DataBind();

     

    Sonuç

    Makalemizde bu kısma kadar basit bir blog uygulamasının birkaç işlevini yerine getirecek sayfaları (tasarım hariç :) ) ve kodları (güvenlik, hız vs gözetmeden) hazırlamış olduk. Hız konusunu özellikle vurgulamak isterim. LINQ kütüphanesi şu ana kadarki pre-release sürümlerinin hiç birinde hız için optimize edilmemiş. Ve yavaş çalışıyor. Ama Release olmaya yakın CTP lerde gözle görülür bir hız artışı farkedebileceğimiz müjdeleniyor.

    Bu makalede hazırladığımız uygulamanın kodlarını dosyalar bölümünde arşivlenmiş halde bulabilirsiniz. (Winrar gerektirir.) ÖNEMLİ: Rar Arşivinin içindeki LinqBlog klasörünü C:\ kök dizinine açarsanız, içindeki solution dosyası ile sorunsuz açabilirsiniz. Veritabanını oluşturacak SQL ifadeleri yukarda da belirttiğim gibi bu adreste

    Diğer makalelerimde LINQ ve özellikle DLINQ in çarpıcı başka özelliklerini vurgulayacak senaryolar hazırlayacağım. Join işlemleri gibi.

    Sorularınız, yorum ve önerileriniz için bana buraksarica[et]bb.com.tr adresinden ulaşabilirsiniz.

    Herkese iyi çalışmalar.

    Burak SARICA

    Bilgisayar Mühendisi
    B&B Bilişim Bilgisayar / Kayseri