22 Nisan 2012

Windows Phone 7 de Json Veri Formatı ve WCF Servisi


Bir süredir makale yazmaya ara verdikten sonra yeniden windows phone 7 serisi ile makale yazımına devam ediyoruz. Bu makalemizin konusu, merkezi web servisleri ile çalışan mobil uygulamalarımızda hız ya da daha çok performans sağlayacağını düşündüğüm JSON (JavaScript Object Notation) veri formatı üzerinde duracağız. Ayrıca bu makalemizin WCF veri servisleri ve bandwith optimizasyonu ile uğraşanların ilgisini çekeceğini düşünüyorum.. Web servisimizi geliştirirken visual studio wcf service application template'den faydalanacağız.

Windows phone uygulamamızı yazmaya başlamadan önce, web servisimizi hazır durma getirerek, projemizi geliştirmeye başlayalım. Visual studio -> File -> New -> Project -> WCF -> "WCF Service Application" Template ile yeni bir solution oluşturacağız.
























WCF servisini oluşturmak için OK butonuna tıkladığımızda aşağıdaki servis dosyaları otomatik olarak oluşturulmuş olacaktır. IService1 isimli interface ve  Service1.svc.cs sınıfını kullanarak servis tarafındaki kodlarımızı yazmaya başlayabiliriz.






















Servis tarafında hizmet verecek bir de veritabanı hazırlayarak, servisin sunacağı verilerin, bir sqlserver veritabanı dosyasından gelmesini sağlayalım. Projemizin içinde yer alan App_Data klasörüne sağ tıklayarak örnek bir veritabanı hazırlayabiliriz. Veritabanımıza aspnetdb.mdf adını veriyorum..


































Bu örneğimizde SqlServer Express Edition kullanıyorum. aspnetdb.mdf db dosyamıza çift tıklayarak visual studio server explorer penceresi üzerinden db'mize PERSONEL adında küçük (3 kolondan oluşan) bir tablo oluşturarak hızlıca ID, ADI ve SOYADI kolonları oluşturalım. (Ben bu örnekte tablonun içine 168 satırlık, birbirinin tekrarı olan isimler ekledim..)

Şimdi gelelim Personel tablosuna ait bir de Personel.cs isimli bir sınıf oluşturmaya. Verilerin bu sınıfın yardımıyla json ve xml olarak olarak serileştirilmesini isteyeceğiz. Bunun için Personel sınıfını ve sınıfa ait property'leri aşağıdaki gibi yazalım.


C#
namespace MyWcfService
{
    [DataContract]
    public class Personel
    {
        [DataMember]
        public int ID { getset; }
        [DataMember]
        public string ADI { getset; }
        [DataMember]
        public string SOYADI { getset; }
    }
}

Personel.cs sınıfımızı ve sınıfa ait üyelerin DataContract, DataMember attribute'lerini eklemeyi unutmuyoruz, sonra sırada IService1 isimli interface içerisine kod satırlarımızı (kullanacağımız method imzalarını) aşağıdaki gibi ekleyelim. Interface içindeki method imzalarının attribute'lerine dikkat edecek olursak, servisimizde sunacağımız verinin formatını burada belirtiyoruz. Json için WebMessageFormat.Json, Xml için ise WebMessageFormat.Xml'den faydalanacağız.



C#
namespace MyWcfService
{
    [ServiceContract]
    public interface IService1
    {
        [OperationContract]
        [WebGet(UriTemplate = "GetAllPersonel", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
        Personel[] GetAllPersonel();
 
        [OperationContract]
        [WebGet(UriTemplate = "GetAllPersonelWithXML", RequestFormat = WebMessageFormat.Xml, ResponseFormat = WebMessageFormat.Xml)]
        Personel[] GetAllPersonelWithXML();
    }
}


Yukarıdaki interface içinde görüleceği gibi [OperationContract] attribute yanı sıra [WebGet] ile Request ve Response formatlarımızı belirttik.

Kasıtlı olarak 2 farklı method oluşturduk. Aynı veriyi (aynı boyutlardaki veri için) hem JSON hem de XML formatında servisimiz ile sunacağız. Bu örneğimizde Json ile GetAllPersonel, Xml ile de GetAllPersonelWithXML isimli methodları kullandık. Gelin şimdi hep birlikte interface'imizi kullanacağımız Service1.svc.cs dosyamızda iş mantığı kodlarımızı hazırlayalım.


C#
public class Service1 : IService1
{
  public Personel[] GetAllPersonel()
  {
      return getAllPersonelFromDB();
  }
  public Personel[] GetAllPersonelWithXML()
  {
      return getAllPersonelFromDB();
  }
 
  private Personel[] getAllPersonelFromDB() {
    try
    {
       string connStr = ConfigurationManager.ConnectionStrings[0].ConnectionString;
       using (SqlConnection connection = new SqlConnection(connStr))
       {
           using (SqlCommand cmd = new SqlCommand("SELECT * from PERSONEL",connection))
           {
               connection.Open();
               using (var myReader = cmd.ExecuteReader())
               {
                   List<Personel> listOfPersonel = new List<Personel>();
                   while (myReader.Read())
                   {
                       Personel p = new Personel() { 
                           ID = (int)myReader["ID"], 
                           ADI = (string)myReader["ADI"], 
                           SOYADI = (string)myReader["SOYADI"] 
                       };
                       listOfPersonel.Add(p);
                   }
                   myReader.Close();
                   connection.Close();
                   return listOfPersonel.ToArray();
               }   
           }
                   
       }
   }
   catch (Exception)
   {
       throw;
   }
 }
}


Yukarıda yazdığımız C# kodlarını inceleyecek olursak. Veritabanından tüm 
Personel verisini çekmek için, SqlCommand nesnesinin ExecuteReader methodunu 
kullandık. Özellikle daha çok performansa ya da hıza ihtiyaç duyduğumuz 
veritabanı sorgularında DataReader kullanmak sorgu işleminin çok daha hızlı 
çalışmasını sağlayacaktır. Aynı kayıt kümesini SqlDataAdapter kullanarak bir 
DataSet nesnesi içine doldurmaya (Fill) 
çalıştığınızda çok daha fazla zaman 
kaybı yaşanacağı hissedilir ölçüde görülecektir. Dileyen arkadaşlar yukarıdaki 
veri sorgusu için DataAdapter ve 
DataSet kullanarak zaman farkını test edebiilirler. Bu noktada tabiiki 
DataSet 
nesnesinin kullanılmasının yeri ve önemini yadsımıyoruz, fakat DataReader ile yalnızca 
ReadOnly (yalnızca okumak için) veri kümelerinin sorgulanacağı 
durumlarda daha fazla performans kazandıracağı bir gerçektir.

Aşağıdaki resmi inceleyecek olursak DataAdapter/DataSet ve DataReader'ın mimarideki yeri daha net görülebilir.






Dileyen MSDN linkinden DataSet ve DataReader arasındaki farklara daha detaylı göz atabilir. MSDN
Konumuzdan fazla uzaklaşmadan kaldığımız yerden devam edelim. Json veri formatı için web.config dosyamızda da gerekli endpoint konfigürasyonlarını hazırlamamız gerekiyor. Web.config dosyamız aşağıdaki gibi olacaktır.

XML (web.config)
xml version="1.0"?>
<configuration>
  <connectionStrings>
    <add name="SQLDB" 
         connectionString="Data Source=.\SQLEXPRESS;
                           AttachDbFilename=DataDirectory;
                           Integrated Security=True;User Instance=True"/>
  connectionStrings>
  <system.web>
    <compilation debug="true" targetFramework="4.0" />
  system.web>
  <system.serviceModel>
    
    <services>
      <service name="MyWcfService.Service1">
        <endpoint name="jsonEndPoint" 
                  binding="webHttpBinding"  
                  behaviorConfiguration="json" address=""
                  contract="MyWcfService.IService1"/>
      service>
    services>
    
    <behaviors>
      <endpointBehaviors>
        <behavior name="json">
          <webHttp/>
        behavior>
      endpointBehaviors>
    behaviors>
    <serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
  system.serviceModel>
 <system.webServer>
    <modules runAllManagedModulesForAllRequests="true"/>
  system.webServer>
configuration>


Web.config dosyasını da düzenledikten sonra bilgisayarımızda 
internet browser üzerinde servis 
methodlarımızı test edebilir duruma geldik. Servis1.svc dosyasına sağ tıklayıp 
web browser'dan açarak 
üzerinden incelemeye devam edelim.



Internet browser'da servis sayfamız aşağıdaki gibi açılacaktır. URL komut satırında svc'den sonra /GetAllPersonel diyerek json data fırmatlı methodumuzu çağıralım.  (http://localhost:55382/Service1.svc/GetAllPersonel)






























Browser'ın alt kısmında çıkan popup'dan "Aç" diyerek verileri notepad dosyası formatında açabiliriz. Bu arada dikkat edilmesi gereken önemli bir noktayı gözden kaçırmayalım, dosyanın boyutunun 7,08 KB olarak göreceğiz. Dosyayı açtığımızda veri formatının da  [{"ADI":"Gökhan","ID":1,"SOYADI":"Manduz"},... şeklinde json formatında olduğunu görebiliriz.























Benzer şekilde, aynı veri kümesini XML formatında sunan methodumuzu da internet browser üzerinden test ettiğimizde aşağıdaki görüntüyle karşılaşacağız. (Aynı veri kümesini sorguladığımızı unutmayalım.)






























XML formatında açılan verilerin tümünü seçerek bir notepad dosyasına alıyorum. Sonuç aşağıdaki gibi : 11,08 KB
























Bu noktadan sonra, gelelim mobil taraftan servisimizi çağırmaya. Ben Json ile serileştirilen methodu kullanacağım. Dileyen XML kullanabilir. Fakat yukarıda da görüldüğü gibi özellikle mobil platformda Json'ın avantajı yadırganamaz.

Hemen aynı solution üzerinden, solution'a sağ tıklayarak bir windows phone projesi ekleyelim ve mobil client tarafını geliştirmeye başlayalım.

























Servis tarafında oluşturduğum Personel.cs sınıfının bire-bir aynısını, attribute'ler hariç client tarafta çalışacak olan phone projemize de ekliyoruz.

C#
namespace MyPhone7Application
{
    public class Personel
    {
        public int ID { getset; }
        public string ADI { getset; }
        public string SOYADI { getset; }
    }
}


Windows Phone ana ekranımın XAML kodlarınıda hızlıca aşağıdaki gibi hazırlıyorum. (Aşağıda RowDefinition / Height ile belirttiğim 15*, yüzde 15 anlamına gelmektedir.)


XAML
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
    <Grid.RowDefinitions>
        <RowDefinition Height="15*"/>
        <RowDefinition Height="15*"/>
        <RowDefinition Height="70*"/>
    Grid.RowDefinitions>
            
    <Button Grid.Row="0" Content="Json Listele" 
            Name="btnJson" Click="btnJson_Click" />
    <TextBlock Grid.Row="1" Name="txtResult"/>
    <ListBox Grid.Row="2" Name="lstPersonel" />
Grid>




C# tarafında WebClient nesnesi kullanacağız. Servis referansı eklemeye gerek kalmadan WebClient ile json olarak serileştirilmiş veriye asenkron çağrıda bulunacağız ve stream veriyi okuyacağız.  Personel[] nesnemize kavuşmak için DataContractJsonSerializer'dan yardım alarak json formatındaki veriyi tekrar geri serileştirmiş olacağız.(deserializer).

DataContractJsonSerializer nesnesi için System.ServiceModel.Web referansını önceden projemize eklememiz gerekmektedir.




























C#

public partial class MainPage : PhoneApplicationPage
{
   private DateTime _startTime;
   private DateTime _endTime;
   public MainPage()
   {
       InitializeComponent();
   }
   private void client_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
   {
       if (e.Error == null) {
           DataContractJsonSerializer serializer = 
               new DataContractJsonSerializer(typeof(ObservableCollection<Personel>));
 
           ObservableCollection<Personel> personelList = 
               (ObservableCollection<Personel>)serializer.ReadObject(e.Result);
 
           lstPersonel.Items.Clear();
           foreach (var item in personelList)
           {
               lstPersonel.Items.Add(String.Format("Id: {0} ,  {1} {2}",item.ID, 
                                                                        item.ADI, 
                                                                        item.SOYADI));
           }
           _endTime = DateTime.Now;
 
           TimeSpan interval = _endTime - _startTime;
           txtResult.Text = String.Format("Sorgulama {0} mili saniye sürdü.", interval.Milliseconds);
       }
   }
   private void btnJson_Click(object sender, RoutedEventArgs e)
   {
       WebClient clientJson = new WebClient();
       Uri uri = 
           new Uri("http://localhost/jsonwebtest/Service1.svc/GetAllPersonel");

       clientJson.OpenReadCompleted += client_OpenReadCompleted;
       _startTime = DateTime.Now;
       clientJson.OpenReadAsync(uri);
   }
}
 
Artık keyifle sonucu görmenin zamanı geldi. Uygulamayı windows phone emulatör'de F5 ile başlatarak test edelim, Uygulama emulator'de başladığında "Json Listele" butonuna tıklayarak sonucu görelim. Sorgu sonucu için geçen sürede tabiiki farklılıklar (kullanılan bilgisayarın da hızına bağlı olarak) olacaktır. Kendi kişisel bilgisayarımda 44 milisaniyeyi gördüm :)
Hızlı uygulamalar yazmanız dileklerimle, makalemizi burada sonlandırıyoruz. Özellikle bandwith optimizasyonu ile uğraşanlar için de json veri formatının oldukça faydalı bir çözüm olacağı kanısındayım.

Windows Phone Emulator










































Kolay gelsin.

Gökhan Manduz
http://gokhanmanduz.blogspot.com/

Hiç yorum yok: