Makale Özeti

Bu yazımızda .NET RIA Services yapısına ön bir bakış atarak genel CRUD işlemlerinin nasıl yapılabileceğine göz atıyoruz.

Makale

Günümüzde artık tarayıcı içi iş uygulamalarında uygulama geliştirme platformu olarak Silverlight dışında pek birşey düşünülemiyor. Fakat bu manzara içerisinde de tabi ki daha üçüncü sürümünde bir ürün olarak Silverlight'ın eksikleri var. Bu eksiklerin bazıları maalesef Silverlight yapısı gereği içerisine oturduğu mimari rolle de alakalı olabiliyor. Çok basit bir örnek olarak veri erişim modelini ele alabiliriz. Bugün Silverlight ile bir uygulama geliştirmek isteyen herhangi bir iş uygulaması geliştirme ekibinin alışması gereken ilk şey servis yönelimli mimari. Buraya kadar herşey aslında çok güzel fakat en basit CRUD (Create, Read, Update; Delete) operasyonları için bile birer servis hazırlayıp, bu servislerin referanslarını alıp SL tarafında tek tek kullanıyor olmak bazı durumlarda rahatsız edici şekilde uygulama geliştirme sürecini uzatabiliyor da.

.NET RIA Services!

İş uygulamalarınızın çapına/büyüklüğüne göre .NET RIA Services dertlerinize çare olabilir. Peki nasıl? Aslında yapmak istediğimiz şey bir şekilde http:80 üzerinden CRUD operasyonlarını yapabilmek. Hemen belki de aklınıza ADO.NET Data Services gelecektir. Kesinlikle doğru yoldasınız. Aslında ADO.NET Data Services ile sunucu tarafında ciddi bir gelişme olmuştu ve artık URL üzerinden sorgularımızı atabiliyor, sonrasında da XML olarak cevabı alabiliyorduk. Fakat hala bazı sorunlar vardı, çünkü ADO.NET Data Services'ın kullanımı Silverlight tarafında pek de kolay değildi. Bu gelişmeleri takiben .NET RIA Services aslında ADO.NET Data Services üzerine oturarak bu altyapının Silverlight ile rahatlıkla konuşabilmesini sağladı. Validasyon kodlarının hem sunucu hem istemci tarafında iki defa yazılması gibi sorunları belirli senaryolarda gidererek .NET RIA Services şu anda July Preview sürümü ile indirilebilir durumda. Daha Release olmamış (yayınlanmamış) bir ürün olduğunun altını çizerek devam edelim.

RIA Services'ın development sürecinde bir çok avantajı var. Bunlardan ilki artık sunucu tarafındaki uygulamanız ile istemci tarafındaki uygulamanızın tek bir uygulama gibi gözükmesi. İlk bakışta çok garip geleceğinin farkındayım fakat artık istemci tarafındaki Silverlight uygulamanızdan sunucuya anında ulaşabiliyorsunuz, herhangi bir şekilde service reference eklemeniz vs gerekmiyor. Tabi tüm bu işlemleri biz yapmıyorsak bizim yerimize birileri yapıyor demektir ki bu durumda Visual Studio yardımımıza koşuyor ve otomatik olarak sunucu ile istemci taraflarını birbiri ile konuşturacak ek kodları yaratıyor. Ayrıca RIA Services tarafında da bulunan bazı sınıflar tabi ki kullanılıyor. Validasyonla ilgili tüm sunucu taraflı tanımlamalar otomatik olarak istemci tarafına da derleme zamanında ekleniyor. Tüm bu kolaylıkları görmek için gelin yeni bir Silverlight projesi yaratalım.

RIA Services kullanacağız.
RIA Services kullanacağız.

Yeni bir Silverlight projesi yaratırken karşınıza çıkan ilk ekranda projeye bir ASP.NET Web Sitesi eklediğimiz gibi bir de "Enable .NET RIA Services" checkbox'ını işaretlememiz gerekiyor. Böylece gerekli referanslar alınacak ve ayarlar yapılacaktır. Yapılan ayarlardan ve altyapının çalışma şeklinden detaylı olarak bahsedeceğiz. Yeni projeyi yarattıktan sonra hızlı bir şekilde projenize bir "LINQ2SQL" dosyası ekleyip veritabanından da istediğiniz bir tabloyu kullanabilirsiniz. Tabi tüm bunları ASP.NET tarafında yapıyoruz. Sunucu tarafında DAL (Data Access Layer) olarak LINQ2SQL'imiz hazır olduğuna göre bir sonraki adımda servis katmanını hazırlamamız gerekecek. Yani bir şekilde bu veritabanındaki tabloları Silverlight tarafına aktaracak olan katmanı yaratmalıyız. Bunun için ASP.NET tarafında projenize "Domain Service Class" türünde yeni bir dosya ekleyebilirsiniz.

DomainServiceClass ile sunucudan istemciye bağlantı sağlıyoruz.
DomainServiceClass ile sunucudan istemciye bağlantı sağlıyoruz.

Domain Service Class eklediğiniz anda karşınıza yukarıdaki ekran gelecektir. Burada "Enable Client Access" zaten seçili olacaktır. Eğer bu seçenecek seçili olmaz ise tabi ki Silverlight'ın bu servise ulaşması mümkün olmaz. Sonrasında hemen alt basamakta kullanacağınız DAL'ı seçiyorsunuz. Bizim projemizde bir LINQ2SQL dosyası bulunduğuna göre onu seçebiliriz. Seçtiğiniz veri kaynağına göre Entity'lerin listesi alt tarafa gelecektir. Buradan da hangi Entity'leri istemciye açmak istiyorsanız onu seçebilirsiniz. Eğer bu Entity'ler aracılığı ile veri üzerinde değişiklik yapılacaksa "Enable Editin" checkbox'ını işaretlemeyi de unutmamanızda fayda var. Son olarak en altta yer alan "Generate associated classes for metadata" kısmı ise her sınıfın propertylerine metadata tanımlanabilmesini sağlayacak ek sınıfları yaratacak kodun otomatik olarak yaratılıp yaratılmaması ile ilgili. Bu konuya ileride detaylıca değineceğiz fakat şimdilik bilmemiz gereken şey şu; eğer sunucu taraflı validasyon kodları yaazacaksan ve bu kodların otomatik olarak istemciye de taşınmasını istiyorsak kesinlikle bu seçenek işaretli kalmalı.

[VB]

<EnableClientAccess()>  _

Public Class DomainService1

    Inherits LinqToSqlDomainService(Of DataClasses1DataContext)

 

    Public Function GetInsans() As IQueryable(Of Insan)

        Return Me.Context.Insans

    End Function

 

    Public Sub InsertInsan(ByVal insan As Insan)

        Me.Context.Insans.InsertOnSubmit(insan)

    End Sub

 

    Public Sub UpdateInsan(ByVal currentInsan As Insan)

        Me.Context.Insans.Attach(currentInsan, Me.ChangeSet.GetOriginal(currentInsan))

    End Sub

 

    Public Sub DeleteInsan(ByVal insan As Insan)

        Me.Context.Insans.Attach(insan)

        Me.Context.Insans.DeleteOnSubmit(insan)

    End Sub

End Class

Domain Service Class'ı eklediğiniz gibi projenizde DomainService1.vb/cs adında bir dosya göreceksiniz. Bu dosya içerisinde kodlar yukarıdaki gibi olacaktır. Görüldüğü üzere yaratılan DomainService doğrudan LinqToSqlDomainService'den türetilmiş hatta türetilirken de DAL olarak LINQ2SQL sınıfımızın gösterilmiş. DomainService içerisinde Insert, Update, Delete işlemlerini yapan metodlar ve bu metodlar içerisinde de aslında LINQ2SQL Context'i kullanan kodlar var. GetInsans adındaki metod ise doğrudan SQL'deki tüm insanları döndürüyor.

Bu noktaya kadar yaptığımız şey iki tıklama ile DAL kısmını çözmek sonrasında da servis katmanı için de bir DomainService eklemek oldu. Daha bir satır kod bile yazmadık. Şimdi ilginç bir sürpriz ile karşılaşmak için Silverlight tarafına geçip Page.XAML arkasındaki VB/CS dosyasını açıyoruz.

Sunucu ve istemci arası iletişim.
Sunucu ve istemci arası iletişim.

Yukarıda gördüğünüz manzaradan da anlaşılabileceği üzere bir anda sunucu tarafındaki ASP.NET projemizin namespace'ine istemcide ulaşır hale geldik. Bunun çalışabilmesi için herhangi bir şekilde web servisi eklememiz vs gerekmedi. Aynı şekilde bu namespace altından sunucu tarafındaki DomainService'e de rahatlıkla ulaşabileceğiz hatta sunucu tarafında yapılan değişiklikler de istemci tarafında development zamanında Visual Studio tarafından yansıtılacak. Peki basit bir şekilde sunucudan tüm insanların bilgilerini çekmek istersek ne yapabiliriz? Deneme amaçlı olarak XAML tarafında sayfanıza adı myGrid olan bir Grid yerleştirdikten sonra aşağıdaki kod örneğinden faydalanabiliriz.

[C#]

        void MainPage_Loaded(object sender, RoutedEventArgs e)

        {

            DomainService1 Servis = new DomainService1();

            System.Windows.Ria.Data.LoadOperation Yukleme = Servis.Load(Servis.GetInsansQuery());

            Yukleme.Completed += new EventHandler(Yukleme_Completed);

        }

 

        void Yukleme_Completed(object sender, EventArgs e)

        {

            myGrid.ItemsSource = ((System.Windows.Ria.Data.LoadOperation<Insan>)sender).Entities;

        }

Örneğimizde hemen UserControl load olduğu gibi DomainService1 adında, aslında sunucu tarafındaki yarattığımız DomainService nesnemizden bir kopya alıyoruz. Ne de olsa bu nesne otomatik olarak istemci tarafına taşındı. Not olarak bu nesneyi SilverlightApplication1.Web altında bulabileceğinizi o nedenle using ile söz konusu sınıfı eklemeyi unutmamakta fayda var. Söz konusu DomainService'den bir kopya aldıktan sonra yükleme işlemini başlatmak üzere bir LoadOperation nesnesi tanımlıyoruz. Bu LoadOperation nesnesine de Servis üzerinden bir Load operasyonu yaratım aktarmamız gerekiyor. Bizim zaten tüm insanların bilgisini getirecek olan sunucu tarafında bir sorgumuz vardı. Onu kullanabilmek için doğrudan Servis üzerinden GetInsansQuery dememiz yeterli oluyor. Aslına bakarsanız GetInsansQuery bize sorgulanabilir bir insan listesi tanımı getirmekle mükellef. Bir sonraki adımda biraz daha detaylara gireceğiz.  Yükleme işlemimiz hazır olduğuna göre hemen yüklememizin Completed event'ını da yakalamakta fayda var. Completed event'ının sender'ı malum bizim LoadOperation'ın ta kendisi. Geriye Insan nesnesi döneceğini bildiğimiz için uygun casting işlemini de yapıp hemen operasyon tarafından döndürülen Entities dizisini alıp gridimize bağlayabiliyoruz.

[C#]

        void MainPage_Loaded(object sender, RoutedEventArgs e)

        {

            DomainService1 Servis = new DomainService1();

            System.Windows.Ria.Data.LoadOperation Yukleme = Servis.Load(from inc in Servis.GetInsansQuery()

                                                                        where inc.Adi.Contains("A") select inc);

            Yukleme.Completed += new EventHandler(Yukleme_Completed);

        }

 

        void Yukleme_Completed(object sender, EventArgs e)

        {

            myGrid.ItemsSource = ((System.Windows.Ria.Data.LoadOperation<Insan>)sender).Entities;

        }

RIA Services'ın esas esnek taraflarından biri de doğrudan istemciden istediğimiz sorguyu sunucuya gönderebiliyor olmamız. Tabi bu noktada itiraf etmem gerek ki kullanılabilecek keyword'lerde ADO.NET Data Services mimarisi gereği bazı sınırlar var fakat genel kullanımda pek sorun yaşanacağını sanmıyorum. Eğer çok kompleks sorgular düşünüyorsanız bunları sunucu tarafında ayrı metodlar olarak tanımlamak gerekecektir. Fakat basit bir where sorgusu gibi sorgularınızın çoğunu yukarıdaki şekilde doğrudan istemciden sunucuya gönderebilirsiniz.

Yukarıdaki kod içerisinde değişen tek şey bizim LoadOperation'ın yaratılırken aldığı parametre. Artık sadece bir GetInsansQuery değil de söz konusu Query ile dönen nesne tanımını tekrar sorgulayan bir yapı tanımlıyoruz. Burada aklınıza takılabilecek nokta; "Acaba tüm veriyi istemciye alıp orada mı sorguluyor?" olabilir. Tabi ki hayır! :)

Sorgumuz nereye nasıl gidiyor?
Sorgumuz nereye nasıl gidiyor?

Ekran görüntüsünde inceleyebileceğiniz rapor hazırladığımız Silverlight uygulaması çalıştırıldığında sunucumuza giden istekleri listeliyor. İsteklerin en sonunda ClientBin altında DataService.axd adında bir yere talep gönderildiğini görebiliyoruz. Bu talebin tam yolunu incelersek aslında bizim sorgunun da orada bulunduğunu görebilirsiniz. Bu da şu anlama geliyor; sorgu doğrudan sunucuya URL üzerinden gönderilmiş durumda.

SQL Profile'a baktık, sorgu orada!
SQL Profile'a baktık, sorgu orada!

Aynı şekilde SQL Profile ile SQL'e giden isteklere baktığımızda da bizim taaa :) Silverlight'tan gönderdiğimiz LINQ sorgusunun ASP.NET tarafından da algılanıp bir SQL sorgusuna çevrilerek parametremiz ile SQL'e gönderildiğini görebiliyoruz. Sanırım yeterince başarılı :)

Insert, Delete, Update nasıl yapılır?

.NET RIA Services tarafındaki operasyonların çoğu LINQ2SQL operasyonlarına benziyor. Yeni bir kayıt eklemek için ilk olarak söz konusu kayda ait Entity'den bir kopya alarak veriyi doldurmanız sonrasında da DomainService üzerinden uygun listeye eklemeniz yeterli.

[C#]

            DomainService1 Servis = new DomainService1();

            Servis.Insans.Add(new Insan() { Adi = "Denek", Soyadi = "denek2", Dogum=DateTime.Now, Tip=1 });

            Servis.SubmitChanges();

Son satırda ayrıca SubmitChages metodunu çağırmamız gerekli. Aksi halde yaptığımız değişiklikler sunucu tarafına gönderilmeyecektir. Delete işleminde ise Servis'in bizden istediği silinecek nesnenin bir referansı oluyor. Bu durumda örneğimizdeki Grid'de seçili nesneyi rahatlıkla silinmek üzere aktarabiliriz.

[C#]

        DomainService1 Servis = new DomainService1();

 

        void btnTikla_Click(object sender, RoutedEventArgs e)

        {

            Servis.Insans.Remove((Insan)myGrid.SelectedItem);

            Servis.SubmitChanges();

        }

Kod örneğindeki gibi servis üzerinden Insans listesinden Grid'deki seçili Insan nesnesini kaldırdıktan sonra tekrar SubmitChanges ile gerekli değişikliklerin sunucu tarafına yansıtılmasını sağlıyoruz. Aynı şekilde Entity'ler üzerinde yaptığımız değişiklikler de sunucu tarafına birer Update komutu ile yansıtılacaktır.

[C#]

        DomainService1 Servis = new DomainService1();

 

        void btnTikla_Click(object sender, RoutedEventArgs e)

        {

            Insan Degisecek = (Insan)myGrid.SelectedItem;

            Degisecek.Adi = "Deneme83";

            Servis.SubmitChanges();

        }

Yukarıdaki kod Grid'de seçili nesneyi alıp adını değiştirip değişikliklerin sunucu tarafına gönderilmesi için de SubmitChanges metodunu çağırarak işlemi tamamlıyor. Zaten Grid içerisinde bir nesne aldığımız için bu değişiklik görsel olarak kullanıcıya da anında yansıyacaktır. Bir diğer güzellik ise aslında Grid'imize aktardığımız verinin zaten servisimiz ile sürekli bağlantı içerisinde olması. Yani biz ilk aşamada verimizi Grid'e bağladıktan sonra kullanıcının yaptığı tüm değişiklikleri otomatik olarak sunucuya göndermek istersek aslında sadece servisin SubmitChanges metodunu çağırmamız yeterli olacaktır. Böylece zaten tüm değişikliker sunucuya gönderilecektir.

Önemli Uyarı!

Silverlight ile uygulama geliştirmeyi çok kolaylaştıran bir yapı olarak RIA Services eminim ki sizleri heyecanlandıracaktır fakat unutmamak gerek ki ürün daha yayınlanmış değil ve Preview aşamasında. Çıkacak yeni Preview'larda çok şey değişebilir. O nedenle bu sistem üzerine yapacağınız yatırımlarda dikkatli olup bu risklerin bilincinde olmak önemli.

Hepinize kolay gelsin.