Makale Özeti

Bu yazımızda Silverlight 2.0 ile ADO.NET Data Services yapısının beraber kullanımına hızlı bir giriş yaparak veritabanından Data Services üzerinden dinamik olarak veri çekmeyi inceliyoruz.

Makale

Silverlight 2.0 tarafında veritabanı erişimi için mecburen web servisleri kullanmak zorundayız. Durum böyle olunca tek tek veritabanındaki işlemler için ayrı web servisleri yazmak bir noktadan sonra işkenceye dönüşebiliyor. Bugünlerde özellikle LINQ ve Entity Framework ile beraber data layer'larımızda ciddi kolaylıklardan faydalanabiliyoruz fakat web servisleri tarafında geldiğinde ise LINQ vs ile gelen nesneleri geri döndüren web servislerini tek tek yazmak yine can sıkıcı bir hal alıyor.

Aslında tüm bu sorunları çözebilecek bir altyapı .NET Framework içerisinde artık mevcut. ASP.NET Data Services adını verdiğimiz altyapı ile beraber bir veritabanına erişimi doğrudan REST üzerinden yapabiliyorsunuz.

Peki nasıl?

İlk önce gelin ASP.NET Data Services sonuç olarak nasıl bir hizmet yaratıyor ona bakalım. Bugün herhangi bir veritabanına farklı where sorguları ile select'ler göndermek istesek bu sorgulardaki where cümleciklerini parametreli hale getirmemiz ve bu parametreleri alarak uygun datayı döndüren web servisleri yazmamız gerekiyor. Ancak bu şekilde Silverlight ile veritabanına erişebiliyoruz. Farklı senaryolardan eğer sorgularınızın filtreleme şekilleri değişirse bu sefer tekrar gidip uygun web servisini yazmak zorunda kalıyorsunuz. Başka bir seçenek olarak where cümleciklerini parametre alan bir servis yazılabilir fakat bu pek güvenli bir manzara olmaz.

Tüm bu problemleri çözmek için ASP.NET Data Services ile sorgularınızı yazabileceğiniz özel bir syntax geliyor ve artık URL üzerinden sorgu gönderebiliyoruz.

http://localhost:4351/WebDataService1.svc/Uruns(2)

Örneğin yukarıdaki gibi bir adrese gittiğimizde veritabanındaki Uruns tablosunda ID'si 2 olan ürünün bilgilerini XML olarak almış oluyoruz.

[XML]

<?xml version="1.0" encoding="utf-8" standalone="yes"?>

<entry xml:base="http://localhost:4351/WebDataService1.svc/" xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" xmlns="http://www.w3.org/2005/Atom">

  <id>http://localhost:4351/WebDataService1.svc/Uruns(2)</id>

  <title type="text"></title>

  <updated>2009-01-11T22:18:06Z</updated>

  <author>

    <name />

  </author>

  <link rel="edit" title="Urun" href="Uruns(2)" />

  <link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/Kategori" type="application/atom+xml;type=entry" title="Kategori" href="Uruns(2)/Kategori" />

  <category term="SilverlightApplication17.Web.Urun" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />

  <content type="application/xml">

    <m:properties>

      <d:ID m:type="Edm.Int32">2</d:ID>

      <d:Adi>Ürün2</d:Adi>

      <d:KategoriID m:type="Edm.Int32">1</d:KategoriID>

    </m:properties>

  </content>

</entry>

Tahmin ettiğiniz üzere aslında bizim normal şartlarda yazdığımız web servisleri de kısmen bu işi yapıyor. Yani bize XML ile istediğimiz veriyi gönderiyoruz. Şimdi bir düşünelim, bu şekilde esnek olarak URL üzerinden farklı sorgular gönderdiğimde bana istediğim veriyi XML ile sanki web servisinden sonuç dönüyormuş gibi döndüren bir sistem aslında benim Silverlight tarafından veritabanını sorgulamam için çok daha esnek bir yapı olmaz mı? Her farklı sorgu için ayrı ayrı web servisleri yazmaktan kurtulmaz mıyım? Evet :) amaç da zaten bu.

Tabi bu arada bir web servisinin çalışma şeklide ve sağladığı XML'lerin snytax'ı ile buradaki biraz farklı. Konumuz dışında olsa da aradaki bu farkın bilincinde olmakta fayda var.

Yapalım şu işi...

Gelin hızlı bir örnek ile ASP.NET Data Services yapısının kullanımını ve Silverlight tarafındaki yansımalarını giriş seviyesine inceleyerek ilerleyelim. İlk olarak yeni bir Silverlight projesi yaratıyor ve yanına da güzel bir ASP.NET sitesi alıyoruz. ASP.NET sitemize hemen bir ADO.NET Data Service dosyası eklememiz gerekiyor. Bunu ASP.NET sitenize sağ tıklayarak "Add New Item" diyerek gelen pencereden uygun dosyayı seçip yapabilirsiniz.

[VB]

Imports System.Data.Services

Imports System.Linq

Imports System.ServiceModel.Web

 

Public Class WebDataService1

    Inherits DataService(Of DataClasses1DataContext)

 

    Public Shared Sub InitializeService(ByVal config As IDataServiceConfiguration)

        config.SetEntitySetAccessRule("*", EntitySetRights.AllRead)

    End Sub

 

End Class

ADO.NET Data Service'i projenize eklediğinizde hemen karşınıza yukarıdaki kodlar çıkacaktır. Koyu ile yazılı kısımları bizim elle eklememiz gerekiyor. DataClasses1DataContext olarak adı geçen şey aslında projemizdeki bir LINQ2SQL Classes dosyası. ADO.NET'in Data Servisleri üzerinden hangi Entity'leri yayınlayacağını belirlememiz gerek. Bu nedenle aslında bir data servisi yaratmadan önce ya LINQ2SQL DBML dosyanızı hazırlamanız ya da Entity Framework tarafında Entity'lerinizi hazırlamanız gerekiyor. Yazımızın konusu dışında olduğu için işin bu kısmına şimdilik değinmeyeceğim. Önemli olan yarattığınız bu veri kaynağının yukarıdaki şekilde Data Servisi'nize aktarmanız.

Bir sonraki adımda ise veri kaynağındaki hangi Entity'lere ne şekilde erişim hakları vereceğiniz. Yani bu data servislerini kullanarak insanlar sadece SELECT mi yapabilecek, yoksa Update veya Delete işlemi de yapabilecekler mi ona karar vermemiz gerekiyor. Bu noktada güvenlik açısından epey dikkatli olmak gerek. Ben şimdilik AllRead diyerek sadece SELECT için veri kaynağındaki tüm tabloları * işareti ile açtım.

Silverlight tarafındaki maceralar.

Servisimiz hazır olduğuna göre artık sıra geldi Silverlight tarafında bu servisi kullanmaya. Başlangıç için normal bir web servisi kullanmaktan pek farklı olmadığını söyleyebilirim. Silverlight projemize sağ tıklayarak "Add Service Reference" diyoruz ve ADO.NET Data Service'imizin SVC dosyasının adresini veriyoruz. Böylece gerekli istemci taraflı proxy yaratılmış oluyor.

Her zamanki gibi veri kaynağımızı kullanmadan önce servis üzerinden bir bağlantı kopyası almamız gerekecektir. Normal şartlarda Silverlight ile SoapClient sınıflarından kopya alırken bu sefer doğrudan DataContext alacağız.

[VB]

Dim Veri As New ServiceReference1.DataClasses1DataContext(New Uri("http://localhost:4351/WebDataService1.svc/"))

DataContext'imizi alırken servisimizin tam yolunu da parametre olarak veriyoruz. Böylece artık bu servis üzerindeki tüm veriye ulaşabiliriz. Sıra geldi sorgularımızı yazmaya. Silverlight tarafında web servislerinin kullanımında da olduğu üzere tüm veri trafiğinin asenkron ilerleyeceğini hatırlarsak aslında sorgularımızı gönderip sonrasında ayrı bir event-listener ile sonucu alacağımızı da tahmin etmek zor değil. ADO.NET Data Services sorgularının URL üzerinden farklı bir syntax ile gittiğini görmüştük fakat elimizde bir DataContext olduğuna göre geri gelen IQueryable nesneleri LINQ ile sorgulayabiliyor olmamız gerekir. Söz konusu LINQ sorguları otomatik olarak Data Services tarafına uygun şekilde çevrilerek gönderilecektir. Sözü daha fazla uzatmadan kodumuzu inceleyelim.

[VB]

Dim Sorgu As System.Data.Services.Client.DataServiceQuery(Of ServiceReference1.Urun) = From gelenler In Veri.Uruns Where gelenler.ID = 2 Select gelenler

Sorgu.BeginExecute(New AsyncCallback(AddressOf Geldi), Sorgu)

[C#]

            System.Data.Services.Client.DataServiceQuery<ServiceReference1.Urun> Sorgu =

                (System.Data.Services.Client.DataServiceQuery<ServiceReference1.Urun>)

                from gelenler in Veri.Uruns where gelenler.ID == 2 select gelenler;

            Sorgu.BeginExecute(new AsyncCallback(Geldi), Sorgu);

Yukarıdaki kodlar bir sorgunun sunucu tarafına gönderilmesini sağlayacak olan kodlar. Sorgumuzu ilk satırda standart LINQ sorgusu olarak yazıyoruz fakat unutmayın ki burada bazı sınırlamalar var. ADO.NET Data Services sorgularında tüm keyword'leri kullanmak mümkün olmuyor. O nedenle eğer buradaki LINQ sorgularında Take vs gibi bazı keyword'leri kullanırsanız Visual Studio hata verecektir.

Yazdığımız sorguyu bir DataSerivceQuery değişkenine eşitliyoruz ve Query nesnemizi yaratırken de geriye ne tür bir nesne döneceğini yine servis üzerinden gelen nesne tanımı ile belirtiyoruz. Artık sorgumuz hazır olduğuna göre BeginExecute ile çalıştırabiliriz. Fakat bu noktada da iki parametreye ihtiyacımız var; birincisi bu sorgu tamamlandığında hangi event çalıştırılacak? Yani bir Callback lazım bize. Asenkron bir Callback yaratarak ilerliyoruz. İkinci parametre ise sorgunun kendisi. Böylece CallBack çalıştığında buradaki sorgunun state'i de geri dönecek.

[VB]

Sub Geldi(ByVal ar As IAsyncResult)

        Dim Sorgu As System.Data.Services.Client.DataServiceQuery(Of ServiceReference1.Urun) = ar.AsyncState

        Dim result = Sorgu.EndExecute(ar)

        MessageBox.Show(result.SingleOrDefault.Adi)

End Sub

[C#]

        void Geldi(IAsyncResult ar)

        {

            System.Data.Services.Client.DataServiceQuery<ServiceReference1.Urun> Sorgu =

                (System.Data.Services.Client.DataServiceQuery<ServiceReference1.Urun>)ar.AsyncState;

            var result = Sorgu.EndExecute(ar);

            MessageBox.Show(result.SingleOrDefault().Adi);

        }

Geldi adındaki asenkron callback'imiz çalıştığında hemen kendisine parametre olarak gelen Result'ın için AsyncState üzerinden Sorgu değişkenimizi alıyoruz. Artık sorgu tamamlandığında göre çalışma işlemini de sonlandırıp sonucu almak gerek. EndExecute metoduna tekrar Callback'e gelen parametreyi verip sonucun bir değişkene aktarılmasını sağlıyoruz ve aldığımız değişken üzerinden istediğimiz veriye ulaşabiliyoruz.

İşte bu kadar...

Data Services yapısı ile Silverlight tarafındaki kodlamanın çok kolaylaştığını söylemek pek doğru olmaz. Elimizde veriyi sağlayan hazır bir web servisi olsaydı çok daha rahat bir kodlama ortamına sahip olabilirdik fakat Data Services bize web servislerine dokunmadan tek bir altyapıya bağlanarak istediğimiz sorguları çalışma zamanında oluşturma şansı tanıyor. Tabi bu sorgular sadece SELECT sorguları olmak zorunda değil, yeri geldiğinde Update, Delete ve Insert de yapabiliriz. Bu makalemizde giriş seviyesinde kalacağımız için şimdilik diğer işlemlere pek dokunmayacağız.

Hepinize kolay gelsin.