Burak Selim Senyurt(MVP)
Matematik Mühendisi bir .NET Severin Yazıları...

Microsoft Teknoloji Günleri Akşam Sınıfı – Gün 3 – WCF ile Servis Yaklaşımı Eğitimi Tamamlandı

Çarşamba, 21 Temmuz 2010 02:45 by bsenyurt

IMG_3578

Merhaba Arkadaşlar,

25 Mayıs 2010 Tarihinde C# 4.0 ile birlikte gelen yenilikleri anlatarak başladığımız Microsoft Teknoloji Günleri Akşam Sınıfının üçüncü gününü de kazasız belasız tamamladık. Öncelikli olarak tüm katılımcılarımızın ayağına sağlık.

Eğitimin video kayıtlarının alınması noktasında yardımcı olan Mustafa Demirhan’a, kameranın kapasitesi dolunca yardıma koşarak kendi kamerası ile çekim yapan Microsoft Student Partner(MSP) adayı Burak Özkan’a, şu anda okumakta olduğunuz blog yazısındaki resimler gibi kaliteli, yüksek çözünürlüklü sayısız fotoğraf çekerek bizle paylaşan Tuba Çebi’ ye, Akşam Sınıfı fikrinin olgunlaşmasında en büyük yardımcım olan ve özellikle katılımcıları aşağıdaki resimde olduğu gibi hediyeler ile mutlu etmemizi sağlayan Buket Şerefli’ ye(Microsoft Türkiye İş Ortağı Yöneticisi) canı gönülden teşekkür etmek istiyorum.

IMG_3582

Buket Şerefli tarafından temin edilen hediyeler arasında bulunan Yeşil Vista Logolu Havlu’ sunu, kazanan değerli katılımcımıza hediye ederken.

IMG_3761

Microsoft Certified Partner logolu duvar saatlerinden birisini, kazanan değerli katılımcımıza hediye ederken.

Eğitimde çok güzel bir sürprizi de ben yaşadım. Imagine Cup Türkiye birincisi olan ve ülkemizi Polonya’ da düzenlenen finallerde başarılı bir şekilde temsil eden ekipten iki arkadaşımız da aramızdaydı.

IMG_3547

Imagine Cup Türkiye birincisi olan ekipten İbrahim Kıvanç(Imagine Cup 2010 Polonya Dünya Finalleri Ardından) ve  Yasemin Çelik, yarıştıkları BabyRC projesi hakkında kısaca bilgi verirken. (Ekibin bu akşamki eğitime katılamayan diğer üyeleri ise Burak Kanmaz ve Fatih Coşkun’ dur)

Eğitimimiz boyunca öncelikli olarak Service Oriented Architecture(SOA) kavramını kısaca tanımaya/anlamaya çalıştık. Sonrasında ise Microsoft’ un uzun süredir vizyonda olan SOA implemantasyonu WCF’ e(Windows Communication Foundation) giriş yaptık. WCF üzerinden uygulanabilen SOA desenelerini görüp, mimarisini ve çalışma zamanını kavradık. WCF’ in ABC’ sini de(AddressBindingContract) işin içerisine katmayı unutmadık elbette. Son olarak basit bir Hello World uygulaması geliştirip, WCF 4.0 ile birlikte gelen bazı yenilikleri(Discovery, Routing, Simplified Configurations) örnek demolar üzerinden anlamaya çalıştık.

Yaklaşık olarak 3 saatten fazla bir zamanımızı Microsoft İstanbul Ofisi Jupiter 1 salonunda geçirdik. Tabi akşam saati ve bir sonraki gününde mesai olması nedeniyle, özellikle uzakta oturan veya çok erken saatte işe giden arkadaşlarımızın bir kısmı mecburen erken çıkmak zorunda kaldılar. Ancak üzülmesinler nitekim bir şey kaçırmadılar. Tüm eğitimin video kaydını gerçekleştirdik. En kısa sürede render işlemini tamamlayıp Nedirtv?Com topluluğu üzerinden sizlerle paylaşıyor olacağım.

Bunlara ek olarak bir önceki eğitimimizde vaat ettiğimiz Pro .Net 4.0 Parallel Programming kitabını da, kazanan arkadaşımıza hediye ettik.

Bir sonraki eğitimimizin konusu WCF Eco System. 20 Ağustos 2010 Cuma günü yapılacak bu eğitimi kaçırmamanızı öneririm. Eğitimin duyurusu ve kayıt linki 15 Ağustos’ tan sonra duyuruluyor olacaktır.

Eğitimde Kullanılan Örnekler : TeknolojiGunleri_WCF.rar (304,80 kb)

Eğitime ait Powerpoint Sunumu : WCF 4.0 - Introduction - Microsoft.pptx (483,87 kb)

Duplex Service için Silverlight İstemcisi Geliştirmek

Pazartesi, 5 Temmuz 2010 10:00 by bsenyurt

Merhaba Arkadaşlar,

Hatırlayacağınız üzere bir önceki yazımızda Silverlight istemcilerinin kullanabileceği Duplex WCF Service uygulamalarının nasıl yazılabileceğini incelemeye çalışmıştık. Çok doğal olarak bu işin bir de istemci tarafı bulunmaktadır. İşte bu yazımızda söz konusu istemciyi geliştirmeye çalışacak ve bir önceki yazının yorgunluğunu üzerimizden atarcasına, basit bir şekilde ilerliyor olacağız. İlk olarak Visual Studio 2010 Ultimate RC ortamında Silverlight 4.0 tabanlı bir uygulama oluşturarak işe başlayabiliriz. Bu işlemin ardından Proxy tabanlı bir WCF servis kullanımı için Add Service Reference seçeneğine başvurmamız gerekecektir. Yine hatırlayacağınız üzere geliştirdiğimiz WorldWeatherService isimli servisi IIS üzerine Publish etmiştik. Bu sebepten ilgili servis referansına aşağıdaki şekilden de görüldüğü üzere http://localhost/WorldWeatherService/WeatherDuplexService.svc adresinden erişebiliriz.

İstemci tarafında çok basit olarak aşağıdaki XAML içeriğine sahip bir kontrol kullanıyor olacağız. Buna göre istemciler bir şehir adı girerek sunucudan anlık hava durumu bilgilerini alabilecekleri bir arayüze sahip olacaklar. Aslında alacaklar demek çok doğru bir tabir değil. Nitekim servisin kendisi, bağlı olan istemci üzerinde tetiklediği bir operasyona bu bilgileri parametre şeklinde gönderiyor olacak.

<UserControl x:Class="WeatherClientApp.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="230" d:DesignWidth="479">

    <Grid x:Name="LayoutRoot" Background="White">
        <Button Content="Start" Height="23" HorizontalAlignment="Left" Margin="207,27,0,0" Name="StartButton" VerticalAlignment="Top" Width="75" Click="StartButton_Click" />
        <TextBox Height="23" HorizontalAlignment="Left" Margin="25,27,0,0" Name="CityTextBox" VerticalAlignment="Top" Width="165" />
        <ListBox Height="142" HorizontalAlignment="Left" Margin="24,64,0,0" Name="WeatherStatusListBox" VerticalAlignment="Top" Width="434" />
    </Grid>
</UserControl>

İstemci tarafı kodlarına gelince;

using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.Windows;
using System.Windows.Controls;
using WeatherClientApp.WeatherServiceReference;

namespace WeatherClientApp
{
    public partial class MainPage : UserControl
    {
        WeatherDuplexServiceClient proxy = null;

        public MainPage()
        {
            InitializeComponent();

            EndpointAddress address = new EndpointAddress("http://localhost/WorldWeatherService/WeatherDuplexService.svc");
            PollingDuplexHttpBinding binding = new PollingDuplexHttpBinding(PollingDuplexMode.MultipleMessagesPerPoll);
            proxy = new WeatherDuplexServiceClient(binding, address);

            proxy.SetCityCompleted += new EventHandler<System.ComponentModel.AsyncCompletedEventArgs>(proxy_SetCityCompleted);
            proxy.NoticeReceived += new EventHandler<NoticeReceivedEventArgs>(proxy_NoticeReceived);
        }

        void proxy_NoticeReceived(object sender, NoticeReceivedEventArgs e)
        {
            WeatherStatus wStatus = e.weather;
            WeatherStatusListBox.Items.Add(String.Format("{0}({1} C){2}", wStatus.City, wStatus.Heat, wStatus.Summary));
        }

        void proxy_SetCityCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
        {
            WeatherStatusListBox.Items.Add("SetCity çağrısı tamamlandı");
        }

        private void StartButton_Click(object sender, RoutedEventArgs e)
        {
            WeatherStatusListBox.Items.Clear();
            proxy.SetCityAsync(CityTextBox.Text);
        }
    }

}

Dikkat edileceği üzere WeatherDuplexServiceClient tipinden olan proxy nesnesi örneklenirken iki önemli parametre bilgisi geçilmektedir. Bunlardan ilki PollingDuplexHttpBinding tipinden olan bağlayıcı tiptir(Binding Type). Diğeri ise servise erişilecek olan Endpoint adresidir. İstemci tarafı asenkron olarak SetCity metoduna erişebilir. Bu nedenle SetCityCompleted olay metodu yüklenmiştir. Dikkat çekici noktalardan birisi de NoticeReceived isimli bir olayın söz konusu olmasıdır. Bilinen Completed son eki yerine Received son ekinin gelmesinin de bir anlamı vardır elbette. Wink Bu, istemcinin servisten gelen Notice çağrısını takiben devreye girecek operasyon ile alakalıdır. Bir başka deyişle servis tarafı Notice metodunu çağırdıktan ve bu operasyon işleyişini tamamladıktan sonra istemci tarafında proxy_NoticeReceived olay metodu devreye girecektir. Ayrıca, bu olay metodunun NoticeReceivedEventArgs tipinden olan parametresi üzerinden yakalanan weather özelliği yardımıyla, servisin gönderdiği WeatherStatus nesnesine ulaşılabilir. Sonuç olarak uygulamayı test ettiğimizde örnek olarak aşağıdakine benzer bir sonuç elde ettiğimizi görebiliriz.

Üç sonuç gelmesi tamamen servis tarafındaki zamanlama ayarları ile alakalı bir durumdur. Bu sürelerde oynayarak servisin istemci tarafına kaç kere bildirimde bulunacağını da ayarlayabilirsiniz. Önemli olan nokta servisin istemci üzerinde bir operasyon tetiklemesidir. Bunu yazdığımız istemci ile test etmiş olduk.

Tüm bu çalışma sırasında dikkat edilmesi gereken bir husus da, önceki yazımızda da değinmiş olduğumuz Client Access Policy kullanımıdır. Eğer IIS root klasörü altında ClientAccessPolicy.xml dosyası ve gerekli içeriği olmassa çalışma zamanında aşağıdaki hata mesajı ile karşılaşılacaktır.

Oysaki geliştirdiğimiz örnek Asp.Net Development Server üzerinden yayınlanmaktadır(http://localhost:22334/WeatherClientAppTestPage.aspx) ve sorunsuz bir şekilde IIS üzerindeki WorldWeatherService uygulamasına erişebilmektedir. Dolayısıyla Silverlight uygulamalarında sıkça rastladığımız Cross Domain sorunu yaşanmamaktadır. Böylece geldik bir yazımızın daha sonuna. Tekrardan görüşünceye dek hepinize mutlu günler dilerim.

WeatherClientApp.rar (540,12 kb) [Örnek Visual Studio 2010 RC sürümü üzerinde geliştirilmiş ve test edilmiştir]

Silverlight İstemcileri için Duplex Service Geliştirmek

Cuma, 18 Haziran 2010 13:50 by bsenyurt

Merhaba Arkadaşlar,

Lost dizisinin müptelası olan arkadaşlar "Push The Button" repliğini bilirler. Hikayeye göre DHARMA girişimin labaratuvarında yer alan ve 108 dakikadan geriye doğru sayan bir numarator vardır. Zaman sayacı sıfırlanmadan önce toplamları 108 olan 4,8,15,16,23,42 sayı dizisinin klavyeden girilmesi ve Enter tuşuna basılması gerekmektedir. Tabi ben Lost dizisinin tüm sezonlarını takip etmemiş ve hatta sonunu getirememiş birisi olarak ne olduğunu tam olarak anlayabilmiş değilim. Lakin bu Push The Button mevzusunda düşündüğüm genelde, sunucu üzerindeki bir servisin Pusher Service olarak hizmet vermesi olmuştur. Buna göre, servis kendisine bağlı olan istemci modundaki lokasyonlara bir bildiri yapmaktadır diyerek konuyu bir şekilde bağlmaya çalışayım. Bu günkü yazımızda Silverlight istemcilerinin Duplex iletişim üzerinden hizmet verebilen servisler yardımıyla nasıl tetiklenebileceğini incelemeye çalışıyor olacağız. Sanıyorum Siverlight tabanlı chat programları geliştirmek isteyenlerin ilgisini çekecek en azından biraz bilgi verecek bir yazı olacaktır.

Bilindiği üzere WCF(Windows Communication Foundation) tarafında geliştirilen servislerin Duplex iletişimi kullanaraktan istemciler üzerinde operasyonlar gerçekleştirmesi, bir başka deyişle metod çağrılarında bulunabilmeleri mümkündür. Burada çift kanallı olarak gerçekleştirilen bir iletişim söz konusudur. Daha çok chat uygulamalarında veya istemcinin her hangibir durum değişikliğinde uyarılması gerektiği vakalarda bu tip servislerden yararlanılabilir. Söz gelimi bu yazımızda geliştireceğimiz WCF Servis örneği, istemcilerden aldığı şehir bilgisine göre anlık hava durumu bilgisini döndürecektir. Bu cümle ilk bakışta istemcinin yapacağı normal bir servis çağrısından ve sonucunun alınmasından farksız bir operasyonmuş gibi görünebilir. Ancak gözden kaçırılmaması gereken bir husus vardır; o da hava durumu bilgisinin bildirilme işleminin, servis tarafından istemci üzerindeki bir operasyon çağrısı ile yapılacağıdır.

Tabi yazımızın başlığından da anlayacağınız üzere söz konusu WCF Servisini bir Silverlight istemcisi üzerinden test etmeye çalışıyor olacağız. Duplex WCF Servisinin geliştirilmesi başlı başına karmaşık bir süreç gerektiğinden yazımızı iki seriye bölüyor olacağız. İlk bölümdeki hedefimiz Duplex iletişimi sağlayacak olan WCF Servisini geliştirmek olacak. İşe Visual Studio 2010 Ultimate RC ortamında WorldWeatherService isminde bir WCF Service Application uygulaması açarak ve hemen C:\Program Files (x86)\Microsoft SDKs\Silverlight\v4.0\Libraries\Server adresinde yer alan System.ServiceModel.PollingDuplex.dll assembly' ını referans ederek başlayabiliriz. Nitekim bu referans içerisindeki tiplere sunucu tarafında ihtiyacımız olacaktır.

Şimdi servis için gerekli sözleşmeleri yazabiliriz. WCF Duplex servisleri iki sözleşme(Contract) içermektedir. Bunlardan birisi istemci tarafından çağırılacak olan operasyonları içeren sözleşmedir. Ancak diğer sözleşme geri bildirim(Callback) sırasında kullanılacak sözleşmedir. Aşağıda söz konusu sözleşmelere ait arayüz(Interface) içerikleri yer almaktadır.

using System.ServiceModel;

namespace WorldWeatherService
{
    // Servis sözleşmesinin kullanacağı geri bildirim sözleşmesi CallbackContract özelliği ile bildirilir.
    [ServiceContract(CallbackContract=typeof(IWeatherDuplexClient))]
    public interface IWeatherDuplexService
    {
        [OperationContract]
        void SetCity(string cityName);
    }

    [ServiceContract]
    public interface IWeatherDuplexClient
    {
        // Servisin istemci tarafında tetikleyeceği Notice operasyonunun geriye bir şey döndürmeyeceği bir başka deyişle tek yönlü çalışan bir metod olduğu IsOneWay niteliği sayesinde bildirilir.
        [OperationContract(IsOneWay=true)]
        void Notice(WeatherStatus weather);
    }
}

Dikkat edileceği üzere IWeatherDuplexService sözleşmesi için kullanılan ServiceContract niteliğinde, geri bildirim sözleşmesi CallbackContract özelliği yardımıyla bildirilmiştir. Bu sayede servis sözleşmesini uygulayan tip, çalışma zamanında gerekli geri bildirim nesne örneğini değerlendirebilecektir. Diğer yandan servisin istemci üzerinde tetiklemede bulunacağı sözleşme, IWeatherDuplexClient adlı interface tipi olarak tanımlanmıştır. Bu sözleşme için önem arz eden konu ise Notice metodunun OperationContract niteliğinde yer alan IsOneWay özelliğinin true değere sahip olmasıdır. Nitekim servisin yaptığı istemci bazlı geri bildirimlerden bir sonuç beklenmemelidir. Bu da ilgili operasyonun tek yönlü çalışacak şekilde işaretlenmesi ile mümkün olacaktır. Gelelim servis sözleşmesinin uygulandığı tipe.

using System;
using System.ServiceModel;
using System.Threading;

namespace WorldWeatherService
{
    public class WeatherDuplexService
        : IWeatherDuplexService
    {
        #region Variables

        IWeatherDuplexClient client;
        string[] posibilities ={"Güneşli",
                                  "Yağmurlu",
                                  "Parçalı Bulutlu",
                                  "Kar Yağışlı",
                                  "Tipi",
                                  "Fırtına",
                                  "Bulutlu",
                                  "Rüzgarlı",
                                  "Zaman zaman yağışlı"
                              };

        #endregion

        #region IWeatherDuplexService Members

        public void SetCity(string cityName)
        {
            // İstemci kanalı yakalanıyor ki geri bildirim sırasında hangi kanal üzerinden gidileceği bilinsin.
            client = OperationContext.Current.GetCallbackChannel<IWeatherDuplexClient>();
            // Geri bildirimlerde devreye girecek metod bloğunu işaret edecek TimerCallback tipinden bir temsilci kullanılır.

            Random rnd = new Random();
            TimerCallback tCallback = o => {
                // Sembolik olarak bir hava durumu bilgisi oluşturulur               
                string summary=posibilities[rnd.Next(0,posibilities.Length-1)];
                int heat = rnd.Next(0, 30);

                // İstemci tarafındaki Notice metodu çağırılır ve söz konusu şehir için anlık hava durumu bilgisi gönderilir(Tabiki hayali olarak)
                client.Notice(new WeatherStatus { City = cityName, Heat = heat, Summary = summary });
            };
            // Olayı simule etmek için,
            // Belirli süre duraksatma yapılır ve sonunda tCallback isimli temsilcinin işaret ettiği metod bloğunun çalıştırılması sağlanır.
            using (Timer tmr = new Timer(tCallback, null, 500, 500))
            {
                Thread.Sleep(2000);
            }
        }

        #endregion
    }
}

WeatherDuplexService sınıfı IWeatherDuplexService arayüzünü(Interfaca) uygulamaktadır. Bu uyarlamaya göre SetCity metodunu ezmektedir. SetCity metodu içerisinde en can alıcı nokta ise, Callback Channel nesne referansının yakalanmasıdır. Dikkat edileceği üzere GetCallbackChannel metodu generic olarak IWeatherDuplexClient arayüzünü kullanmaktadır. Buna göre, çalışma zamanında istemcinin servise gönderdiği çağrıya göre yakalayacağı kanalın, IWeatherDuplexClient arayüzünü uygulamış bir tip olacağı ortadadır. E haliyle bu arayüzün içerisinde tanımlanmış bir de operasyonumuz bulunmaktadır. Notice isimli metod.Wink Dolayısıyla yakalanan kanal üzerinden yapılabilecek olan bir Notice operasyon çağrısı mevcuttur ve bu çağrı servis tarafından istemci üzerinde gerçekleştirilecektir. Kod içerisinde sembolik olarak bir gecikme işlemi uygulanmış ve bunun sonucunda TimerCallback temsilci tipinin(Delegate) işaret ettiği bir metod gövdesinin de devreye girmesi sağlanmıştır. Bu metod bloğu içerisindeyse Notice metod çağrısı gerçekleştirilmekte ve aşağıdaki içeriğe sahip olan WeatherStatus tipinden bir nesne örneği üretilmektedir.

namespace WorldWeatherService
{
    public class WeatherStatus
    {
        public int Heat { get; set; }
        public string Summary { get; set; }
        public string City { get; set; }
    }
}

Sırada servis tarafının çalışma zamanını ilgilendiren konfigurasyon ayarlarının yapılması yer almakta. Burada işler biraz karışıyor. Sealed Neyseki MSDN üzerinden konu ile ilişkili yardımcı dökümanların fazlasıyla yararı olduğunu ifade edebilirim. İşte sunucu uygulamamıza ait web.config içeriğimiz.

<?xml version="1.0"?>
<configuration>
    <system.serviceModel>
        <extensions>
            <bindingExtensions>
                <add name="duplexHttpBinding" type="System.ServiceModel.Configuration.PollingDuplexHttpBindingCollectionElement, System.ServiceModel.PollingDuplex, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
            </bindingExtensions>
        </extensions>
        <bindings>
            <duplexHttpBinding>
                <binding name="duplexHttpBindingConfiguration" duplexMode="MultipleMessagesPerPoll" maxOutputDelay="00:00:05"/>
            </duplexHttpBinding>
        </bindings>
        <behaviors>
            <serviceBehaviors>
                <behavior name="">
                    <serviceMetadata httpGetEnabled="true"/>
                    <serviceDebug includeExceptionDetailInFaults="false"/>
                </behavior>
            </serviceBehaviors>
        </behaviors>
        <serviceHostingEnvironment multipleSiteBindingsEnabled="true"/>
        <services>
            <service name="WorldWeatherService.WeatherDuplexService">
                <endpoint address="" binding="duplexHttpBinding" bindingConfiguration="duplexHttpBindingConfiguration" contract="WorldWeatherService.IWeatherDuplexService"/>
                <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
            </service>
        </services>
    </system.serviceModel>
    <system.web>
        <compilation debug="true"/>
  </system.web>
</configuration>

Anneciğimmm!!! Sakın korkmayın. Ezberlemeye gerek yok. Ancak bir özet geçmemizde yarar olduğu kanısındayım. Öncelikli olarak Silverlight tarafı için gerekli bir takım işlemler yapıldığını söyleyebiliriz. Bunlardan ilki bir bindingExtension bildirimidir. Sanırım projeyi oluşturduktan sonra neden System.ServiceModel.PollingDuplex.dll assembly' ını referans ettiğimizi anlamışsınızdır. Söz konusu extension ile yeni bir bağlayıcı tip(Binding Type) tanımlayarak kullanıma sunuyoruz. duplexHttpBinding olarak isimlendirdiğimiz bağlayıcı tipin bir takım özellikleri de(duplexMode, maxOutputDelay) belirtilmiş durumdadır.

Peki ya bundan sonrası? Örneğimizi Asp.Net Development Server yerine IIS altında konuşlandıracak şekilde tesis edebiliriz. Aslında projeyi doğrudan IIS altına Publish ettikten sonra servisi bir tarayıcı uygulama ile açarak sorunsuz bir şekilde çağırılıp çağırılmadığını görmekte yarar olacağı kanısındayım. Publish seçenekleri aşağıdakine resimde görüldüğü gibi yapılabilir.

Örneğimizi test ettiğimiz aşağıdaki gibi bir sonuç ile karşılaşırsak her şey yoldundadır diyebiliriz. Diyebiliriz çünkü asıl test Silverlight istemcisi tarafından servise erişmeye ve kullanmaya çalıştığımızda oluşacaktır.

Servis tarafında dikkat edilmesi gereken noktalardan birisi de Client Access Policy konusudur. Servisimizi IIS altına Publish etsek bile herhangibir Silverlight istemcisinin kullanabilmesi için ClientAccessPolicy.xml içeriğinin Domain Root altında yer alması gerekmektedir. Söz konusu dosyasnın içeriğini etkileyen pek çok faktör vardır ve açıkçası bu kadar detaya girmemize şimdilik gerek yoktur. Ancak en geçerli kaynaklardan birisi olarak Time Heuer' in blog girdisinden ve tabiki Microsoft' un Network Security Access Restricions in Silverlight yazısından yararlanabilirsiniz. Örneğimiz için aşağıdaki gibi bir içerik yeterli olacaktır.

<?xml version="1.0" encoding ="utf-8"?>
<access-policy>
  <cross-domain-access>
    <policy>
      <allow-from>
        <domain uri="*" />
      </allow-from>
      <grant-to>
        <resource path="/WorldWeatherService" include-subpaths="true"/>
      </grant-to>
    </policy>
  </cross-domain-access>
</access-policy>

Bu içeriğe sahip olan ClientAccessPolicy.xml dosyasının ise WCF Servisimizi Publish ettiğimiz IIS sunucusundaki ilgili Domain' e ait root klasörde yer alması gerekmektedir. Aşağıdaki şekilde görüldüğü gibi.

Artık farklı bir domainde yer alan herhangibir Silverlight istemcisi WorldWeatherService' ini kullanabilecektir. Artık geride istemci tarafının yazılması ve test edilmesinden başka bir şey kalmamıştır. Ancak biraz nefes alalım ve bunu bir sonraki yazımıza bırakalım. Tekrardan görüşünceye dek hepinize mutlu günler dilerim.

WorldWeatherService.rar (157,92 kb) [Örnek Visual Studio 2010 Ultimate RC sürümü üzerinde geliştirilmiş ve test edilmiştir.]

WCF Service' lerine Silverlight İstemcilerinden Channel Bazlı Erişim

Çarşamba, 2 Haziran 2010 10:55 by bsenyurt

Merhaba Arkadaşlar,

Bir zamanlar(aslında çok uzun zaman olmadı ayrılalı) özel bir eğitim firmasında Yazılım Eğitmeni olarak görev yapmaktaydım. Freelance olarak başladığım ilk dönemlerde kurumda en çok dikkatimi çeken nokta, şirketin gece koruyuculuğunu yapan köpekleri olmuştu. Aslında herkes yandaki resimde görülen ki kadar etkili ve caydırıcı olmasını bekleyebilir ancak son derece sakin ve kendi halinde sevimli bir köpekti. Bilişim sektöründe sistem eğitimleri de veren bu şirketin, geceleri koruma görevi ile barındırdığı köpeğinin adı ise, şirketin yaptığı işle ilintili olarak Proxy olarak verlimişti. Bilenler bilir...Neredeyse gıkı bile(pardon havı bile) çıkmayan bu sevimli köpeğin yersiz serzenişte bulunmamasının da bir nedeni vardı elbette. Proxy...Gelen veriyi süzüp ona göre aksiyon veriyordu çünküLaughing Şaka bir yana tesadüfe bakın ki bu günkü konumuzda Proxy kavramı ile alakalı.

WCF Servislerinin herhangibir istemci uygulama tarafından kullanılmasını sağlamak için tercih ettiğimiz yollardan birisi de Proxy tiplerinden faydalanmaktır. Genellikle Add Service Reference veya Svcutil.exe ya da SlSvcUtil.exe(Silverlight versiyonu) gibi araçlar yardımıyla Proxy üretimi kolayca gerçekleştirilebilir. Proxy tipleri, servislere erişilmesi sırasında istemci tarafında yazılan kodu hafifletmekle kalmaz aynı zamanda çalışma zamanının ayağa kaldırılması gereken ya da iletişim sırasında oluşturulması gereken pek çok nesnenin iş yükünü de üzerine alır. Ancak bazı durumlarda istemci tarafında Proxy tipi kullanımı yerine, servis ile olan iletişimde gerekli olan kanal(Channel) yapısının manuel kod ile oluşturulması ve diğer hazırlıkların yapılarak iletişim kurulması istenebilir.

Bu istek özellikle servis tarafındaki özel serileştirilebilir tiplerin(Serializable Types) çeşitli yardımcı fonksiyonelliklere sahip olduğu durumlarda önem kazanmaktadır. Örneğin sunucu tarafındaki bu tiplerde doğrulama işlemleri(Validation) için yazılmış bazı özel fonksiyonlar yer alabilir ve bunların istemci tarafında üretilen tiplere de alınması istenebilir. Böylece bu fonksiyonelliklerin içerdiği bazı iş kurallarının istemci tarafında da yüklenilmesi istenebilir ki normal Proxy üretiminde bu metodların istemci tarafına taşınmadığı bilinmektedir. İşte bu teoriden yola çıkarak yazdığımız bu blog girdimizde, söz konusu durumun Silverlight uygulamalarında nasıl çözümlenebileceğini incelemeye çalışıyor olacağız. (Aslında çok eskiden .Net Remoting ile uğraşmış birisi olarak, dağıtık uygulama geliştirirken servis tarafında çalışan bileşenlerin, istemci tarafına da referans edildikleri bir yöntem olduğunu ifade edebilirim)

Not : Silverlight tarafında Proxy tabanlı olarak WCF servislerinin nasıl kullanılabileceğini Screencast - Silverlight Enabled WCF Services görsel dersinden izleyebilirsiniz.

Dilerseniz vakit kaybetmeden örneğimize başlayalım. Visual Studio 2010 RC sürümünde oluşturduğumuz Silverlight 4.0 uygulamamızın içerisinde aşağıdaki IAlbumProducer isimli arayüzün(Interface) olduğunu düşünelim.

using System.Runtime.Serialization;
using System.ServiceModel;

namespace WithChannelBased.Web
{
    [ServiceContract]
    public interface IAlbumProducer
    {
        // Silverlight istemcilerin asenkron çağrı yapmalarını zorlamak için aşağıdaki ön işlemci direktifi(Pre Proccesor Directive) eklenmiştir.
#if SILVERLIGHT
       
        [OperationContract(AsyncPattern=true)]
        IAsyncResult BeginGetAlbum(int albumId, AsyncCallback callback, object state);

        Album EndGetAlbum(IAsyncResult result);
       
#else

        [OperationContract]
        Album GetAlbum(int albumId);

#endif
    }

    [DataContract]
    public class Album
    {
        [DataMember]
        public int Id { get; set; }
        [DataMember]
        public string Title { get; set; }
        [DataMember]
        public string Genre { get; set; }
    }
}

Dikkat edileceği üzere Interface tipi aslında bir servis sözleşmesi(Service Contract) tanımlamaktadır. Bu sözleşmenin Siverlight istemcilerinde asenkron çağrıları zorunlu kılması içinse bir ön işlemci direktifi kullanılmıştır. Yanlız bu ön işlemci direktifinin büyük harfler ile yazılması önemlidir. Peki neden böyle bir gereksinimimiz olmuştur?

Öncelikli olarak sunucu tarafında BeginGetAlbum ve EndGetAlbum metodlarının, arayüz zorlaması nedeniyle servis sınıfı içerisinde uygulanması istenmemektedir. Diğer yandan aynı dosyayı istemci tarafına alıyor olacağız ki bu durumda Silverlight istemcisinin asnekron çağrılar için söz konusu BeginGetAlbum ve EndGetAlbum metodlarını da kullanabilmesi gerekmektedir. İşte bu sebepten bir ön işlemci direktifi kullanılması tercih edilmiştir.

Diğer yandan sözleşmenin bulunduğu dosya içerisinde Album isimli serileştirilebilir bir tipin de yer aldığı görülmektedir. Album sınıfı bir veri sözleşmesi(Data Contract) şeklinde tanımlanmıştır. Önemli olan noktalardan birisi servis sözleşmesi ve veri sözleşmesinin aynı fiziki dosya içerisinde tutulmuş olmalarıdır. Nitekim bu dosya Silverlight uygulamasına doğrudan link olarak bağlanacaktır. Servis sözleşmesini uygulayacak tipin aslında Silverlight destekli bir WCF Servisi olduğu düşünüldüğünde projeye Silverlight-enabled WCF Service şablonunda AlbumProducer isimli bir öğenin eklenmesi web.config dosyası içerisinde bazı ön hazırlıkların yapılmasını sağlayacaktır. Elbette eklenen bu tipin yukarıda tanımlı olan servis sözleşmesini uygulaması gerekmektedir. Aşağıdaki kod parçasında görüldüğü gibi;

using System.Runtime.Serialization;
using System.ServiceModel;

namespace WithChannelBased.Web
{
    public class AlbumProducer
        :IAlbumProducer
    {
        #region IAlbumProducer Members

        public Album GetAlbum(int albumId)
        {
            return new Album { Id = 1000, Title = "Benim Şarkılarım", Genre="Türkçe Pop" };
        }

        #endregion
    }
}

GetAlbum metodunun ne yaptığı çok fazla önemli değildir. Sadece test amacıyla kullanacağımız bir içerik döndürmektedir. Bu işlemlerin ardından servisin bulunduğu sunucu tarafındaki web.config dosyasının içeriğinde bazı düzenlemeler yapılması gerekmektedir.

<?xml version="1.0"?>
<configuration>
    <system.web>
        <compilation debug="true" targetFramework="4.0" />
    </system.web>
    <system.serviceModel>
        <behaviors>
            <serviceBehaviors>
                <behavior name="">
                    <serviceMetadata httpGetEnabled="true" />
                    <serviceDebug includeExceptionDetailInFaults="false" />
                </behavior>
            </serviceBehaviors>
        </behaviors>
        <bindings>
            <customBinding>
                <binding name="WithChannelBased.Web.AlbumProducer.customBinding0">
                    <binaryMessageEncoding />
                    <httpTransport />
                </binding>
            </customBinding>
        </bindings>
        <!--<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />-->
        <services>
            <service name="WithChannelBased.Web.AlbumProducer">
                <endpoint address="" binding="customBinding" bindingConfiguration="WithChannelBased.Web.AlbumProducer.customBinding0"
                    contract="WithChannelBased.Web.IAlbumProducer" />
                <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
            </service>
        </services>
    </system.serviceModel>
</configuration>

Aslında Siverlight-enabled WCF Service' i AlbumProcuder.svc adıyla eklediğimizden web.config dosyasında yukarıdaki XML içeriği otomatik olarak oluşturulmaktadır. Bir kaç küçük fark ile...İlk olarak Contract tipinin aslında IAlbumProcuder arayüzü olarak belirtilmesi gerekmektedir. Diğer yandan Asp.Net Compatibility Enabled opsiyonunun pasif olması gerekmektedir. Aksi durumda servisi, herhangibir tarayıcı uygulama üzerinden görüntülemek istediğimizde aşağıda yer alan hata ile karşılaşırız.

Şu aşamdan sunucu tarafındaki hazırlıklar tamamlanmıştır. Buna göre AlbumProducer.svc sayfasının tarayıcı uygulamadan talep edilmesi halinde aşağıdaki görüntü ile karşılaşılması işlerin iyi gittiğinin habercisidir.

Hemen bir hatırlatmada bulunalım. Servisi çalıştıran Asp.Net Development Server uygulamasının açtığı port numarası önemlidir. Nitekim istemci tarafında yazılacak olan kod içerisinde bu port numarası değerlendirilecektir Wink

İşin belkide kodlama açısından en sıkıcı noktası ise istemci tarafını geliştirmektir. Her şeyden önce istemci tarafının, sunucu tarafında yer alan IAlbumProducer.cs dosyasını referans etmesi gerekmektedir ki bunu ilgili dosyayı ilave ederken Add As Link seçeneğinin kullanılmasında yarar vardır. Böylece dosyanın tek bir noktada durması garanti edilmiş olur. Diğer yandan System.ServiceModel.dll(Servis çalışma zamanının tesisi için gerekli tipleri kullanabilmek için) ve System.Runtime.Serialization.dll(Veri sözleşme nitelikleri için) Assembly' larının Silverlight uygulamasının olduğu projeye referans edilmesi şarttır.

MainPage.xaml içeriğimizde basit olarak bir Button bileşenine basıldığında TextBlock kontrolünün içeriğinin sunucundan gelen Album bilgisi ile doldurulması planlanmaktadır. Buna göre kod içeriğini aşağıdaki gibi oluşturmamız yeterli olacaktır.

using System;
using System.Collections.Generic;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using WithChannelBased.Web;

namespace WithChannelBased
{
    public partial class MainPage : UserControl
    {
        // User Interface için ayrı bir Thread' in değerlendirileceği nesne tanımlanır
        SynchronizationContext syncContext;

        public MainPage()
        {
            InitializeComponent();
        }

        private void GetAlbumButton_Click(object sender, RoutedEventArgs e)
        {
            // BindingElement listesi tanımlanır
            List<BindingElement> bindings = new List<BindingElement>();
            // Sunucu tarafındaki web.config dosyasından hatırlanacağı üzere BinaryMessageEncoding tipinden bir Binding tipi mevcuttur. Öncelikle bu bağlayıcı listesine eklenir.
            bindings.Add(new BinaryMessageEncodingBindingElement());
            // Yine sunucu tarafındaki Binding listesine bakıldığında HttpTransport tipinden bir bağlayıcının da olduğu görülmektedir. Dolayısıyla bu tipten bir nesne örneğide oluşturulur.
            bindings.Add(new HttpTransportBindingElement());

            // Aynen Web.config dosyasında olduğu gibi, yukarıda tanımlanan Binding nesne örnekleri bir CustomBinding nesne örneği içerisinde toplanır. Bu nedenle parametre olarak bindings isimli liste verilmiştir.
            CustomBinding cBinding = new CustomBinding(bindings);

            // WCF çalışma zamanının bir kanal oluşturması için gerekli fabrika tipi tanımlanır.
            // İlk parametre kanalın kullanacağı bağlayıcı listesidir. İkinci parametre ise EndPoint için gerekli adres bilgisini içermektedir. (Port numarasını saklayın demiştim :) )
            var channelFactory = new ChannelFactory<IAlbumProducer>(
                cBinding,
                new EndpointAddress("http://localhost:57845/AlbumProducer.svc")
                );

            syncContext = SynchronizationContext.Current;

            // Kanal oluşturulu ve dolayısla açılır
            IAlbumProducer cnl = channelFactory.CreateChannel();

            // Servis tarafındaki GetAlbum metodu asenkron olarak çağırılır.
            cnl.BeginGetAlbum(
                1102
                , iar => {
                    Album albm=((IAlbumProducer)iar.AsyncState).EndGetAlbum(iar);
                    syncContext.Post(
                        obj =>
                        {
                            AlbumInfoTextBlock.Text = String.Format("{0}\n{1}\n{2}", albm.Id, albm.Genre, albm.Title);
                        }
                        , albm);
            }
            , cnl
            );
        }
    }
}

Volaaa!!! Uygulamayı test ettiğimizde aşağıdaki çalışma zamanı çıktısını almamız beklenmektedir.

Dikkat edileceği üzere istemci tarafında bir konfigurasyon dosyası içeriği hazırlanmamıştır. Bir başka deyişe WCF Çalışma Zamanı(WCF Runtime) için gerekli Endpoint, CustomBinding bildirimlerinin tamamı kod içerisinde gerçekleştirilmiştir. Özet olarak Silverlight istemcisinin Proxy tipine ihtiyaç duymadan çalışması sağlanabilmiştir. Böylece geldik bir görsel dersimizin daha sonuna. Tekrardan görüşünceye dek hepinize mutlu günler dilerim.

WithChannelBased_RTM.rar (72,10 kb) [Örnek Visual Studio 2010 RC sürümü üzerinde geliştirilmiş ve RTM sürümü üzerinden de test edilmiştir. Son sürümle birlikte test etmenizde yarar bulunmaktadır.]

Screencast - Ajax Enabled WCF Service’ lerin Silverlight ile Kullanılması

Çarşamba, 31 Mart 2010 14:35 by bsenyurt

Merhaba Arkadaşlar,

Daha önceki görsel derslerimizde WCF RIA Service ve Proxy bazlı WCF Service örneklerinin, Silverlight tarafında nasıl kullanılabildiklerini yeri geldikçe irdelemeye çalışmıştık. Silverlight uygulamalarının ASP.NET gibi Web ortamlarında sunulduğu göz önüne alındığında, kullanabileceği servis tiplerinden birisi de AJAX Enabled WCF Service' leridir ki özellikle Web Form' lar üzerinde bu tip servisler sıklıkla kullanılmaktadır. Söz gelimi otomatik metin tamamlama işlemlerinde...(Aslında AJAX tabanlı WCF Service' leri dışında ASMX tipindeki AJAX Service' leri de söz konusudur ama bu görsel dersimizde ele alınmamaktadır)

İşte bu görsel dersimizde öncelikli olarak bir AJAX Enabled WCF Service geliştirip, bunu örnek bir Silverlight uygulamasında nasıl kullanabileceğimizi görmeye çalışıyor olacağız. Geliştireceğimiz örnekte servis ve Silverlight uygulamasının aynı domain içerisinde yer aldıklarını farz ediyor olacağız ki ilerleyen görsel derslerimizde ayrı domain' lerdeki servisleri nasıl değerlendirebileceğimizi de incelemeye çalışacağız. E haydi o zaman, ne duruyorsunuz? NedirTv? sponsorluğunda hazırladığımız görsel dersimizi izleyin.

Not : Dersin ilerleyen kısımlarında, arka planda bizim ufaklığın ve onu memnun etmeye çalışan ev ahalisinin derinden gelen seslerini duyabilirsiniz. Ama dikkati dağıtacak derecede olmayacaktır.

Süre : 14:27

Dosya Boyutu : 24 Mb

Download Etmek veya İndirmek İçin

SilverlightApplication6.rar (100,34 kb) [Örnek Visual Studio 2010 RC Sürümü üzerinde geliştirimiş ve test edilmiştir]

Screencast - Silverlight Enabled WCF Services

Çarşamba, 24 Mart 2010 11:10 by bsenyurt

Merhaba Arkadaşlar,

Silverlight tarafında servis denildiğinde akla ilk gelen model WCF RIA Services' leridir. Esas itibariyle WCF Eco System' in bir parçası olan WCF RIA Service' ler Silverlight tarafında ele alınabilecek tek servis modeli değildir. Söz gelimi, aynı domain içerisinde yer alan bir WCF Service' i de, Silverlight istemcileri tarafından Proxy kullanımı ile tüketilebilir(Consume). İşte NedirTv? sponsorluğunda hazırladığımız bu görsel dersimizde serileştirilebilir bir tipi geriye döndüren bir operasyonu içeren Silverlight destekli bir WCF Service' inin, Proxy tabanlı olarak nasıl kullanılabileceğini incelemeye çalışıyoruz. Üstelik Proxy üretiminin bir faydası olarak ilgili servis operasyonu çağrısının asenkron olarak nasıl yapılabileceğini de göreceğiz.

Süre : 14:55

Boyut : 21.7 Mb

Download etmek veya izlemek için

SilverlightApplication4.rar (104,07 kb) [Örnek Visual Studio 2010 RC sürümü üzerinde geliştirilmiş ve test edilmiştir]

Screencast - AJAX Enabled WCF Services

Perşembe, 4 Mart 2010 10:45 by bsenyurt

Merhaba Arkadaşlar,

Görsel derslerimize kaldığımız yerden devam ediyoruz. Bu sefer elimizdeki materyaller bir WCF Service, Asp.Net Web Uygulaması ve AJAX. Bunları bir arada düşündüğümüzde ise karşımıza AJAX Enabled WCF Service kavramı çıkıyor. Bildiğiniz üzere Asp.Net uygulamalarında AJAX imkanları kullanılabilmekte ve bu sayede kısmi olarak post işlemleri gerçekleştirilebilmekte. Çok basit anlamda bir sayfanın tamamını sunucuya göndermeden sadece istenilen parçaların gönderilmesi ve sonuçlarının ele alınabilmesi mümkündür. Tabi sonuçların istemci tarafında ele alınması gerekmekte(Örneğin Javascript ile). AJAX Destekli WCF Servislerinde ise herhangibir servis operasyonunun çağrısı sırasında, servisi çağıran web sayfasının tamamının sunucuya gönderilmemesi imkanı kazanılmaktadır. AJAX Destekli WCF Servislerinin kullanıldığı pek çok senaryo söz konusudur. En basit haliyle otomatik metin tamamlama kabiliyetine sahip kontroller için bu teknikten yararlanılabilir. Bizde görsel dersimizde konu olarak bebek adlarını(Baby Names) ele almaya çalışacağız. Upss!!! Bebek Adları mı? Wink İzleyelim ve görelim.

Video Boyutu : 24 Mb

Süre : 14:06

Download Etmek veya İzlemek için

WebApplication2.rar (22,09 kb)

WCF Eco System

Salı, 5 Ocak 2010 10:30 by bsenyurt

Merhaba Arkadaşlar,

Özellikle son bir iki yıllık zaman dilimi içerisinde .Net tarafında pek çok servis modeli ve ismiyle karşılaştık. Örneğin Astoria kod adıyla başlayan Ado.Net Data Services, Silverlight gibi Rich Internet Application' ları hedef alan .Net RIA Services vb... (Eğer Microsoft' un ürünleri için kullandığı kod adlarını merak ediyorsanız Wikipedia' daki ilgili listeye bakmanızı öneririm) Hal böyle olunca ortada bir sürü kod adı ve isim oluşmaya başladı. Buda çok doğal olarak bizim gibi geliştricilerin kafasında pek çok soru işaretine neden oldu. Acaba hangi servis modelini hangi amaçlar ile kullanmalıyız? Bunların nihai sürümler yaklaştıkça isimlendirmeleri neler olacak? Ne gibi avantaj veya dez avantajları var?

Soruları arttırmak mümkün. Aslında kabul edilmesi gereken önemli bir nokta var; Tüm bu servis modelleri .Net Framework 3.0' dan beri var olan ve her sürümde önemli yetenekler kazanan Windows Communication Foundation(WCF) alt yapısı(Infrastructure diyebiliriz) üzerinde konuşlandırılmış durumda. Şu an içinde bulunduğumuz servis modellerinin çeşitliliğini ve sayısını düşündüğümüzde ise bir Eco System' in oluştuğunu net bir şekilde ifade edebiliriz. Aşağıdaki tabloda WCF Eco System' in parçaları yer almakta olup kısaca amaçları özetlenmeye çalışılmaktadır.

Model

Özet Bilgi

SOAP Services Modeli

Interoperability standartlarına uygun olan böylece örneğin Java gibi platformlar ile konuşabilen, mesaj tabanlı güvenliği(Message Based Security) baz alabilen, transaction akışına(Transaction Flow) izin veren, IIS üzerinden HTTP tabanlı veya IIS dışından host edilebilen(örneğin bir Windows Service, Windows Forms veya WPF uygulaması yada basit bir Console programı olarak), pek çok WS-* standardını destekleyen tipteki servisler olarak düşünülebilir. Bu servis modeli .Net Framework 3.0 versiyonundan bu yana mevcuttur. Geliştiricilere çok daha fazla kontrol imkanı sunan bir model olarak düşünülebilir. Diğer servis modellerindeki gibi belirli bir konuya odaklanmaktan ziyade ihtiyaçlara göre düşünülen çözümlerde değerlendirilir.

WebHttp Services Modeli

URI(Uniform Resource Identifier) bazlı olaraktan servis operasyonlarının RESTful yaklaşımına göre sunulduğu fonksiyonellikleri barındıran servis modelidir. WCF tarafındaki bu yetenekler .Net Framework 3.5 ile birlikte gelen Web programlama modeli(Web Programming Model) sayesinde ortaya çıkartılmış olup .Net Framework 4.0' da ek özellikler ile arttırılmıştır. Bu modelde verinin Get,Post,Put ve Delete gibi HTTP protokol metodlarına uygun olaraktan sunulması mümkündür. Geliştiriciler verinin sunulması sırasındaki URI bilgisine, çıktı formatına(örneğin JavaScript Object Notation tipinden olması) müdahalede bulunabilir.

Data Services Modeli

Veri modelimizi(Data Model) ve bu modelin içerdiği iş mantığın bir RESTful arayüzü üzerinden sunmak istediğimiz durumlarda kullandığımız servis modeli yaklaşımıdır. .Net için Open Data Protocol desteğini de içermektedir. Aslında ilk olarak .Net Framework 3.5 Service Pack 1 ile ve Ado.Net Data Services adıyla ortaya çıkmıştır. Bu yaklaşımda servisin sunacağı veri kaynağına ulaşırken Ado.Net Entity Framework gibi gelişmiş ORM birimlerinden yararlanılabilir. Ancak istenildiğinde Custom LINQ Provider' lardanda faydalanılıp farklı veri kaynaklarının kullanılması mümkün olabilir.

* RIA Services ile Data Services zaman zaman bir birlerine karıştırılmaktadır. Aslında Data Services, veri modelinin ve bu model ile ilişkili iş mantığının sunulması ile ilgilenmekte iken RIA Service' leri Silverlight uygulamalarının end-to-end modelinde geliştirilmesine odaklanmaktadır. Bu nedenle benzer olmalarına rağmen ilgilendikleri vakalar tamamen farklıdır.

** Yine Data Service' lerin zaman zaman WebHttp Service' leri ile karıştırılmasıda söz konusudur. Ancak yine her iki servis modelinin ilgilendiği vakalar farklıdır. WebHttp Service' leri URI, format ve protocol bilgileri üzerinde tam yönetime izin verecek şekilde RESTful servisler geliştirebilmemize olanak sağlar. Ancak Data Service' lerde RESTful arayüzü zaten hazırdır ve sadece veri modeli ile ilişkilendirilmesi yeterlidir.

Workflow Services Modeli

Uzun süreli(Long Running) ve kalıcılık(Persistence) desteği olması gereken Workflow uygulamalarının servis bazlı olarak kullanılabilmesini sağlayan modeldir. Bu modele göre Workflow nesnelerinin WCF servisi olarak sunulması, tüketilmesi ve ayrıca Workflow nesnelerinin içerisinde WCF servislerinin çağrılıp değerlendirilmesi mümkündür.

* .Net Framework 3.0' da birbirlerine uzaktan bakan WF ve WCF alt yapıları, .Net Framework 3.5 ile flört etmeye başlamış ve .Net Framework 4.0' da evlenmiştir. Wink

RIA Services Modeli

Sliverlight gibi Rich Internet Application(RIA) uygulamalarında orta katmandaki iş modelinin servis bazlı olarak hem istemci hemde sunucu tarafında yönetilmesini, oluşturulmasını ve kullanılmasnı kolaylaştıran end-to-end servis modelidir. Önceki adı .Net RIA Services olmasına rağmen değişen adıyla birlikte tam olarak Silverlight 4.0 sürümünde nihai versiyonuna ulaşacağı tahmin edilmektedir.

Tabi buradaki bilgiler dışında aşağıdaki çizelgeyide göz önüne almamızda yarar vardır. Bu çizelgede WCF Eco System' in mimari modeli gösterilmektedir.

Aslında WCF alt yapısı üzerinde duran servis programlama modelleri, geliştiricilerin vakaya göre WCF alt yapı detaylarından uzaklaşmasını sağlamaktadır. Bu, özellikle Data Services ve RIA Services modellerinde ön plana çıkmaktadır. Yine de istenildiğinde bu modelleri esnetebileceğimizi bilmeliyiz. Hatta bana kalırsa WCF alt yapısı üzerine oturan kendi programlama modellerimizi de geliştirebiliriz. Ancak buna gerek olup olmadığını tartışmalıyız.

Umarım WCF Eco System hakkında biraz fikir sahibi olabilmişizdir. Tekrardan görüşünceye dek hepinize mutlu günler dilerim.

WCF Known Types Analizi

Salı, 20 Ekim 2009 22:36 by bsenyurt

Merhaba Arkadaşlar,

Bilindiği üzere WCF aslında SOA(Service Oriented Architecture) mimarisinin uygulama modellerinden birisidir. İşin içerisinde servisler söz konusu olduğunda ağlar ve sistemler arası mesajlaşlamalar söz konusudur. Mesajlaşmalar söz konusu olduğundaysa, servis ve istemci arasında hareket eden verinin serileşebilir olması önem arz eden konuların başında gelmektedir. Ne varki serileşen veri içeriklerinin, platform bağımsızlık adına her iki tarafında kullanabileceği tiplerden(Types) oluşmasının sağlanması bir avantajdır. İşte bu noktada biz WCF geliştiricileri için anlaşılması zor olan ve dikkatle üzerinde durumlası gereken kıyıda köşede kalmış konulardan biriside Known Types kavramıdır.

WCF ile birlikte gelen ve serileştirmede kullanılan tipler esas itibariyle Shared Contracts kategorisindendir. Nitekim serileşen tipin ve kullandığı içeriğin Interoperabilitiy kuralları çevresinde değerlendirilebiliyor olması gerekir. DataContractSerializer, JsonDataContractSerializer ve XmlSerializer gibi serileştirici tipler bu kategoride yer almaktadır. Diğer yandan BinaryFormatter, SoapFormatter veya NetDataContractSerializer gibi tipler, Shared Types kategorisinde yer alan serileştiricilerdir. Genellikle serileşen tiplerin içerdiği tip bilgilerinin tanımlandığı Assembly' lar, uygulama ile aynı makinede yer alır. Örneğin serileştirilebilir tipin içeriğinin .Net CLR tiplerinden oluştuğunu düşünelim. Bu durumda ters serileştirme işlemini üstelenen uygulamanında bu CLR(Common Language Runtime) tiplerini biliyor olması, bir başka deyişle uygun Framework Assembly' larına sahip olması yeterlidir. Ancak SOA tabanlı bir sistemde serileştirme(Serialize) ve ters-serileştirme(Deserialize) yapan tarafların aralarında taşıdıkları tiplerle ilişkili olarak ortak bir noktada buluşmaları gerekmektedir. Nitekim hem sağlayıcı hemde tüketici taraflar kendi platformlarında kendi özel veri tiplerine sahiptir ve sadece bu tipleri kullanabilir. Bu noktada tipin XSD(XmlSchemaDefinition) şemaları içerisinde tanımlandığını(dolayısıyla serileşen paketler içerisinde taşındığını) ve örneğin Proxy' yi üreten tarafta bile kullanılabilecek bir tipe karşılık gelmesi gerektiğini söyleyebiliriz. Tabiki buradaki veri tipi bilgileri arada transfer edilen XML içeriklerinde ortaktır.

Aslında bu teorik bilgiler, yazarken bile insanın kafasını allak bullak etmeye neden olabilir. Bu yüzden kafamızda sorunsal haline gelebilecek bu konuyu örnekler üzerinden açıklamakta yarar olacağı kanısındayım. İşe ilk olarak Visual Studio 2008 ortamında oluşturulumuş bir Console uygulaması ve aşağıdaki kod parçası ile başlayalım.(System.Runtime.Serialization referansının eklenmiş olması gerektiğini unutmayın)

using System.IO;
using System.Runtime.Serialization;

namespace KnownTypes
{
    class Program
    {
        static void Main(string[] args)
        {
            // DataContractSerializer nesnesi örneklenirken parametre olarak serileştirilecek tip bilgisi verilir.
            XmlObjectSerializer serializer = new DataContractSerializer(typeof(Product));
            // Serileştirme işlemi yapılır. İlk parametre ile çıktının Product.xml isimli dosyaya yaplacağı belirtilir.
            // İkinci parametrede Product nesnesi örneklenir. Dikkat edilecek nokta Information özelliğine object tipinden bir nesne örneğinin aktarılmış olmasıdır.           
            serializer.WriteObject(
                new FileStream("Product.xml", FileMode.Create, FileAccess.Write)
                , new Product { Information = new object() }
                );

            // Object tipine string atama
            serializer.WriteObject(
                new FileStream("ProductV2.xml", FileMode.Create, FileAccess.Write)
                , new Product { Information = "Ürün hakkında çeşitli bilgiler" }
                );

            // Exception durumu
            //serializer.WriteObject(
            //    new FileStream("ProductV3.xml", FileMode.Create, FileAccess.Write)
            //    , new Product { Information = new ProductInformation { Id=1, Summary="Ürün için çeşitli bilgiler" } }
            //    ); 
        }
    }

    [DataContract]
    class Product
    {
        [DataMember]
        public object Information { get; set; }
    }

    // [DataContract]
    // class ProductInformation
    // {
    //     [DataMember]
    //     public string Summary { get; set; }
    //     [DataMember]
    //     public int Id { get; set; }
    // }
}


Örneğimizde Product isimli serileştirilebilir bir tip tanımlandığı görülmektedir. DataContract niteliği ile işaretlenmiş olan Product tipinin object tipinden Information isimli bir özelliği de bulunmaktadır ki işleri karıştıracak olan nokta burasıdır. Program kodu içerisindeki amaç, Product tipinden nesne örneklerinin nasıl serileştirildiğine bakmaktır. serializer isimli XmlObjectSerializer nesne örneği üzerinden yapılan ilk WriteObject çağrısındai Information özelliğine object tipinden bir değer atandığı görülmektedir. İkinci WriteObject çağrısında ise bir string değer atanmıştır ki bu son derece doğaldır. (Hatırlayalım .Net tipleri en üstte Object tipinden türemektedir. Bu nedenle her nesnenin Object tipi ile ifade edilebilmesi mümkündür.) Bu kod parçasını çalıştırdığımızda Product.xml ve ProductV2.xml isimli iki dosya üretildiğini görürüz. Önce Product.xml içeriğine ve şemasına bir bakalım.

Product.xml içeriği;

<Product xmlns="http://schemas.datacontract.org/2004/07/KnownTypes" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><Information/></Product>

Product.xsd içeriği;

<?xml version="1.0" encoding="windows-1254"?>
<xs:schema xmlns:i="http://www.w3.org/2001/XMLSchema-instance" attributeFormDefault="unqualified" elementFormDefault="qualified" targetNamespace="http://schemas.datacontract.org/2004/07/KnownTypes" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="Product">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="Information" />
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</xs:schema>

Tahmin ettiğimiz ve beklediğimiz gibi bir çıktı üretilmiştir. Ancak Information özelliğine string bir değer atanmasının sonucu oluşan çıktı biraz farklıdır. Bu noktada dikkatle duralım Sealed

ProductV2.xml içeriği;

<Product xmlns="http://schemas.datacontract.org/2004/07/KnownTypes" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
  <Information i:type="a:string" xmlns:a="http://www.w3.org/2001/XMLSchema">Ürün hakkında çeşitli bilgiler</Information>
</Product>

ProductV2.xsd içeriği;

<?xml version="1.0" encoding="utf-8"?>
<a:schema xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:a="http://www.w3.org/2001/XMLSchema" attributeFormDefault="unqualified" elementFormDefault="qualified" targetNamespace="http://schemas.datacontract.org/2004/07/KnownTypes">
  <xs:element name="Product">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="Information" type="xs:string" />
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</a:schema>

XSD şemasında, Information elementi için type niteliğinde xs:string kullanıldığı görülmektedir. Üstelik üretilen veri içeriğinde de i:type niteliğinde Information elementinin içeriğinin string veri tipinden olduğu işaret edilmektedir. Yani Information özelliğine atadığımız string değişken, primitive bir tip tanımana göre XML içerisinde bildirilmiştir. Zaten primitive tiplere dönüşüm yapıldığı takdirde pek bir sıkıntı yoktur. Söz gelimi Information özelliğine örneğin 12 değerini atadığımızda buna uygun olarak XML içeriğinde de int tipinin kullanıldığı görülebilir. Sorun yorum satırı kodlarını çalışır hale getirdiğimizde ortaya çıkmaktadır. Yeni ProductInformation tipini etkinleştirip aşağıdaki kodları çalıştırdığımızda... Undecided

// Exception durumu
            serializer.WriteObject(
                new FileStream("ProductV3.xml", FileMode.Create, FileAccess.Write)
                , new Product { Information = new ProductInformation { Id=1, Summary="Ürün için çeşitli bilgiler" } }
                );
               
Bu sefer Information özelliğine serileştirilebilir(ki DataContract ve DataMember nitelikleri nedeni ile) ProductInformation tipinden bir nesne örneği atanmaktadır. Bu durumda çalışma zamanında aşağıdaki ekran görüntüsünde yer alan SerializationException istisnasının alındığı görülür.

Bu istisna mesajından anlamamız gereken özlü söz şudur;

"Serileştirici tiplerin, serileştirecekleri nesne örneklerinin özelliklerinin tiplerinin neler olabileceğini açık bir şekilde bilmeye ihtiyaçları vardır. " Wink

Her ne kadar primitive bir tip kullanıldığında sorun olmasa da, yukarıdaki örnekte görüldüğü gibi bilinmeyen bir tipin atanmasında sorunlar yaşanabilir. Üstelik belkide atanan verinin tipinin, serileştirici tarafından varsayılan olarak atanan bir tip olmaması da gerekebilir. Söz gelimi Information özelliğine noktasız sayısal bir değer atandığında büyüklüğüne göre varsayılan olarak int tipi göz önüne alınacak ve XML içeriğinde bu yönde bir tanımlama olacaktır. Ki karşı tarafta belkide bu sayısal değerin string olarak değerlendirilmesi isteniyor olabilir! Upsss...

Peki serileştiricinin serileştirdiği tipin içeriğinde kullanılabilecek tipleri kesin olarak bilmesi nasıl sağlanabilir?

Yöntemlerden birisi ve belkide en basiti, aşağıdaki kod parçasında olduğu gibi serileştirilecek tip için KnownType niteliğini kullanmaktır.

Kişisel Not: Başka yöntemlerde bulunmaktadır. Örneğin tip içerisinde Type[] dizisi döndüren bir metodun adı KnownType niteliğinde kullanılarak birden fazla tipin bildirimi yapılabilir. Ya da servis sözleşmesinde ServiceKnownType niteliğinde bu bildirim yapılabilir. İşte size güzel bir araştırma konusu. Bu tekniklerin nasıl uygulanabileceğini araştırabilirsiniz. Özellikle ilk teknik dikkate değerdir. Yani bir metod aracılığıyla, KnowType niteliğine birden fazla tipin Type[] dizisi olarak bildirilmesi. Dikkat edilmesi gereken tek nokta Type[] dizisi döndüren metodun static olmasını sağlamaktır. Hatta buradan bir adım öteye gidip generic bir modelin KnownType niteliğine söz konusu metod yardımıyla aktarılması dahi sağlanabilir. Bu kadar ipucu yeter. Haydi klavye başına Wink

[DataContract]
[KnownType(typeof(ProductInformation))]
class Product
{
   [DataMember]
   public object Information { get; set; }
}

Bu şekildeki kullanım sonrasında artık SerializationException istisnası üretilmeyecek ve aşağıdaki çıktılar oluşacaktır.

ProductV3.xml içeriği;

<Product xmlns="http://schemas.datacontract.org/2004/07/KnownTypes" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
  <Information i:type="ProductInformation">
    <Id>1</Id>
    <Summary>Ürün için çeşitli bilgiler</Summary>
  </Information>
</Product>

Dikkat edileceği üzere ProductInformation nesne örneği içeriği ile birlikte Product elementi içerisine alınmıştır. Bu durumda şema içeriğinde gerekli bilgilendirmelerin aşağıdaki gibi yapıldığı görülecektir.

ProductV3.xsd içeriği

<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:i="http://www.w3.org/2001/XMLSchema-instance" attributeFormDefault="unqualified" elementFormDefault="qualified" targetNamespace="http://schemas.datacontract.org/2004/07/KnownTypes" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="Product">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="Information">
          <xs:complexType>
            <xs:sequence>
              <xs:element name="Id" type="xs:unsignedByte" />
              <xs:element name="Summary" type="xs:string" />
            </xs:sequence>
          </xs:complexType>
        </xs:element>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</xs:schema>

Known Type sorunsalının bir sorunsal olarak değerlendirilmesinin ise iki sebebi vardır.

Birincisi, SOA düşünce tarzına aykırı olduğu görüşününün yaygın olmasıdır. Nitekim Shared Contract' ların söz konusu olduğu senaryolarda Interoperability sağlanırken, XML içerisindeki tipin karşı tarafça anlaşılabilir olması gerekmektedir. İkinci olarak serileştirme ve ters serileştirme işlemleri sırasında Known Type' a göre Reflection mekanizmasının devreye girmesi, XML üzerinde tip ile ilişkili bilgi edinme ve yazma gibi operasyonların söz konusu olmasından kaynaklanan performans kayıpları değerlendirilmektedir. Bu iki sebep nedeniyle zorunlu kalınmadıkça Known Type kullanımından kaçınılması önerilmektedir. Peki neden böyle bir konuya değindik? Aslında bir sonraki yazımıza zemin hazırlamaya çalışıyoruz. Nitekim WCF 4.0 tarafında bu konu ile ilişkili bir yenilik gelmesi muhtemeldir. Tekrardan görüşünceye dek hepinize mutlu günler dilerim.

KnownTypes.rar (22,64 kb)

Tags:  
Categories:   WCF
Actions:   E-mail | del.icio.us | Permalink | Yorumlar (0) | Comment RSSRSS comment feed
Bookmark and Share

Ado.Net Data Services 1.5 - Projections

Çarşamba, 30 Eylül 2009 09:00 by bsenyurt

Merhaba Arkadaşlar,

Gün geçmiyorki yazılım teknolojilerinde bir yenilik, bir güncelleme, bir genişletme çıkmasın...Özellikle dünyanın dev yazılım şirketlerinin en büyüğü olarak görebileceğimiz Microsoft tarafında bu gelişme ve güncelleme hızı oldukça yüksek. Gerçektende heyecan verici yenilikler, özellikler ile karşılaşmıyor değiliz. Bu konuya nereden mi geldim? Çok zaman değil daha bir sene öncesine kadar Astoria kod adlı Ado.Net Data Services konusunu incelemeye başlamıştım. Entity Framework veya Custom LINQ Provider' ları ile sunulan veri kümelerine, REST bazlı olarak URL sorgular atılabilmesini sağlayan ve özellikle Silverlight gibi RIA içeriklerinde son derece kıymetli olan bir servis uygulaması olarak değerlendirebileceğimiz bu konu ile ilişkili ilk paylaşımlarımı yaptıktan sonra araya WCF 4.0, WF 4.0, Design Patterns, Design Principles, .Net RIA Services gibi konular girdi. Bu konulardaki incelemelerimi ve paylaşımlarımı devam ettirirken bir baktım ki Ado.Net Data Services konusuna çok uzun zaman ara vermişim. Ara vermeklede iyi yapmamışım Undecided

Nitekim program yöneticisi olan Mike Flasko boş durmamış ve Ado.Net Data Services v1.5 versiyonu için CTP2 sürümünü duyurmuş(.Net Framework 3.5 Service Pack 1 ve Silverlight 3.0' ı hedefleyen ama .Net Framework 4.0 içerisinede dahil edilecek olan özellikleri içeren bir sürüm olarak düşünülebilir). Duyurulması ile birlikte hem blog sitesinde hemde çeşitli kaynaklarda konu ile ilişkili yazılar yayınlanmaya da başlanmış.

Bu versiyonda bazı yenilikler ve daha önceki sürüme ait çeşitli düzeltmeler(bug-fix) yer almakta. Gelen yeni özelliklerden birisi de Projections kullanımı. Bu yeniliğe göre servis üzerinde gerçekleştirilen URL bazlı sorguların sonuçları kırpılabiliyor ve sadece ilgilenilmek istenenlerin istemci tarafına çekilmesi sağlanabiliyor. Wink Bir başka deyişle, istemcinin yapmış olduğu bir talebin(Request) sonuçlarında sadece ilgilendiği özelliklerin getirilmesi sağlanabilmekte. Bunu tam olmasada, LINQ sorguları sırasında anonymous type kullanımına benzetebiliriz. Söz konusu özellik içerisinde primitive/complex tipleri veya navigation özelliklerini de kullanabilmekteyiz. Özelliğin getirisi, istemcinin talebi sonrası tüm Entity kümesinin işlenmesi ve ağ üzerinde hareket etmesi yerine, sadece istediği özellikleri içeren kümenin/kümelerin değerlendirilebilmesi olarak görülebilir. Bu çok doğal olarak istemci ile sunucu arasındaki trafiği boyutsal olarak azaltmaktadır. Projections kullanımı son derece basittir. Bunun için $select operatöründen yararlanılmaktadır. Tabiki konuyu anlamamızın en iyi yolu basit bir örneği adım adım geliştirmek ve üzerinde ilerlemekle olacaktır. Bu nedenle kolları sıvayıp işe koyulalım. İlk olarak Visual Studio 2008 ortamında(Service Pack 1 yüklü olan) basit bir Asp.Net Web Uygulaması oluşturarak işe başlayabiliriz. Sonrasında servisimiz için gerekli Entity kaynağını oluşturmamız gerekiyor. Bu amaçla Ado.Net Entity Framework' ten yararlanabilir ve yine kobay veritabanımız olan AdventureWorks' ü değerlendirebiliriz. Örneğimizde aşağıdaki EDM şemasını kullanıyor olacağız.

AdventureWorks veritabanındaki Production şemasında yer alan ProductCategory, ProductSubCategory ve Product tablolarını kullanmaya çalışıyoruz. Entity modelimizi oluşturduktan sonra, projemize yeni bir Ado.Net Data Services öğesi ekleyerek devam edebiliriz. Tabi bu seferki örneğimizde v1.5 CTP2 sürümüne ait öğeyi kullanmamız gerekiyor.

Ado.Net Data Services öğemizin kod içeriğini ise aşağıdaki gibi değiştirmemiz yeterli olacaktır.

using System.Data.Services;

namespace Projections
{
    public class AdventureServices
        : DataService<AdventureWorksEntities>
    {
        public static void InitializeService(DataServiceConfiguration config)
        {
            // Tüm Entity' leri sadece okuma amaçlı açıyoruz
            config.SetEntitySetAccessRule("*", EntitySetRights.AllRead);
            // İstemciden gelecek olan Projection taleplerinin değerlendirileceğini belirtiyoruz
            config.DataServiceBehavior.AcceptProjectionRequests = true;
            // Versiyon 2 için geliştirme yapacağımızı belirtiyoruz. Bu versiyon belirtilmediği takdirde select operatörü ve projection fonksiyonelliği çalışmayacaktır.
            config.DataServiceBehavior.MaxProtocolVersion = System.Data.Services.Common.DataServiceProtocolVersion.V2;
     }
    }
}

Dikkat edilmesi gereken noktalardan birisi AcceptProjectionRequest ise MaxProtocolVersion özelliklerine atanan değerlerdir. Bu değerlere göre servisimiz, istemcilere Projection fonksiyonelliğini sunabilecektir. AdventureServices.svc dosyasını bir tarayıcı yardımıyla talep ettiğimizde, başlangıç için aşağıdakine benzer bir ekran görüntüsü ile karşılaşırız.

Görüldüğü üzere Product, ProductCategory ve ProductSubcategory Entity' leri kullanılmaya hazırdır. Evetttt...Gelelim yazımızın önemli olan kısmına. Tarayıcı üzerinden aşağıdaki sorguyu talep ettiğimizi düşünelim.

http://localhost:1714/AdventureServices.svc/Product?$select=ProductID,Name,ListPrice

Dikkat edileceği üzere Product Entity' si üzerinden select sorgusu atılmış ve sadece ProductID,Name,ListPrice alanları talep edilmiştir. Bu sorgunun çalışma zamanı çıktısı aşağıdaki gibi olacaktır.

Dikkat edileceği üzere Product tablosundaki tüm ürünlerin sadece ProductID,Name ve ListPrice alanları çekilmiştir. İşin güzel yanı, bu URL talebi için arka planda çalıştırılan SQL sorgusuda sadece istenen alanları değerlendirmektedir. İşte URL' imize ait SQL sorgusunun SQL Server Profiler' dan yakalanan içeriği.

SELECT
1 AS [C1],
CASE WHEN ([Extent1].[ProductID] IS NULL) THEN N'' ELSE N'AdventureWorksModel.Product' END AS [C2],
N'ProductID,Name,ListPrice' AS [C3],
[Extent1].[ProductID] AS [ProductID],
[Extent1].[Name] AS [Name],
[Extent1].[ListPrice] AS [ListPrice]
FROM [Production].[Product] AS [Extent1]

Dolayısıyla Projection kullanılaraktan, bir Entity üzerinden sadece istenen alanları içeren çıktıların alınması sağlanabilir. Bu kullanım aynen SQL tarafı içinde geçerli olduğundan, performans adına da bazı kazanımların elde edildiği ortadadır.

select operatörünü dilersek navigasyon özellikleri(Navigation Properties) ilede bir aradada kullanabiliriz. Örneğin aşağıdaki gibi bir URL talebinde bulunduğumuzu düşünelim.

http://localhost:1714/AdventureServices.svc/ProductSubcategory?$select=Name,Product

Buna göre ProductSubcategory Entity' sinden sadece Name alanlarının değerlerini isterken, her alt kategoriye bağlı ürünleri tutan Product Entity örneklerini de talep etmekteyiz. Bu URL talebinin çıktısı aşağıdaki gibi olacaktır.

Dikkat edileceği üzere alt kategoriye bağlı olan Product kümeleri için sadece bağlantı bildirimi yapılmaktadır. Söz konusu URL' ın çalıştırılması sonucunda SQL tarafında da aşağıdaki sorgunun yürütüldüğü görülecektir.

SELECT
1 AS [C1],
CASE WHEN ([Extent1].[ProductSubcategoryID] IS NULL) THEN N'' ELSE N'AdventureWorksModel.ProductSubcategory' END AS [C2],
N'Name,ProductSubcategoryID' AS [C3],
[Extent1].[Name] AS [Name],
[Extent1].[ProductSubcategoryID] AS [ProductSubcategoryID]
FROM [Production].[ProductSubcategory] AS [Extent1]

Fark edilebileceği gibi, Product tablosu ile ilişkili bir sorgu ifadesi yer almamaktadır. Diğer yandan sadece Name alanını talep etmemize rağmen, PrimaryKey olan ProductSubcategoryID alanı da getirilmektedir. Bu son derece doğaldır nitekim, belirli bir ProductSubcategory' nin çekilmesinde primary key alanı ayırt edici özelliklerdendir üstelik entry/id elementleri içerisinde gereklidir. Diğer yandan, URL satırını aşağıdaki gibi değiştirirsek,

http://localhost:1714/AdventureServices.svc/ProductSubcategory?$select=Name,Product&$expand=Product&$top=2

Hımmm...Wink Bu sorguya göre ProductSubcategory içeriğinden sadece Name alanını almakla kalmıyor, aynı zamanda alt kategoriye bağlı olan ürünleride çekiyoruz. Üstelik sadece ilk 2 ProductSubcategory tipini ele alıyoruz(Sondaki top=2 sorgusu nedeniyle). İşte çalışma zamanı çıktımız.

Peki bu URL talebi sonrası arka planda nasıl bir SQL sorgusu çalışıyor?

SELECT
[Project2].[ProductSubcategoryID] AS [ProductSubcategoryID],[Project2].[Name] AS [Name], [Project2].[rowguid] AS [rowguid], [Project2].[ModifiedDate] AS [ModifiedDate], [Project2].[C1] AS [C1], [Project2].[C2] AS [C2], [Project2].[C3] AS [C3], [Project2].[C4] AS [C4], [Project2].[C5] AS [C5], [Project2].[C6] AS [C6], [Project2].[C7] AS [C7], [Project2].[ProductID] AS [ProductID], [Project2].[Name1] AS [Name1], [Project2].[ProductNumber] AS [ProductNumber], [Project2].[MakeFlag] AS [MakeFlag], [Project2].[FinishedGoodsFlag] AS [FinishedGoodsFlag], [Project2].[Color] AS [Color], [Project2].[SafetyStockLevel] AS [SafetyStockLevel], [Project2].[ReorderPoint] AS [ReorderPoint], [Project2].[StandardCost] AS [StandardCost], [Project2].[ListPrice] AS [ListPrice], [Project2].[Size] AS [Size], [Project2].[SizeUnitMeasureCode] AS [SizeUnitMeasureCode], [Project2].[WeightUnitMeasureCode] AS [WeightUnitMeasureCode], [Project2].[Weight] AS [Weight], [Project2].[DaysToManufacture] AS [DaysToManufacture], [Project2].[ProductLine] AS [ProductLine], [Project2].[Class] AS [Class], [Project2].[Style] AS [Style], [Project2].[ProductModelID] AS [ProductModelID], [Project2].[SellStartDate] AS [SellStartDate], [Project2].[SellEndDate] AS [SellEndDate], [Project2].[DiscontinuedDate] AS [DiscontinuedDate], [Project2].[rowguid1] AS [rowguid1], [Project2].[ModifiedDate1] AS [ModifiedDate1]
FROM ( SELECT
 [Limit1].[ProductSubcategoryID] AS [ProductSubcategoryID],  [Limit1].[Name] AS [Name],  [Limit1].[rowguid] AS [rowguid],  [Limit1].[ModifiedDate] AS [ModifiedDate],  [Limit1].[C1] AS [C1],  [Limit1].[C2] AS [C2],  [Limit1].[C3] AS [C3],  [Limit1].[C4] AS [C4],  [Limit1].[C5] AS [C5],  [Limit1].[C6] AS [C6],  [Extent2].[ProductID] AS [ProductID],  [Extent2].[Name] AS [Name1],  [Extent2].[ProductNumber] AS [ProductNumber],  [Extent2].[MakeFlag] AS [MakeFlag],  [Extent2].[FinishedGoodsFlag] AS [FinishedGoodsFlag],  [Extent2].[Color] AS [Color],  [Extent2].[SafetyStockLevel] AS [SafetyStockLevel],  [Extent2].[ReorderPoint] AS [ReorderPoint],  [Extent2].[StandardCost] AS [StandardCost],  [Extent2].[ListPrice] AS [ListPrice],  [Extent2].[Size] AS [Size],  [Extent2].[SizeUnitMeasureCode] AS [SizeUnitMeasureCode],  [Extent2].[WeightUnitMeasureCode] AS [WeightUnitMeasureCode],  [Extent2].[Weight] AS [Weight],  [Extent2].[DaysToManufacture] AS [DaysToManufacture],  [Extent2].[ProductLine] AS [ProductLine],  [Extent2].[Class] AS [Class],  [Extent2].[Style] AS [Style],  [Extent2].[ProductModelID] AS [ProductModelID],  [Extent2].[SellStartDate] AS [SellStartDate],  [Extent2].[SellEndDate] AS [SellEndDate],  [Extent2].[DiscontinuedDate] AS [DiscontinuedDate],  [Extent2].[rowguid] AS [rowguid1],  [Extent2].[ModifiedDate] AS [ModifiedDate1], 
 CASE WHEN ([Extent2].[ProductID] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C7]
 FROM   (SELECT TOP (2) [Project1].[ProductSubcategoryID] AS [ProductSubcategoryID], [Project1].[Name] AS [Name], [Project1].[rowguid] AS [rowguid], [Project1].[ModifiedDate] AS [ModifiedDate], [Project1].[C1] AS [C1], [Project1].[C2] AS [C2], [Project1].[C3] AS [C3], [Project1].[C4] AS [C4], [Project1].[C5] AS [C5], [Project1].[C6] AS [C6]
  FROM ( SELECT
   [Extent1].[ProductSubcategoryID] AS [ProductSubcategoryID],
   [Extent1].[Name] AS [Name],
   [Extent1].[rowguid] AS [rowguid],
   [Extent1].[ModifiedDate] AS [ModifiedDate],
   1 AS [C1],
   1 AS [C2],
   CASE WHEN ([Extent1].[ProductSubcategoryID] IS NULL) THEN N'' ELSE N'AdventureWorksModel.ProductSubcategory' END AS [C3],
   N'Name,ProductSubcategoryID' AS [C4],
   N'Product' AS [C5],
   1 AS [C6]
   FROM [Production].[ProductSubcategory] AS [Extent1]
  )  AS [Project1]
  ORDER BY [Project1].[ProductSubcategoryID] ASC ) AS [Limit1]
 LEFT OUTER JOIN [Production].[Product] AS [Extent2] ON [Limit1].[ProductSubcategoryID] = [Extent2].[ProductSubcategoryID]
)  AS [Project2]
ORDER BY [Project2].[ProductSubcategoryID] ASC, [Project2].[C7] ASC

Amanınnnn!!! Sealed Aslında biraz can sıkıcı ama doğal olarak tüm Product alanlarının değerlendirildiğini görüyoruz. Nitekim aksini belirtmedik. Peki belirtebilir miyiz? Yani ProductSubcategory kümesinden ve genişletilebilen Product kümesinden bir kaç alanı almayı başarabilir miydik? İşte örnek bir cevabı Cool

http://localhost:1714/AdventureServices.svc/ProductSubcategory?$select=Name,Product/Name,Product/ListPrice&$expand=Product&$top=5

Görüldüğü gibi EntityAdı/AlanAdı(örneğin Product/Name) stilinde yapılan bildirimlerle, üretilecek olan çıktıda birden fazla Entity' den gelebilecek alanları ayrı ayrı belirtebiliyoruz. (Buna göre sizlerde ProductCategory,ProductSubcategory ve Product kümelerinin tamamının bir arada bulunduğu örnek URL üzerinde çalışabilirsiniz. Çalışmanızı öneririm.)

Görüldüğü üzere alt kategori ile ilişkili Feed girişlerinde Name alanı yer almaktayken, o alt kategoriye bağlı Product tipleri için sadece Name ve ListPrice değerleri getirilmektedir. Dolayısıyla SQL sorgusuda buna göre aşağıda görüldüğü gibi oluşacaktır.

SELECT
[Project2].[ProductSubcategoryID] AS [ProductSubcategoryID], [Project2].[Name] AS [Name], [Project2].[C1] AS [C1], [Project2].[C2] AS [C2], [Project2].[C3] AS [C3], [Project2].[C4] AS [C4], [Project2].[C5] AS [C5],
[Project2].[C9] AS [C6], [Project2].[C6] AS [C7], [Project2].[C7] AS [C8], [Project2].[C8] AS [C9], [Project2].[Name1] AS [Name1], [Project2].[ListPrice] AS [ListPrice], [Project2].[ProductID] AS [ProductID]
FROM ( SELECT
 [Limit1].[ProductSubcategoryID] AS [ProductSubcategoryID],  [Limit1].[Name] AS [Name],  [Limit1].[C1] AS [C1],  [Limit1].[C2] AS [C2],  [Limit1].[C3] AS [C3],  [Limit1].[C4] AS [C4],  [Limit1].[C5] AS [C5],  [Extent2].[ProductID] AS [ProductID],  [Extent2].[Name] AS [Name1],  [Extent2].[ListPrice] AS [ListPrice],  CASE WHEN ([Extent2].[ProductID] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C6],
 CASE WHEN ([Extent2].[ProductID] IS NULL) THEN CAST(NULL AS varchar(1)) ELSE CASE WHEN ([Extent2].[ProductID] IS NULL) THEN N'' ELSE N'AdventureWorksModel.Product' END END AS [C7],
 CASE WHEN ([Extent2].[ProductID] IS NULL) THEN CAST(NULL AS varchar(1)) ELSE N'Name,ListPrice,ProductID' END AS [C8],
 CASE WHEN ([Extent2].[ProductID] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C9]
 FROM   (SELECT TOP (5) [Project1].[ProductSubcategoryID] AS [ProductSubcategoryID], [Project1].[Name] AS [Name], [Project1].[C1] AS [C1], [Project1].[C2] AS [C2], [Project1].[C3] AS [C3], [Project1].[C4] AS [C4], [Project1].[C5] AS [C5]
  FROM ( SELECT
   [Extent1].[ProductSubcategoryID] AS [ProductSubcategoryID], [Extent1].[Name] AS [Name], 1 AS [C1], 1 AS [C2],
   CASE WHEN ([Extent1].[ProductSubcategoryID] IS NULL) THEN N'' ELSE N'AdventureWorksModel.ProductSubcategory' END AS [C3],
   N'Name,ProductSubcategoryID' AS [C4],
   N'Product' AS [C5]
   FROM [Production].[ProductSubcategory] AS [Extent1]
  )  AS [Project1]
  ORDER BY [Project1].[ProductSubcategoryID] ASC ) AS [Limit1]
 LEFT OUTER JOIN [Production].[Product] AS [Extent2] ON [Limit1].[ProductSubcategoryID] = [Extent2].[ProductSubcategoryID]
)  AS [Project2]
ORDER BY [Project2].[ProductSubcategoryID] ASC, [Project2].[C9] ASC

Görüldüğü üzere Ado.Net Data Services v1.5 CTP2 ile gelen Projection özelliği performans kazanımı elde etmemizi sağlayacak derecede önemli bir özellik olarak karşımıza çıkmaktadır. Bu yazımızda kullandığımız sorgular aşağıdaki gibidir.

  • http://localhost:1714/AdventureServices.svc/Product?$select=ProductID,Name,ListPrice ->(Product kümesinden ProductID, Name ve ListPrice alanları alınır)
  • http://localhost:1714/AdventureServices.svc/ProductSubcategory?$select=Name,Product -> (ProductSubcategory kümesinden Name alınır, her bir alt kategoriye bağlı Product kümelerinin sadece linkleri getirilir.)
  • http://localhost:1714/AdventureServices.svc/ProductSubcategory?$select=Name,Product&$expand=Product&$top=2 -> (Bir önceki sorgu değerlendirilir ama Product kümesinin tüm üyeleri ve sadece ilk iki alt kategori tipi çekilir)
  • http://localhost:1714/AdventureServices.svc/ProductSubcategory?$select=Name,Product/Name,Product/ListPrice&$expand=Product&$top=5 -> (Bir önceki sorgu çalışır ancak Product kümesinden sadece Name ve ListPrice alanları hesaba katılır. Alt kategorilerinde ilk 10 adedi getirilir.)

Bakalım Ado.Net Data Services 1.5 CTP2 tarafında bizleri başka ne gibi sürprizler beklemekte. Bu konularıda ilerleyen yazılarımızda değerlendirmeye çalışıyor olacağız. Tekrardan görüşünceye dek hepinize mutlu günler dilerim .

Projections.rar (53,71 kb)