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

Silverlight - JSON ile Çalışmak

Cuma, 13 Ağustos 2010 10:15 by bsenyurt

Merhaba Arkadaşlar,

Uzun süredir şöyle deliksiz uyuyamıyordum. Malum evde bir afacan var. Pek uyumayı sevmeyen, sürekli hareket halinde olmak isteyen Sarp Efe izin verdiğinde, eşim ve ben dinlenmek için çeşitli işlere dalıyoruz. Ben uzun süredir Bulmacalara takılmış durumdayım. Bir de şu eski dil karşılıklarını isteyen sorular olmasa. Geçtiğimiz günlerde yine böyle bir boşluk yakalamışken, kendimi bulmacalar arasında yüzerken buluverdim. Ancak bir süre sonra "...eski dildeki karşılığı..." sorularından sıkıldım ve televizyonda neler olduğuna bir akayım dedim.

Televizyonda yandaki resimde görülen adam vardı ve ismi Jason' dı. Açıkçası Jason Statham' ın fanatiği bir sinemasever olarak bu isim benzerliğinin, böyle korkutucu bir karakter üzerinde olması beni üzmüştü. Nitekim Jason ismini düşününce aklıma gıcır gıcır parlayan Audi marka arabalar gelmekteydi. Her neyse...Filme fazla takılmadım ama Jason, Jason derken, bu isim JSON diye dudaklarımdan süzülmeye başladı. Pek tabi bunun doğal sonucu olarak bilgisayarımın başına oturdum ve JSON ile ilişkili bir şeyler yazmaya karar verdim. İşte başlıyoruz Wink

Bildiğiniz üzere HTTP bazlı WCF servislerinden(WCF WebHttp Services - JSON Formatlı Response Üretmek) JSON(JavaScript Object Notation) formatında çıktılar yayınlanabilmektedir. Bazı durumlarda istemci tarafı, JSON veri içeriği ile çalışmayı tercih edilebilir. Özellikle XML ile karşılaştırıldığında, JSON formatının daha az yer tutan bir yapıya sahip olması, bu seçimin yapılmasında önemli bir etkendir. Biz bu yazımızda bir WCF WebHttp Service tarafından yayınlanan JSON formatlı veri çıktısının, örnek bir Silverlight istemcisi tarafından nasıl ele alınabileceğini incelemeye çalışıyor olacağız.

Silverlight tarafında JSON içeriği ile çalışabilmek adına geliştirilmiş JsonArray, JsonObject, JsonPrimitive gibi tipler bulunmaktadır. Bu tipler sayesinde JSON veri kümesinde yer alan string, number, Boolean gibi veri türleri kod içerisinde ele alınabilir. Ayrıca tek JSON nesnesi veya bir JSON nesne listesinin ele alınması da sağlanabilir. Bu geliştiriciler için önemlidir. Nitekim Web ortamında gelen JSON içeriğinin Parse edilme işlemleri ile uğraşılmasına gerek kalmamaktadır.

Dilerseniz hiç vakit kaybetmeden örnek bir Silverlight uygulaması üzerinden ilerlemeye çalışalım. İşe ilk olarak IIS üzerinde host edeceğimiz WCF Rest Service Application projesini ve aşağıdaki kod içeriğine sahip LogService servis örneğini geliştirerek başlayabiliriz.

using System.Collections.Generic;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.ServiceModel.Web;

namespace TraceLogServiceApplication
{
    [ServiceContract(Namespace = "")]
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    [ServiceBehavior(InstanceContextMode=InstanceContextMode.PerCall)]
    public class LogService
    {
        [WebGet(UriTemplate = "Logs/All",ResponseFormat=WebMessageFormat.Json)]
        public List<Log> GetAllLogs()
        {
            return new List<Log>()
            {
                new Log{ Source="Sql Server", Content="Sql servisi başlatıldı", IsCritical=false, Level=5},
                new Log{ Source="Sql Server", Content="Sql Agent servisinde hata.", IsCritical=true, Level=1},
                new Log{ Source="DTC", Content="Dağıtık Transaction nesnesi üretildi.", IsCritical=false, Level=3},
                new Log{ Source="WF Runtime", Content="Süreç persist edildi", IsCritical=true, Level=2}
            };
        }
    }

    public class Log
    {
        public string Source { get; set; }
        public string Content { get; set; }
        public int Level { get; set; }
        public bool IsCritical { get; set; }
    }
}

LogService içerisinde yer alan GetAllLogs isimli servis operasyonu Log tipinden bir kaç eleman içeren basit bir List<Log> koleksiyonunu geriye döndürmektedir. Çalışma zamanında oluşturulacak olan bu içerik, istemci tarafına JSON formatında gönderilecektir. Bunun için dikkat edileceği üzere ResponseFormat özelliğinin değeri WebMessageFormat.Json sabiti olarak belirlenmiştir. Servisimizi bu haliyle test etmek istediğimizde adres satırından http://localhost:12043/LogService/Logs/All gibi bir çağrı yapmamız yeterli olacaktır. Bunun sonucunda aşağıdaki JSON içeriği üretilecektir.

[{"Content":"Sql servisi başlatıldı","IsCritical":false,"Level":5,"Source":"Sql Server"},{"Content":"Sql Agent servisinde hata.","IsCritical":true,"Level":1,"Source":"Sql Server"},{"Content":"Dağıtık Transaction nesnesi üretildi.","IsCritical":false,"Level":3,"Source":"DTC"},{"Content":"Süreç persist edildi","IsCritical":true,"Level":2,"Source":"WF Runtime"}]

Bu işlemin ardından servisi IIS alınta Publish etmemiz yeterlidir. Publish ayarlarını aşağıdaki resimde görüldüğü gibi belirleyebiliriz.

Eğer Publish işlemi başarılı olduysa(IIS üzerinden ilgili uygulamanın Web Application olarak set edilmesine-Convert to Application seçeneği dikkat ederekten) herhangibir tarayıcı uygulamadan, http://localhost/TraceLogServiceApplication/LogService/Logs/All şeklinde bir çağrıda bulunabiliyor olmamız gerekmektedir ki bu çağrının sonucu olarakta, yukarıdaki JSON içeriğine tekrardan ulaşabiliyor olmalıyız.

TraceLogServiceApplication.rar (30,47 kb) [Örnek Visual Studio 2010 Ultimate sürümü üzerinde test edilmiştir]

Tabi yapmamız gereken bir işlem daha bulunmaktadır. Hatırlayacağınız üzere Silverlight istemcileri için Cross-Domain Policy sorunsalı mevcuttur. Bu nedenle IIS üzerinde daha önceki yazılarda değindiğimiz ClientAccessPolicy.xml dosyasının içeriğini aşağıdaki gibi düzenlememiz ve TraceLogServiceApplication için gerekli garanti haklarını(grant-to) belirlememiz gerekmektedir.

Artık Silverlight 4.0 tabanlı istemci uygulamamızı geliştirmeye başlayabiliriz. Bu amaçla, JsonConsumer isimli Silverlight uygulamamız içerisindeki MaingPage.xaml ve kod içerikleri aşağıdaki gibi geliştirilebilir.

MainPage.xaml içeriği;

<UserControl x:Class="JsonConsumer.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="300" d:DesignWidth="400" xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk">

    <Grid x:Name="LayoutRoot" Background="White">
        <Button Content="Get All Logs" Height="23" HorizontalAlignment="Left" Margin="24,20,0,0" Name="GetLogsButton" VerticalAlignment="Top" Width="75" Click="GetLogsButton_Click" />
        <sdk:DataGrid AutoGenerateColumns="True" ItemsSource="{Binding}" Height="204" HorizontalAlignment="Left" Margin="24,56,0,0" Name="LogsDataGrid" VerticalAlignment="Top" Width="347"/>
    </Grid>
</UserControl>

MainPage.xaml.cs içeriği;

using System;
using System.IO;
using System.Json;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;

// JsonArray, JsonObject gibi tiplerin kullanılabilmesi için Silverlight projesine System.Json.dll assembly' ının referans edilmesi gerekmektedir.

namespace JsonConsumer
{
    public partial class MainPage
        : UserControl
    {
        WebClient client = null;

        public MainPage()
        {
            InitializeComponent();

            client= new WebClient();
            client.OpenReadCompleted += new OpenReadCompletedEventHandler(client_OpenReadCompleted);
        }

        private void GetLogsButton_Click(object sender, RoutedEventArgs e)
        {
            client.OpenReadAsync(new Uri("http://localhost/TraceLogServiceApplication/LogService/Logs/All")); 
        }

        void client_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
        {
            Stream responseStream = e.Result;
            // JsonArray sınıfının static Load metodu, Http Web servisine yapılan talep sonrası dönen Stream örneğini alır.
            // Load metodu JsonValue tipinden bir referans döndürmektedir ve dizi olarak ele alabilmek için JsonArray tipine bilinçli bir dönüşüm yapılmıştır.
            JsonArray logs=(JsonArray)JsonArray.Load(responseStream);
            // Elde edilen JSON verisinden IsCritical değeri true olanlar çekilir ve LogInfo isimli tip içerisinde toplanır.
            var criticialLogs = from log in logs
                                where log["IsCritical"]
                                select new LogInfo
                                {
                                     Content=log["Content"].ToString(),
                                     Source=log["Source"].ToString(),
                                     Level=log["Level"]
                                };

            // Elde edilen veri kümesi DataGrid kontrolüne veri kaynağı olarak gösterilir
            LogsDataGrid.DataContext = criticialLogs;
        }
    }
    // Servis tarafındaki Log tipinin istemci tarafındaki karşılığı
    public class LogInfo
    {
        public string Source { get; set; }
        public string Content { get; set; }
        public int Level { get; set; }
        public bool IsCritical { get; set; }
    }
}

Hatırlayacağınız üzere WCF WebHttp Service örneklerine yapılacak olan istemci çağrıları için WebClient tipinden yararlanılmaktadır. Bu amaçla Button kontrolüne basıldığında, asenkron olarak söz konusu servise bir talepte bulunulmaktadır(OpenReadAsync). Talep sonuçlandığında ise geri bildirim olay metodu devreye girmektedir(OpenReadCompleted). İşte bu olay metodu içerisinde JSON veri içeriğinin ele alınması için gerekli işlemler gerçekleştirilmektedir.

Bu metoda ait kod parçasındaki en büyük yardımıcı JsonArray tipi ve Load fonksiyonudur . Bu fonksiyon, parametre olarak LogService isimli WCF WebHttp Servisine gönderilen talep sonucu, istemci tarafına indirilen Stream referansını kullanmaktadır. Sonuç daha sonradan basit bir LINQ sorgusu ile değerlendirilmiş ve örnek olarak kritik seviyedeki log bilgilerinin değerlendirilmesi amaçlanmıştır. Uygulamanın çalışma zamanı görüntüsü aşağıdaki gibi olacaktır.

Görüldüğü gibi JSON formatındaki içerik Silverlight tarafında başarılı bir şekilde ele alınmış ve veri bağlı bir kontrol(DataGrid) ile ilişkilendirilebilmiştir. Böylece geldik bir yazımızın daha sonuna. Tekrardan görüşünceye dek hepinize mutlu günler dilerim.

JsonConsumer.rar (1,92 mb) [Örnek Visual Studio 2010 Ultimate sürümü üzerinde test edilmiştir]

Silverlight Tarafından Feed Okumak

Perşembe, 22 Temmuz 2010 18:05 by bsenyurt

Merhaba Arkadaşlar,

Yeni bir maceraya hazır mısınız? Hureyyy dediğinizi duyar gibiyim. Bildiğiniz üzere Internet kaynaklarının takibinin kolay bir şekilde yapılabilmesi adına RSS veya Atom formatındaki Feed içeriklerinden sıklıkla yararlanmaktayız. Blog, Community, News Group ve benzeri pek çok internet kaynağı, güncel içeriklerini yayınlamak amacıyla global olarak standart hale getirilmiş olan bu formatları kullanmaktalar. Pek tabi yayınlanan bu içeriklerin takip edilebilmesi içinde çeşitli istemci programlar söz konusu. FeedReader bu uygulamalara örnek olarak verilebilecek Windows tabanlı iddialı programlardan birisi. Feed içerikleri zaman zaman internet siteleri üzerinde kontrol şeklinde de barındırılmaktadır. Söz gelimi pek çok blog içerisinde bu durum söz konusudur ve hatta hazır Widget' lar yardımıyla entegrasyonları son derece kolaydır. Peki maceramız nerede başlıyor? Özellikle ambulans resminin bu konu ile alakası nedir? Sealed

Doğruyu söylemek gerekirse sıkıldığım bir ara ne yapayım diye düşünürken Silverlight 4.0 tabanlı olarak geliştirilen bir uygulamadan RSS içeriklerini nasıl okuyabileceğimi düşünmeye başladım. Daha önceden HTTP bazlı Get,Post,Put, Delete metodlarınaa cevap veren WCF tabanlı servislerin tüketilmesi için WebClient tipinden nasıl yararlanıldığını incelemiştim(Silverlight Tarafında HTTP Bazli Servisleri Kullanmak isimli yazıyı incelemenizi öneririm) Yine aynı şekilde devam ederek herhangibir RSS içeriğini örnek Silverlight uygulamama taşıyabileceğimi düşünerek kolları sıvadım ve heyecanlı bir şekilde aşağıdaki ekran görüntüsü ve XAML içeriğine sahip kontrolü oluşturdum.

XAML içeriği;

<UserControl x:Class="RSSReaderim.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="337" d:DesignWidth="394" xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk">

    <Grid x:Name="LayoutRoot" Background="White">
        <Button Content="RSS Oku" Height="23" HorizontalAlignment="Left" Margin="313,76,0,0" Name="ReadRSSButton" VerticalAlignment="Top" Width="75" Click="ReadRSSButton_Click" />
        <sdk:Label Height="29" HorizontalAlignment="Left" Margin="8,12,0,0" Name="label1" VerticalAlignment="Top" Width="69" Content="RSS Adresi" FontSize="10" />
        <ListBox Height="180" HorizontalAlignment="Left" Margin="6,105,0,0" Name="RSSListBox" VerticalAlignment="Top" Width="382" ItemsSource="{Binding}">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Title.Text}" Foreground="BlueViolet" />
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
        <TextBox Height="23" HorizontalAlignment="Left" Margin="8,47,0,0" Name="RSSTextBox" VerticalAlignment="Top" Width="380" />
        <sdk:Label Height="40" HorizontalAlignment="Left" Margin="8,297,0,0" Name="RSSInfoLabel" VerticalAlignment="Top" Width="380" FontSize="9" />
    </Grid>
</UserControl>

Aslında teori son derece basitti. Kullanıcı TextBox kontrolü üzerinden bir RSS adresi girecekti. Sonra düğmeye basarak içeriğin ListBox kontrolüne dolmasını seyredecekti. Son derece basit ve masumane bir talep öyle değil mi? Undecided Tabi bu işlemler için kod tarafını da, heyecanlı bir şekilde aşağıdaki gibi geliştirmeye çalıştım.

using System;
using System.Net;
using System.ServiceModel.Syndication;
using System.Windows;
using System.Windows.Controls;
using System.Xml;

namespace RSSReaderim
{
    public partial class MainPage : UserControl
    {
        WebClient client;

        public MainPage()
        {
            InitializeComponent();
            // WebClient nesnesi örneklenir
            client = new WebClient();
            // RSS Adresinden okuma işlemi tamamlanınca devreye girecek olan olay metodu yüklenir
            client.OpenReadCompleted += new OpenReadCompletedEventHandler(client_OpenReadCompleted);
        }

        void client_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
        {
            if (e.Error == null) // Eğer okuma işlemi sırasında bir hata oluşmadıysa
            {
                // RSS bilgisi e.Result üzerinden Stream şeklinde elde edilir ve XmlReader nesnesinin örneklenmesi için kullanılır. Bu gereklidir nitekim SyndicationFeed.Load metodu XmlReader tipi ile çalışmaktadır.
                XmlReader xReader = XmlReader.Create(e.Result);
                // System.ServiceModel.Syndication.dll Assembly' ının projeye referans edilmesi gerekmektedir.
                SyndicationFeed feed = SyndicationFeed.Load(xReader);
                // Items koleksiyonu ListBox bileşenine veri kaynağı olarak bağlanır
                RSSListBox.ItemsSource = feed.Items;
            }
            else if(e.Error!=null)
            {
                // Bir hata oluştuysa istisna mesajını Label kontrolünde göster
                RSSInfoLabel.Content = String.Format("Bir Sorun oluştu. {0}", e.Error);
            }
        }

        private void ReadRSSButton_Click(object sender, RoutedEventArgs e)
        {           
            // Okuma işlemini başlat.
            // Örnek RSS Adresi : http://www.buraksenyurt.com/syndication.axd?format=rss
            if (!String.IsNullOrEmpty(RSSTextBox.Text))
                client.OpenReadAsync(new Uri(RSSTextBox.Text));
            else
                RSSInfoLabel.Content = "Lütfen bir RSS Adresi giriniz";
        }
    }
}

Kod parçasından da görüldüğü üzere WebClient tipini kullanarak TextBox kontrolüne girilen adres için bir talepte bulunulmaktadır. Söz konusu talepin sonucu elde edildiğinde devreye giren olay metodu içerisinde ise, öncelikli olarak bir hata kontrolü yapılmaktadır. Eğer herhangibir hata söz konusu değilse SyndicationFeed tipinden yararlanılarak elde edilen Stream referansının Feed olarak ele alınabilmesi amacıyla gerekli işlemler yapılmaktadır. Son olarak söz konusu içerik nesnesi üzerinden ulaşılan Items koleksiyonu, ListBox kontrolüne bağlanır.

Şimdi blog girdimizin başında yer alan resmi açıklayalım. Bu kadar süratli araba kullanırsanız duvara toslamanız an meselesi olabilir. Aynen örneğimizde şu an tosladığımız gibi Undecided İşte duvara tosladığımız anda saniyenin milyonda birinde şişen hava yastığı içinden fırlayan Exception mesajımız.

Hayda breeeee!!! Surprised İşte hızlı gitmenin doğal sonucu.

Aslında gözden kaçırdığımız çok önemli bir durum söz konusu. O da Silverlight tarafında önem arz eden konuların başında gelen Cross-Domain Policy vakası. Sonuç itibariyle RSS çıktısı için talepte bulunduğumuz Domain adresi ile örneği geliştirmekte olduğumuz Asp.Net Development Server' ın port numarası eşliğine açtığı Domain adresleri birbirlerinden farklı. Bu sebepten sunucu tarafının bir ClientAccessPolicy.xml dosyasına sahip olması ve içerisinde söz konusu talepler için gerekli garanti haklarını belirtmiş olması şart. Ancak bu senaryoya göre Silverlight istemcileri için Cross-Domain Policy desteği vermeyen hiç bir sunucudan RSS içeriğini okumamız mümkün değil. Peki öyleyse ne yapacağız? Çözüm olarak biraz dolambaçlı bir yol olsa da, aşağıdaki şekilde görülen planı izleyebiliriz.

Biliyoruz ki, Silverlight uygulamaları Asp.Net gibi Web uygulamaları içerisinde host edilebilmektedir. Planımıza göre Cross-Domain Policy sorunu ile karşılaşmayacak olan WCF Service' lerinin, Silverlight istemcilerinin talep edeceği RSS içeriklerini ele alması söz konusudur. Buna göre Silverlight istemcileri, RSS çıktılarına doğrudan talepte bulunmak yerine söz konusu taleplerini önce arada Proxy görevini üstlenen bir WCF servisine iletecektir. Bu WCF servisi, ilgili adres bilgisini alarak Feed çıktısını talep edecek ve elde ettiği içeriği tekrardan Silverlight tarafına gönderecektir. Şekildeki plana göre 1 ve 2 numaralı iki adet WCF servisi söz konusudur. Bunlardan hangisinin seçileceği tamamen tercihe bağlıdır. İstersek Silverlight uygulaması ile aynı Domain içerisinde yer alan bir WCF servisini, istersek IIS üzerinde konuşlandırılan ayrı bir WCF servisini kullanabiliriz. Tabi IIS üzerinde host edilen bir WCF Servisi söz konusu ise, Silverlight istemcisi ile olan iletişiminin güvenlik sorununa takılmaması için ClientAccessPolicy.xml kullanılması gerekecektir. Tercih tamamen geliştiriciye bağlıdır. Ancak aynı Web sunucusu üzerinde yer alan birden fazla Silverlight istemcisi söz konusu ise ilgili servisin IIS altında konuşlandırılması daha çok tercih edilebilir.

Not : Bu noktada hazır olarak Siverlight istemcilerine hizmette bulunabilen Feed servislerinden de yararlanabileceğimizi belirtmek isterim. Reading data and RSS with Silverlight and no cross-domain policy başlıklı yazıda Tim Heuer söz konusu servislerden bahsetmektedir.

Ben örneğimizde hız kesemeden devam edebilmek adına, aynı uygulamaya Silverlight destekli bir WCF servisini ekleyerek ilerlemeyi tercih ettim. İşte FeedReaderService isimli Silverlight servisinin kod içeriği.

Not : Silverlight destekli WCF Servicelerinin nasıl geliştirileceğini Screencast - Silverlight Enabled WCF Services isimli görsel dersten takip edebilirsiniz.

using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.ServiceModel.Syndication;
using System.Web.Services.Protocols;
using System.Xml;

namespace RSSReaderim.Web
{
    [ServiceContract(Namespace = "")]
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    public class FeedReaderService
    {
        [OperationContract]
        public List<SyndItem> ReadRss(string address)
        {
            List<SyndItem> feedItems = null;          
            try
            {
                // XmlReader.Create metodu parametre olarak address bilgisini almaktadır. Elde edilen Xml içeriği Load metodu yardımıyla çekilir ve SyndicationItem tipinden olan Items koleksiyonu çekilir.               
                var syndicationItems =SyndicationFeed.Load(XmlReader.Create(address)).Items;
                // SyndicationItem örneklerinin her biri ele alınıp yeni bir SyndItem örneklenmesinde kullanılır.
                feedItems = (from syndicationItem in syndicationItems
                             select new SyndItem
                             {
                                 Title = syndicationItem.Title.Text,
                                 PublishDate = syndicationItem.PublishDate.DateTime,
                                 Summary = syndicationItem.Summary.Text,
                                 Link=syndicationItem.Links[0].Uri
                             }
                           ).ToList();
            }
            catch(Exception excp)
            {
                throw new SoapException("Bir hata oluştu", new XmlQualifiedName("RssReadError"), excp);
            }
            return feedItems;
        }
    }

    // SyndicationItem tipi serileştirme sorununa neden olduğundan araya bir Surrogate tip alınmıştır. Bu tip içerisinde Silverlight tarafı için gerekli temel Feed bilgileri yer almaktadır.
    public class SyndItem
    {
        public string Title { get; set; }
        public DateTime PublishDate { get; set; }
        public string Summary { get; set; }
        public Uri Link { get; set; }
        //TODO: Diğer bilgilerde getirilmelidir. Örneği yazar bilgisi, son güncellenme tarihi veya kategoriler.
    }
}

Servis kodunda dikkat edilmesi gereken en önemli noktalardan birisi, ReadRss metodunun geriye SyndItem tipinden generic bir List koleksiyonu döndürmesidir. Bu noktada akla şu soru gelebilir. Neden List<SyndicationItem> gibi bir koleksiyon döndürmüyoruz? Wink Aslında buradaki sorun SyndicationItem tipinin serileştirme işlemi sırasında çalışma zamanı hatasına neden olmasıdır. Serileştirmedeki bu sıkıntı bizi alternatif bir yola itmiştir. Bu sebepten örnekte bir Surrogate tip kullanılmaktadır. Bu işlemin ardından artık Silverlight tarafı için gerekli geliştirmeler yapılabilir. İlk etapta aynı Domain içerisindeki(bir başka deyişle aynı Solution içerisindeki) WCF Servisinin Silverlight projesine eklenmesi gerekmektedir.

Sonrasında ise istemci tarafı için gerekli kodlar yazılabilir. Yeni örnekte XAML içeriği de aşağıdaki gibi düzenlenmiştir.

<UserControl x:Class="RSSReaderim.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="337" d:DesignWidth="394" xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk">

    <Grid x:Name="LayoutRoot" Background="White">
        <Button Content="RSS Oku" Height="23" HorizontalAlignment="Left" Margin="313,76,0,0" Name="ReadRSSButton" VerticalAlignment="Top" Width="75" Click="ReadRSSButton_Click" />
        <sdk:Label Height="29" HorizontalAlignment="Left" Margin="8,12,0,0" Name="label1" VerticalAlignment="Top" Width="69" Content="RSS Adresi" FontSize="10" />
        <ListBox Height="180" HorizontalAlignment="Left" Margin="6,105,0,0" Name="RSSListBox" VerticalAlignment="Top" Width="382" ItemsSource="{Binding}">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Vertical" Margin="2" Background="Black">
                        <TextBlock Text="{Binding Title}" Foreground="Gold" />
                        <TextBlock Text="{Binding Link}" Foreground="LightCyan" />
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
        <TextBox Height="23" HorizontalAlignment="Left" Margin="8,47,0,0" Name="RSSTextBox" VerticalAlignment="Top" Width="380" />
        <sdk:Label Height="40" HorizontalAlignment="Left" Margin="8,297,0,0" Name="RSSInfoLabel" VerticalAlignment="Top" Width="380" FontSize="9" />
    </Grid>
</UserControl>

Bu kez ListBox.ItemTemplate içerisinde hem Title hemde Link bilgilerinin gösterilmesi sağlanmıştır. Yeni örnekte SnydItem isimli bir Surrogate tip söz konusu olduğundan ve bu tipin Title özelliği String tipten tanımlandığından, bir önceki XAML kodunda yer alan {Binding Title.Text} eşitlemesi kullanılmamalıdır. Gelelim kodlarımıza;

using System;
using System.Windows;
using System.Windows.Controls;
using RSSReaderim.FeedReaderServiceSpace;

namespace RSSReaderim
{
    public partial class MainPage : UserControl
    {
        FeedReaderServiceClient client = null;
        public MainPage()
        {
            InitializeComponent();
            client = new FeedReaderServiceClient();
            client.ReadRssCompleted += new EventHandler<ReadRssCompletedEventArgs>(client_ReadRssCompleted);
        }

        private void ReadRSSButton_Click(object sender, RoutedEventArgs e)
        {
            client.ReadRssAsync(RSSTextBox.Text);
        }
        void client_ReadRssCompleted(object sender, ReadRssCompletedEventArgs e)
        {
            if (e.Error != null)
            {
                RSSInfoLabel.Content = e.Error;
            }
            else if (e.Cancelled)
            {
                RSSInfoLabel.Content = "İşlem iptal edildi";
            }
            else
            {
                RSSListBox.ItemsSource = e.Result;
            }           
        }
    }
}

Kod içeriğinden de görüldüğü üzere WCF servisine ait Proxy tipinden yararlanılarak Feed içeriğinin asenkron olarak ortama çekilmesi işlemi gerçekleştirilmektedir. İşte örnek çalışma zamanı çıktılarından birisi.

ve diğer bir örnek;

Görüldüğü üzere RSS içerikleri başarılı bir şekilde getirilebilmektedir. Elbetteki örnekte eksik olan bir çok kısım vardır. Söz gelimi RSS ile ilişkili olarak daha çok verinin getirilmesi daha iyi olacaktır. Söz gelimi Feed' in sahibi olan siteye ait bilgiler. Diğer yandan eksik kalan önemli noktalardan biriside ListBox' ta bir öğe seçildiğinde ilgili Feed adresine nasıl gidileceğidir. Sonuç itibariyle Silverlight uygulaması tarayıcı üzerinde çalışmaktadır ve ilgili Feed içeriğinin Content verisinin gösterilmesini herkes isteyecektir. İşte size güzel bir araştırma konusu ve ödev Smile Benden buraya kadar. Bir süre dinlenmeye çalışacağım. Tekrardan görüşünceye dek hepinize mutlu günler dilerim.

RSSReaderim_RTM.rar (1,48 mb)[Örnek Visual Studio 2010 Ultimate RTM sürümünde geliştirilmiş ve test edilmiştir]

Silverlight Tarafında HTTP Bazli Servisleri Kullanmak

Pazartesi, 12 Temmuz 2010 09:55 by bsenyurt

Merhaba Arkadaşlar,

Eğitmenlik yaptığım yıllarda Microsoft' un ders kitaplarında yer alan LAB çalışmalarını mümkün mertebe yapmaya ve yaptırmaya çalışırdım. Hatta çoğu zaman eğitimlere hazırlanırken sık sık bu lab çalışmalarını kendim yapar ve hatta ek ilaveler ile daha da eğlenceli hale getirmeye çalışırdım. Tabi bazen elimizde lab yapacağımız kitaplarımız olmazdı ki o ayrı bir hikaye. Lab çalışmaları öğrencinin adım adım yapması gerekenleri söylerek, konunun en yalın haliyle anlaşılmasını sağlamakta önemli rol oynamaktadır. Lab çalışmalarındakine benzer konu anlatımları benimde özümsediğim ve faydalı bulduğum öğrenme tekniklerinden birisidir. İşte bu yazımızda da bu kültüre uymaya çalışarak ilerlemeye çalışıyor olacağız. Hedefimiz Silverlight uygulamalarından, HTTP tabanlı taleplere göre operasyonel hizmetlerde bulunan servisleri nasıl kullanabileceğimizi, en yalın haliyle görmek. Haydi o zaman lab için gerekli materyalleri değerlendirerek yola koyulalım.

Adım 0 : Mevzumuz

Bilindiği üzere bazı servisler HTTP protokolü üzerinden GET, POST, PUT veya DELETE metod çağrıları ile kullanılabilmektedir. Bu anlamda WCF Eco System içerisinde yer alan WebHTTP servisleri, söz konusu tipteki hizmetleri sunmak üzere WCF alt yapısı üzerine oturmuş bir model sunmaktadır. Çok doğal olarak Silverlight tabanlı istemciler de bu servislerin tüketicileri olabilirler. Bu tip servislerin kullanıldığı senaryolarda istemci tarafında herhangibir Proxy tipi söz konusu olmadığı için, HTTP GET,POST,PUT veya DELETE metodlarının manuel olarak hazırlanması ve gönderilmesi gerekmektedir. Silverlight tarafında bu işlemler için WebClient veya HttpWebRequest tiplerinden yararlanılabilmektedir. Biz bu yazımızda WebClient tipinden yararlanarak, IIS(Internet Information Services) üzerinde konuşlandırılmış basit bir WebHttp Service örneğinin nasıl kullanılabileceğini incelemeye çalışıyor olacağız.

Adım 1 : WCF Rest Application Uygulaması ve Entity Data Model' in Oluşturulması

İşe ilk olarak WCF Rest Service Application şablonunda bir proje oluşturarak başlayabiliriz. Bildiğiniz üzere bu proje şablonu(Project Template) hali hazırda yüklü değilse Online Template' ler arasından install etmeniz gerekmektedir. Söz konusu örnekte Chinook veritabanında yer alan ve çok basit olarak ilerlemek istediğimizden sadece Album tablosunu içeren bir Entity Data Model kullanabiliriz. Aşağıdaki şekilde örneğimizde kullanmakta olduğumuz Entity Data Model yer almaktadır.

 

Adım 2 : WebHttp Service Örneğinin Geliştirilmesi

Entities isimli WCF WebHttp Service sınıfımızın içeriğini ise aşağıdaki gibi düzenlediğimizi düşünebiliriz.

using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.ServiceModel.Web;

namespace ChinookDataPortal
{
    [ServiceContract]
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
    public class Entities
    {
        [WebGet(UriTemplate = "Albums/All")]
        public List<Album> GetAlbums()
        {
            List<Album> albums = null;

            ChinookEntities entities = new ChinookEntities();
            albums=(from albm in entities.Albums
                    orderby albm.Title
                    select albm).ToList();

            return albums;
        }

        [WebGet(UriTemplate="Albums/{firstLetter}")]
        public List<Album> GetAlbumsByFirstLetter(string firstLetter)
        {
            List<Album> albums = null;

            ChinookEntities entities = new ChinookEntities();
            albums = (from albm in entities.Albums
                      where albm.Title.ToLower().StartsWith(firstLetter.ToLower())
                      orderby albm.Title
                      select albm).ToList();

            return albums;
        }
    }
}

Servis tipimiz iki operasyon içermekte olup her ikiside HTTP Get çağrılarına cevap verecek şekilde düzenlenmişlerdir. GetAlbums metoduna yapılan çağrılarda servis URL adresine Albums/All takısı eklenmelidir. Diğer yandan ilk harflerine göre albümleri listeleyen GetAlbumsByFirstLetter metodu, URL adresine Albums/{firstLetter} bilgisinin eklenmesini beklemektedir. Her iki metod ChinookEntities tipini kullanmakta ve basit LINQ sorguları ile sonuç üretmektedir. Servisimizi bu şekilde geliştirdikten sonra IIS altına Publish ederek devam edebiliriz.

Adım 3 : IIS Publish

Publish işlemleri için aşağıdaki şekilde görülen Profile ayarlarını kullanabilirsiniz.

Bu ayarlara göre servisimizin IIS üzerinde yer alan Default Web Site isimli Application Pool altına dağıtılacağı belirtilmiş olunur.

Not: IIS üzerinden Convert To Application işlemini yapmanız gerekebilir.

Sonuç olarak IIS içerisinde aşağıdaki gibi servisin üretilmiş olması gerekmektedir. Bu arada örneği geliştirdiğimiz makinede Windows 7 Enterprise işletim sisteminin ve IIS 7.5.7600.16385 sürümünün olduğunu belirtelim.

Bu noktadan sonra Silverlight uygulamasının geliştirilmesi aşamına geçilecektir. Ancak öncelikle gerekli testleri yapılmasında yarar vardır.

ChinookDataPortal.rar (46,54 kb) [Örnek Visual Studio 2010 Ultimate RC ortamında geliştirilmiş ve test edilmiştir]

Adım 4 : WebHttp Service Test

Silverlight tarafındaki uygulamamızı geliştirmeden önce servisimizi IIS üzerinden test etmemizde ve çalıştığından emin olmamızda yarar olacağı kanısındayım. İlk olarak yardım sayfasına ulaşıp ulaşamadığımızı öğrenelim. Bilindiği üzere WebHttp Service örnekleri aksi belirtilmedikçe hazır bir yardım sayfası sunmaktadır. Bu amaçla tarayıcı uygulamadan http://localhost/ChinookDataPortal/Entities/help şeklinde bir talepte bulunduğumuzda, aşağıdaki ekran çıktısı ile karşılaşmış olmalıyız.

Yardım sayfasının çalışıyor olması dışında servis tarafında yer alan operasyonel metodların da test edilmesinde yarar vardır. Örneğin tüm albümleri elde etmek için http://localhost/ChinookDataPortal/Entities/Albums/All şeklinde talepte bulunduğumuzda, aşağıdaki ekran görüntüsünde yer alan sonuçları elde etmiş olmamız gerekmektedir. Tabi veri içeriklerinde değişiklikler söz konusu olabilir. Ancak XML çıktısının şematik yapısının benzer olması gerekmektedir.

Son olarak örneğin Cake adı ile başlayan albümleri çekmek istediğimizi ve bu amaçla URL satırından http://localhost/ChinookDataPortal/Entities/Albums/Cake şeklinde bir talep gönderdiğimizi düşünelim. Bu durumda ekran çıktısının aşağıdakine benzer olması gerekmektedir.

Eğer bu sonuçları elde edebiliyorsak servisimizin çalıştığını ve Sliverlight tarafı için kullanılabilir olduğunu söyleyebiliriz. Lakin dikkat etmemiz gereken bir nokta daha vardır.

Adım 5 : Client Access Policy Ayarları

Silverlight uygulamamızın farklı bir Domain içerisinde host edilmesine karşılık, IIS üzerinde gerekli Client Access Policy ayarlarının bulunması gerekmektedir. Bu nedenle IIS root klasörü altında yer alması gereken ClientAccessPolicy.xml dosyasının içeriğini aşağıdaki gibi düzenleyebiliriz.

<?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"/>
        <resource path="/ChinookDataPortal" include-subpaths="true"/>
      </grant-to>
    </policy>
  </cross-domain-access>
</access-policy>

Burada görüleceği üzere ChinookDataPortal ve alt yollarına erişim izni verilmiştir. Artık Silverlight tarafını geliştirmeye başlayabiliriz.

Adım 6: Silverlight Application Projesinin Oluşturulması

Bu amaçla Visual Studio 2010 ortamında ConsumingHTTPBasedServices isimi ve Silverlight 4.0 tabanlı bir Application oluşturduğumuzu düşünelim. Söz konusu uygulamada RIA Service kullanılmayacağı için bu seçeneği pasif olarak bırakabiliriz. Bu işlem sonucu oluşturulan MainPage sayfasına ait XAML içeriğini ise aşağıdaki gibi geliştirebiliriz.

Adım 7 : MainPage.Xaml içeriği ve Kodun Yazılması

<UserControl x:Class="ConsumingHTTPBasedServices.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="300" d:DesignWidth="517">

    <Grid x:Name="LayoutRoot" Background="White">
        <ListBox Height="203" HorizontalAlignment="Left" Margin="8,70,0,0" Name="AlbumListBox" VerticalAlignment="Top" Width="497" />
        <StackPanel Height="54" HorizontalAlignment="Left" Margin="9,10,0,0" Name="ButtonsStackPanel" VerticalAlignment="Top" Width="496" Orientation="Horizontal" />
    </Grid>
</UserControl>

MainPage içerisinde yer alan ListBox kontrolü içeriği A' dan Z' ye harfler ile doldurulacaktır. Herhangibir harfe basıldığında, WebHttp Service' imiz için bir HTTP Get talebi oluşturulacak ve sonuçların ListBox içerisinde gösterilmesi sağlanacaktır. Bu amaçla kod içeriğini aşağıdaki gibi geliştirmemiz yeterlidir.

using System;
using System.Linq;
using System.Net;
using System.Windows.Controls;
using System.Xml;
using System.Xml.Linq;

namespace ConsumingHTTPBasedServices
{
    public partial class MainPage
        : UserControl
    {
        // WebHttp Servisine basit HTTP metodları ile talepte bulunabilmemizi sağlayan WebClient nesnesi tanımlanır
        WebClient client;

        public MainPage()
        {
            InitializeComponent();

            // WebClient nesnesi örneklenir
            client=new WebClient();
            // Belirtilen URL adresine yapılan talep sonucu gerçekleşecek okuma işlemi tamamlandığında(bir başka deyişle veri istemci tarafında indirildiğinde) devreye girecek olan olay metodu tanımlanır.
            client.OpenReadCompleted += new OpenReadCompletedEventHandler(client_OpenReadCompleted);

            // A...Z Button üretimleri gerçekleştirilir
            for (int i = 65; i < 91; i++)
            {
                Button btn = new Button();
                btn.Width = 18;
                btn.Height = 18;
                btn.FontSize = 10;
                btn.Content = ((char)i).ToString();
                ButtonsStackPanel.Children.Add(btn);
                // Herhangibir Button tıklandığında
                btn.Click += (o, e) =>
                {                  
                    // Önce WebHttp Service' ne doğur yapılacak HTTP Get talebi için gerekli URI oluşturulur
                    Uri address = new Uri(String.Format("http://localhost/ChinookDataPortal/Entities/Albums/{0}", ((Button)o).Content));
                    // Belirtilen URI talebi asenkron olarak çalışan OpenReadAsycn metodu ile gönderilir
                    client.OpenReadAsync(address);                   
                };
            }
        }

        // URI ile belirtilen adres talebi gerçekleştirilip ilgili veri içeriği istemci tarafına indirildikten sonra devreye giren olay metodudur
        void client_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
        {
            // İçerik bir Stream olarak gelmektedir ve tasarlanan ChinookDataPortal WebHttp Servisi varsayılan olarak XML içerik göndermektedir.
            // Bu sebepten Stream XmlReader ile okunur
            XmlReader xReader = XmlReader.Create(e.Result);
            // XLINQ sorgusunun yapılabilmesi için XElement.Load metodu parametre olarak Stream' i kullanan XmlReader nesne örneğini alır
            XElement xElement = XElement.Load(xReader);
            // XLINQ sorgusu ile Title elementleri çekilir. XName.Get metodunun ikinci parametre XML Namespace' inin adıdır.
            var titles = from x in xElement.Elements().Elements(XName.Get("Title", "http://schemas.datacontract.org/2004/07/ChinookDataPortal"))
                         select x.Value;
            // Çekilen veri içeriği ListBox kontrolünün ItemsSource özelliğine bağlanır
            AlbumListBox.ItemsSource = titles;
        }
    }
}

Not: XElement tiplerini kullanabilmek ve XLINQ sorgularını yazabilmek için, Silverlight uygulamasına(ConsumingHTTPBasedServices.Web uygulamasına değil) System.Xml.Linq.dll Assembly' ının referans edilmesi gerekmektedir.

Adım 8 : Silverlight Uygulamasının Test Edilmesi

Dilerseniz uygulamanın çalışma zamanı sonuçlarına hemen bakalım. Böylece çalışma zamanı testlerini yapmış oluruz. Örneğin A başlıklı Button kontrolüne bastığımızda, aşağıdaki ekran görüntüsündekine benzer sonuçları almış olmalıyız. Yani Title alanındakilerden A harfi ile başlayanların listesinin elde edilebiliyor olması gerekmektedir.

Görüldüğü üzere ListBox içeriği baş harfi A olan albüm adları ile doldurulmuştur. Hemen bu işlemin arkasından örneğin C başlıklı Button kontrolüne basarsak aşağıdaki sonuçlar ile karşılaştığımız görürüz.

Süper değil mi? Wink

Özet

Tabi bu örnekte dikkat edilmesi gereken noktalardan birisi de, istemci tarafında herhangibir Proxy tipinin olmayışıdır. Bunun yerine HTTP Get metodu ile talepte bulunulmuş ve elde edilen Stream üzerindeki XML içeriği değerlendirilmiştir. Diğer yandan çok doğal olarak Servis tarafında kullanılan Entity Data Model içerisindeki tiplerin istemci tarafındaki karşılıkları bulunmamaktadır. Eğer bu tiplerin istemci tarafında ele alınması arzu edilirse açık bir şekilde oluşturulmaları gerekecektir. Tabi böyle bir senaryoda gelen XML veya JSON tipindeki içeriğinde ilgili tiplere dönüştürülmesi gibi bir işlem söz konusu olacaktır.

Ödev Smile

  1. Servisin XML yerine JSON(JavaScript Object Notation) formatında bir çıktı vermesi halinde, Silverlight tarafında gerekli olan kod düzenlemelerini yapınız.
  2. Servis üzerinden HTTP Put metod ile güncelleme işlemi yapabilmenizi sağlayacak bir geliştirmeyi aynı örnek üzerinden yapmaya çalışınız.

Böylece geldik bir yazımızın daha sonuna. Tekrardan görüşünceye dek hepinize mutlu günler dilerim.

ConsumingHTTPBasedServices_RTM.rar (273,75 kb)[Örnek Visual Studio 2010 Ultimate RC Sürümü üzerinde geliştirişmiş ve RTM sürümü üzerinde test edilmiştir]

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 - Entity Framework, WCF RIA Services, Silverlight 4.0

Cuma, 19 Mart 2010 22:56 by bsenyurt

Merhaba Arkadaşlar,

Bildiğiniz üzere 2010 yılı Microsoft geliştiricileri açısından epey bir hareketli başladı. Aslında ayak sesleri 2008 yılındaki Profesyonel Geliştiriciler Konferansında(Microsoft PDC) duyulan pek çok ürünün artık nihai sürümlerinin çıkacağı günlere yaklaşıyoruz. Aldığımız duyumlar Nisan ayı içerisinde .Net Framework 4.0 ve Visual Studio 2010 tarafında Relase sürümlerinin en azından RTM sürümlerinin çıkacağı yönünde. 2008 ve belki de daha öncesinden beri süre gelen zaman içerisinde beni en çok şaşırtan ürünlerinden birisi de Silverlight. Daha dün gibi ilk versiyonunu hatırladığımız ürünün geçtiğimiz günlerde MIX2010 ile birlikte 4.0 sürümünün çıktığı duyruldu. Visual Studio 2010 Beta 2 ile kullanıp bakabildiğimiz ama RC sürümünde kullanılamayan sürüm artık kullanılabilir halde Wink Bunun için Silverlight resmi sitesinden gerekli kurulumları yapmanız yeterli olacaktır. Şu aşamada Visual Studio 2010 RC ve Visual Studio 2008 SP1 sürümlerinde ele alabiliyorıuz.

Silverlight tarafında beni en çok ilgilendiren konuların başında ise sunucu kaynaklarının istemci tarafından kullanılabilmesinde önemli bir rol üstelenen WCF RIA Service' ler gelmekte. Eski adıyla .NET RIA Service' lerin WCF Eco System içerisinde WCF RIA Service olarak anılmaya başlandığını biliyoruz. Daha öncesinde WCF RIA Service' ler ile ilişkili çeşitli blog yazılarım oldu ancak görsel anlatım ne denli güçlü olduğunu hepimiz gayet iyi biliyoruz. İşte bu felsefe ile ve NedirTv? desteğiyle hazırladığımız bu görsel dersimizde WCF RIA Service' lerinin kullanımına dair basit bir Hello World uygulaması geliştiriyor olacağız. Üstelik Domain Service tarafında Entity Framework 4.0 sağlayıcısından yararlanarak veri sunumunu gerçekleştireceğiz. İyi seyirler dilerim.

Boyut : 23.2 Mb

Süre : 15:32

Download Etmek veya İzlemek İçin

SilverlightApplication2.rar (2,37 mb) [Örnek Visual Studio 2010 RC sürümü üzerinde geliştirilmiş ve test edilmiştir]