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.