https://buraksenyurt.com/Burak Selim Şenyurt - Silverlight 4.02017-01-30T14:14:54+00:00Matematik Mühendisi Bir Bilgisayar Programcısının NotlarıBurak Selim SenyurtBlogEngine.Net Syndication Generatorhttps://buraksenyurt.com/opml.axdBurak Selim SenyurtMatematik Mühendisi Bir Bilgisayar Programcısının Notlarıtr-TRBurak Selim Şenyurt0.0000000.000000https://buraksenyurt.com/post/TCP-Bazlc4b1-WCF-Service-ve-Silverlight-IstemcileriTCP Bazlı WCF Service ve Silverlight İstemcileri2011-02-27T16:10:00+00:00bsenyurt<p><a href="https://buraksenyurt.com/pics/blg218_Giris_1.jpg"><img style="display: inline; margin-left: 0px; margin-right: 0px; border-width: 0px;" title="blg218_Giris" src="/pics/blg218_Giris_thumb_1.jpg" alt="blg218_Giris" width="302" height="178" align="right" border="0" /></a> Merhaba Arkadaşlar,</p>
<p>Bir yazar, hazırlayacağı hikaye için çoğu zaman çevrede dolaşıp malzeme toplar. Olayın kahramanlarını tasvir etmek için çevredeki insanları göz önüne alır. Hatta gezdiği yerleri inceler. Bu açıdan bakıldığında iyi yazarların aslında çok iyi birer gözlemci olduğu söylenebilir.</p>
<p>Sonuç olarak yazarın elinde bir senaryo taslağı oluşur. Artık tek yapması gereken sakin bir köşe bulmak ve daktilosunun başına geçerek<em>(ki günümüde büyük bir olasılıkla bu diz üstü bir bilgisayar olacaktır)</em> yazmaya başlamaktır. Çözülmesi en zor olan parçaların başında kitaba bir isim bulmak ve ilk giriş cümlesini yazmak gelmektedir. Her ne kadar bu güne kadar yazılmış bir kitabım olmasa da böyle olduğunu tahmin etmekteyim.</p>
<p>Bugün yazımız içinde elimizde bir takım malzemelerimiz bulunmakta. Bir adet <strong>TCP</strong> bazlı olarak çalışan <strong>WCF(Windows Communication Foundation)</strong> servisi. Bu servisi kullanan <strong>Silverlight 4.0</strong> tabanlı bir istemci. <strong>TCP</strong> bazlı servisimiz son derece zıpkın bir delikanlı aslında. Nitekim şirketin iç ağı üzerinden <strong>Binary</strong> tabanlı mesaj formatını kullandığı için ondan daha hızlısı neredeyse yok gibi. Diğer yandan <strong>Silverlight</strong> istemcimiz son derece yakışıklı ve zengin bir kız<em>(Rich Internet Application)</em>. İşte bu yazımızda bu iki kişiyi buluşturmaya çalışıyor olacağız. Ne varki arada zıpkın delikanlının bir de ablası var ki o da<strong> IIS(Internet Information Services)</strong> mahallesinde oturmayı istemeyen ama hep hayal eden <strong>Self-Hosted </strong>stilde yazılmış bir <strong>Console</strong> uygulaması. Neredeyse içi kap kara olmuş birisi<em>(Ama biz çalışma zamanı ekranında onun içindeki iyiliği beyaza boyayıp çıkartacağız)</em> Bakalım abla, kızın, erkek arkadaşına ulaşmasına izin verecek mi? <img title="Laughing" src="/editors/tiny_mce3/plugins/emotions/img/smiley-laughing.gif" alt="Laughing" border="0" /></p>
<h2><strong>Varsayılan Olarak</strong></h2>
<p>Normal şartlar altında <strong>Silverlight</strong> istemcilerinin genellikle <strong>HTTP</strong> bazlı çalışan <strong>WCF</strong> servislerini kullanması söz konusudur. Hatta<strong> WCF RIA Services</strong>’ ler en sık kullanılanıdır. Ancak <strong>Intranet</strong> tabanlı bir sistemde <strong>TCP</strong> bazlı <strong>WCF</strong> Servisleri de söz konusu olabilir. Dilerseniz olayı örnekleyerek canlandırmaya çalışalım. İlk olarak elimizin altında aşağıdaki gibi bir <strong>WCF Service Library</strong> içeriğinin olduğunu düşünelim.</p>
<p><a href="https://buraksenyurt.com/pics/blg218_ServiceClassDiagram_1.gif"><img style="display: inline; border-width: 0px;" title="blg218_ServiceClassDiagram" src="/pics/blg218_ServiceClassDiagram_thumb_1.gif" alt="blg218_ServiceClassDiagram" width="299" height="308" border="0" /></a></p>
<p>Servis sözleşmemiz oldukça basit bir içeriğe sahip.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System.ServiceModel;
namespace YourMeetingServices
{
[ServiceContract]
public interface IMeetingService
{
[OperationContract]
string FirstHello(string yourName);
}
}</pre>
<p>Sadece bir Merhaba demek için gerekli operasyon tanımını içermekte. Bu operasyon ise aşağıdaki gibi uygulanmakta.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">namespace YourMeetingServices
{
public class MeetingService
: IMeetingService
{
#region IMeetingService Members
public string FirstHello(string yourName)
{
return string.Format("Merhaba {0}", yourName);
}
#endregion
}
}</pre>
<p>Bu servis kütüphanesini referans ederek host eden <strong>Console</strong> uygulamasının içeriği ise aşağıdaki gibi.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System;
using System.ServiceModel;
using YourMeetingServices;
namespace ServerApp
{
class Program
{
static void Main(string[] args)
{
ServiceHost host = new ServiceHost(
typeof(MeetingService)
);
host.Opened += (o, e) =>
{
Console.WriteLine("Servis dinlemede");
};
host.Closed += (o, e) =>
{
Console.WriteLine("Servis kapatıldı");
};
host.Open();
Console.WriteLine("Çıkmak için bir tuşa basınız");
Console.ReadLine();
host.Close();
}
}
}</pre>
<p>Çok doğal olarak config dosyası içeriğinin de aşağıdaki gibi olduğunu söyleyebiliriz.</p>
<pre class="brush:xml;auto-links:false;toolbar:false" contenteditable="false"><?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata/>
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service name="YourMeetingServices.MeetingService">
<endpoint address="" binding="netTcpBinding" contract="YourMeetingServices.IMeetingService"/>
<endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="net.tcp://localhost:8342/MeetingRoom/MeetingService/" />
</baseAddresses>
</host>
</service>
</services>
</system.serviceModel>
</configuration></pre>
<p>Buna göre servisimiz <strong>TCP</strong> bazlı olaraktan <strong>net.tcp://localhost:8342/MeetingRoom/MeetingService/</strong> adresi üzerinden yayın yapmaktadır. Ayrıca <strong>mexTcpBinding</strong> bağlayıcı tipini kullanan ve <strong>Mex</strong> son ekini var olan <strong>Base</strong> <strong>Address</strong> tanımına ekleyerek, <strong>service</strong> <strong>metadata</strong> <strong>publishing</strong> yapan bir <strong>EndPoint’</strong> e de sahiptir.</p>
<p>Buna göre basit bir <strong>Console</strong> istemcisinin söz konusu servisi kullanması için tek yapması gereken, Servis uygulaması çalışırken <strong>Add Service Reference</strong> seçeneğinde aşağıdaki şekilde görülen adres tanımlamasını kullanmak olacaktır.</p>
<p><a href="https://buraksenyurt.com/pics/blg218_AddServiceReference_1.gif"><img style="display: inline; border-width: 0px;" title="blg218_AddServiceReference" src="/pics/blg218_AddServiceReference_thumb_1.gif" alt="blg218_AddServiceReference" width="635" height="514" border="0" /></a></p>
<p>Buna göre sıradan bir istemcinin söz konusu servisi kullanması mümkün ve kolaydır.</p>
<h2><strong>İlk Tanışma</strong></h2>
<p>Ancak söz konusu istemci bir <strong>Silverlight</strong> uygulaması ise biraz daha sıkıntılı bir durumla karşı karşıya olabiliriz. Dilerseniz bu durumu analiz etmek için yukarıda yazmış olduğumuz servisimizi bir <strong>Silverlight</strong> istemcisine referans etmeye çalışalım. Tabiki Sunucu uygulamanın çalışıyor olması gerektiğini hatırlatmayacağım.</p>
<p>Sunucu uygulama çalışıyor ve servis iletişime açık iken, <strong>Silverlight</strong> uygulamasına referans ekleme işlemi başarılı olacaktır. Ancak aşağıdaki şekildeki gibi iki adet <strong>Warning’</strong> in de oluştuğu görülecektir.</p>
<p><a href="https://buraksenyurt.com/pics/blg218_Warnings_1.gif"><img style="display: inline; border-width: 0px;" title="blg218_Warnings" src="/pics/blg218_Warnings_thumb_1.gif" alt="blg218_Warnings" width="501" height="190" border="0" /></a></p>
<p>Üstelik üretilen config dosyası içeriğine bakıldığında aşağıdaki görüntü ile karşılaşılır.</p>
<p><a href="https://buraksenyurt.com/pics/blg218_Config_1.gif"><img style="display: inline; border-width: 0px;" title="blg218_Config" src="/pics/blg218_Config_thumb_1.gif" alt="blg218_Config" width="334" height="83" border="0" /></a></p>
<p>Uppsss!!! İlginç bir durum. Nitekim <strong>Endpoint</strong> üretimlerinin <span style="text-decoration: underline;">yapılmaması</span> bir yana, <strong>WCF</strong> servisi ile olan iletişim için gerekli hiç bir ayar da <span style="text-decoration: underline;">bulunmamaktadır</span>. Buna göre <strong>Abla</strong>’ nın, iletişimi engellediğini ifade edebiliriz.</p>
<h2><strong>İkinci Karşılaşma</strong></h2>
<p>İlk karşılaşmanın başarısızlığı üzerine servis ile ilişkili olarak bir takım ilkelerin uygulanması gerektiğini söyleyebiliriz. Her şeyden önce sunucunun, <strong>Silverlight</strong> istemcilerinin <strong>HTTP</strong> bazlı olarak <strong>80</strong> portu üzerinden erişebilmelerine izin veriyor olması gerektiği ip ucunu verebiliriz. Bir başka deyişle <strong>Cross Domain</strong> için <strong>ClientAccessPolicy.xml </strong>ve doğru içeriğin uygulanması gerekliliği söz konusudur. Bu amaçla servis kütüphanemize aşağıdaki servis sözleşmesini eklediğimizi düşünelim.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System.IO;
using System.ServiceModel;
using System.ServiceModel.Web;
namespace YourMeetingServices
{
[ServiceContract]
public interface ITCPPolicy
{
[OperationContract, WebGet(UriTemplate = "/clientaccesspolicy.xml")]
Stream GetPolicy();
}
}</pre>
<p>Burada <strong>WebGet</strong> niteliğinin de uygulandığı<em>(ki bunun için System.ServiceModel.Web.dll assembly’ ının projeye referans edilmesi gerekir) </em>bir operasyon yer almaktadır. Bu operasyon geriye <strong>Stream</strong> tipi tarafından taşınabilen bir referans döndürmektedir. Söz konusu operasyona ulaşılırken <strong>HTTP Get</strong> metodunda <strong>Clientaccesspolicy.xml</strong> son ekinin kullanılacağı da ifade edilmektedir. Bir başka deyişle bu sözleşme istemci için gerekli olan <strong>ClientAccessPolicy</strong> içeriğini sunacaktır. Çok doğal olarak bu sözleşmenin ilgili tipe uygulanıyor olması gerekmektedir. Aynen aşağıda görüldüğü gibi.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System;
using System.IO;
using System.ServiceModel.Web;
using System.Text;
namespace YourMeetingServices
{
public class MeetingService
: IMeetingService,ITCPPolicy
{
#region IMeetingService Members
public string FirstHello(string yourName)
{
return string.Format("Merhaba {0}", yourName);
}
#endregion
#region ITCPPolicy Members
public Stream GetPolicy()
{
string content = File.ReadAllText(Path.Combine(Environment.CurrentDirectory, "PolicyContent.xml"));
WebOperationContext.Current.OutgoingResponse.ContentType = "application/xml";
return new MemoryStream(Encoding.UTF8.GetBytes(content));
}
#endregion
}
}</pre>
<p>Yapılan bu değişiklikler sonucuda servis kütüphanesinin içeriğinin özetle aşağıdaki şekile görüldüğü gibi olduğunu ifade edebiliriz.</p>
<p><a href="https://buraksenyurt.com/pics/blg218_ClassDiagram2_1.gif"><img style="display: inline; border-width: 0px;" title="blg218_ClassDiagram2" src="/pics/blg218_ClassDiagram2_thumb_1.gif" alt="blg218_ClassDiagram2" width="479" height="314" border="0" /></a></p>
<p><strong>GetPolicy</strong> metodunun uygulanışı içerisindeki en önemli nokta sır gibi duran <strong>content</strong> değişkeninin değeridir. Bu değer içeriği aşağıdaki gibi olan <strong>PolicyContent.xml</strong> dosyasından getirilmektedir. Söz konusu dosyanın <strong>output</strong> klasörü sunucu uygulamaya ait exe çıktısının olduğu yer olarak belirtilmiştir.</p>
<pre class="brush:xml;auto-links:false;toolbar:false" contenteditable="false"><?xml version="1.0" encoding="utf-8"?>
<access-policy>
<cross-domain-access>
<policy>
<allow-from http-request-headers="*">
<domain uri="*"/>
</allow-from>
<grant-to>
<socket-resource port="8342" protocol="tcp" />
</grant-to>
</policy>
</cross-domain-access>
</access-policy></pre>
<p>Dikkat edileceği üzre <strong>TCP</strong> bazlı <strong>8342</strong> numaralı port için garanti verilmiştir. Tabi buna göre sunucu uygulama üzerinde de bir takım değişikliklerin yapılması gerekmektedir. Aslında <strong>App.config</strong> dosyası içerisinde aşağıdaki değişiklikleri yapmamız yeterli olacaktır.</p>
<pre class="brush:xml;auto-links:false;toolbar:false" contenteditable="false"><?xml version="1.0"?>
<configuration>
<system.serviceModel>
<bindings>
<netTcpBinding>
<binding name="TcpBindingConfiguration">
<security mode="None" />
</binding>
</netTcpBinding>
</bindings>
<behaviors>
<endpointBehaviors>
<behavior name="WebBehavior">
<webHttp />
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior name="">
<serviceMetadata />
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service name="YourMeetingServices.MeetingService">
<endpoint address="" binding="netTcpBinding" bindingConfiguration="TcpBindingConfiguration"
name="TcpEndpoint" contract="YourMeetingServices.IMeetingService" />
<endpoint address="mex" binding="mexTcpBinding" name="MexTcpEndpoint"
contract="IMetadataExchange" />
<endpoint address="" behaviorConfiguration="WebBehavior" binding="webHttpBinding"
name="WebHttpEndpoint" contract="YourMeetingServices.ITCPPolicy" />
<host>
<baseAddresses>
<add baseAddress="http://localhost:8080" />
<add baseAddress="net.tcp://localhost:8342/MeetingRoom/MeetingService" />
</baseAddresses>
</host>
</service>
</services>
</system.serviceModel>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/></startup></configuration></pre>
<p>İlk dikkati çeken nokta <strong>WebHttp</strong> davranışını destekleyen <strong>WebHttpBinding</strong> bağlayıcı tipini kullanan ve <strong>ITCPPolicy</strong> sözleşmesini sunan bir <strong>Endpoint’</strong> in eklenmiş olmasıdır. Üstelik bu <strong>Endpoint, </strong> <strong>base </strong>olarak <strong>http://localhost:8080</strong> adresini kullanmaktadır.</p>
<p>Diğer yandan <strong>TCP</strong> Bazlı iletişim sağlayan <strong>Endpoint</strong> için <strong>security</strong> <strong>mode</strong> değeri <strong>none</strong> olarak işaret edilmiştir. Bu durumda <strong>Silverlight</strong> uygulamamızda<strong> net.tcp://localhost/8342/MeetingRoom/MeetingService/mex</strong> adresi üzerinden yapılan referans ekleme işlemi sonrasında, daha önceden aldığımız <strong>Warning</strong> mesajlarının kalktığı ve aşağıdaki istemci <strong>config</strong> içeriğinin oluştuğu görülecektir.</p>
<pre class="brush:xml;auto-links:false;toolbar:false" contenteditable="false"><configuration>
<system.serviceModel>
<bindings>
<customBinding>
<binding name="TcpEndpoint">
<binaryMessageEncoding />
<tcpTransport maxReceivedMessageSize="2147483647" maxBufferSize="2147483647" />
</binding>
</customBinding>
</bindings>
<client>
<endpoint address="net.tcp://localhost:8342/MeetingRoom/MeetingService"
binding="customBinding" bindingConfiguration="TcpEndpoint"
contract="MeetingSpace.IMeetingService" name="TcpEndpoint" />
</client>
</system.serviceModel>
</configuration></pre>
<p>Artık <strong>Silverlight</strong> istemcisi üzerinden bir test gerçekleştirebiliriz. Bu amaçla <strong>MainPage</strong> içeriğini aşağıdaki gibi oluşturduğumuzu düşünelim.</p>
<p><a href="https://buraksenyurt.com/pics/blg218_DesignTime_1.gif"><img style="display: inline; border-width: 0px;" title="blg218_DesignTime" src="/pics/blg218_DesignTime_thumb_1.gif" alt="blg218_DesignTime" width="284" height="182" border="0" /></a></p>
<p>MainPage.xaml.cs</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System;
using System.Windows;
using System.Windows.Controls;
using SilverApp.MeetingSpace;
namespace SilverApp
{
public partial class MainPage : UserControl
{
MeetingServiceClient proxy = null;
public MainPage()
{
InitializeComponent();
proxy = new MeetingServiceClient("TcpEndpoint");
proxy.FirstHelloCompleted += new EventHandler<FirstHelloCompletedEventArgs>(proxy_FirstHelloCompleted);
}
void proxy_FirstHelloCompleted(object sender, FirstHelloCompletedEventArgs e)
{
if (e.Error != null)
lblCallResult.Content = "Bir sorun oluştu";
else if (e.Cancelled)
lblCallResult.Content = "İşlem iptal edildi";
else
lblCallResult.Content = e.Result;
}
private void btnCall_Click(object sender, RoutedEventArgs e)
{
proxy.FirstHelloAsync(txtYourname.Text);
}
}
}</pre>
<p>Görüldüğü üzere, <strong>config</strong> dosyasında <strong>TcpEndpoint</strong> için tanımlanmış olan ayarlara göre ilgili <strong>WCF</strong> servisine asenkron olarak bir çağrı gerçekleştirilmektedir.</p>
<p>Şimdi ilk testimizi yapalım. Önce sunucu uygulamayı ardından da <strong>Silverlight</strong> <strong>Web</strong> uygulamamızı çalıştıralım. İlk etapta her şey güllük gülistanlıktır. Aynen aşağıdaki şekilde görüldüğü gibi.</p>
<p><a href="https://buraksenyurt.com/pics/blg218_FirstRuntime_1.gif"><img style="display: inline; border-width: 0px;" title="blg218_FirstRuntime" src="/pics/blg218_FirstRuntime_thumb_1.gif" alt="blg218_FirstRuntime" width="559" height="348" border="0" /></a></p>
<p>Ancak <strong>Call</strong> başlıklı <strong>Button</strong> kontrolüne bastığımızda aşağıda görülen <strong>çalışma zamanı istisnasını(Runtime Exception) </strong>aldığımızı görürüz.</p>
<p><a href="https://buraksenyurt.com/pics/blg218_FirstRuntimeException_1.gif"><img style="display: inline; border-width: 0px;" title="blg218_FirstRuntimeException" src="/pics/blg218_FirstRuntimeException_thumb_1.gif" alt="blg218_FirstRuntimeException" width="560" height="202" border="0" /></a></p>
<p>Aslında buradaki hata mesajının tam içeriği şöyledir;</p>
<blockquote>
<p>Could not connect to net.tcp://localhost:8342/MeetingRoom/MeetingService. The connection attempt lasted for a time span of 00:00:00.2250225. TCP error code 10013: An attempt was made to access a socket in a way forbidden by its access permissions.. This could be due to attempting to access a service in a cross-domain way while the service is not configured for cross-domain access. You may need to contact the owner of the service to expose a sockets cross-domain policy over HTTP and host the service in the allowed sockets port range 4502-4534.</p>
</blockquote>
<p>Anlaşılacağı üzere bir <strong>Cross Domain Policy</strong> sorunsalı baş göstermiştir gibi durmaktadır.</p>
<h2><strong>Son Karşılaşma</strong></h2>
<p>Aslında Servisin geliştirilme mantığına göre <strong>IIS</strong>’ in olmadığı bir durum simüle edilmektedir. Nitekim <strong>Siverlight</strong> istemcilerinin <strong>TCP</strong> bazlı bir servisi tüketmesi için bu servisin <strong>IIS</strong> üzerinde host edilmesi ve<strong> ClientAccessPolicy.xml</strong> dosyası ile desteklenerek gerekli güvenlik izinlerinin verilmesi yeterlidir. Dolayısıyla bizim geliştirdiğimiz senaryoda <strong>Servis</strong> uygulamasının <strong>IIS</strong> gibi çalıştığı varsayılabilir. Buna göre ilk olarak <strong>HTTP base address</strong> tanımlamasının <strong>http://localhost:80</strong> şeklinde değiştirilmesi düşünülmelidir.</p>
<p>Diğer yandan çalışma zamanındaki hata mesajı <strong>4502</strong> ile <strong>4534</strong> numaralı portlar arasında bir değerin kullanılmasını beklemektedir. Bu sebepten servis tarafındanki <strong>TCP based address</strong> değerinin de örnek olarak <strong>net.tcp://localhost:4505/MeetingRoom/MeetingService</strong> şeklinde değiştirilmesi düşünülebilir. Yani 4502 ile 4534 arasında bir port değeri atanmalıdır. Ancak bu da yeterli olmayacaktır. Nitekim <strong>policy</strong> içeriğini teşkil eden <strong>XML</strong> dosyasında yer alan port numarası da <strong>4505</strong> olarak ayarlanmalıdır. Tüm bu değişiklikler <strong>Silverlight</strong> istemcisine servis referansının yeniden eklenmesini gerektirecektir.</p>
<p><a href="https://buraksenyurt.com/pics/blg218_AddServiceReferenceLast.gif"><img style="display: inline; border-width: 0px;" title="blg218_AddServiceReferenceLast" src="/pics/blg218_AddServiceReferenceLast_thumb.gif" alt="blg218_AddServiceReferenceLast" width="635" height="514" border="0" /></a></p>
<p>Artık tanışmak için son bir deneme yapılabilir. İşte sonuç.</p>
<p><a href="https://buraksenyurt.com/pics/blg218_Final.gif"><img style="display: inline; border-width: 0px;" title="blg218_Final" src="/pics/blg218_Final_thumb.gif" alt="blg218_Final" width="557" height="311" border="0" /></a></p>
<p>Görüldüğü üzere servis tarafındaki metod başarılı bir şekilde çalışmıştır. Peki bu kadar zahmete girmeye gerek var mıdır? Aslında olmadığını söylersem şu anda bana çok kızabileceğinizi düşünüyorum. Ancak var. Nitekim <strong>IIS(Internet Information Services)</strong> üzerinde <strong>WAS(Windows Process Activation Service)</strong> kullanımı sayesinde <strong>host</strong> edebileceğimiz <strong>TCP</strong> bazlı bir servisin, doğru <strong>ClientAccessPolicy.xml</strong> içeriği ile bir <strong>Silverlight</strong> istemcisi tarafından kullanılabilmesi mümkündür. Şu anda umuyorum ki içinizde <strong>@#$½!:=|<></strong> gibi bir şey demiyorsunuzdur <img title="Embarassed" src="/editors/tiny_mce3/plugins/emotions/img/smiley-embarassed.gif" alt="Embarassed" border="0" /> Tabi yapılan örneğe göre kafalarda hale soru işaretleri oluşabilir. Söz gelimi <strong>80</strong> yerine örneğin <strong>4508</strong> numaralı bir port üzerinden iletişim geçerli olsa(<strong>http://localhost:4508</strong> şeklinde) Yine de hatalar ile karşılaşır mıyız acaba? <img title="Wink" src="/editors/tiny_mce3/plugins/emotions/img/smiley-wink.gif" alt="Wink" border="0" /> Bu sorunun araştırılmasını siz değerli okurlarıma bırakıyorum. Böylece geldik bir yazımızın daha sonuna. Tekrardan görüşünceye dek hepinize mutlu günler dilerim.</p>
<p><a href="https://buraksenyurt.com/pics/2010%2f7%2fTCPandSilverlight.rar">TCPandSilverlight.rar (1,80 mb)</a> <strong>[Örnek Visual Studio 2010 Ultimate üzerinde ve Silverlight 4.0 odaklı olarak geliştirilmiştir]</strong></p>2011-02-27T16:10:00+00:00nettcpbindingwcfwcf servicessilverlight 4.0silverlightbsenyurtBugün yazımız içinde elimizde bir takım malzemelerimiz bulunmakta. Bir adet TCP bazlı olarak çalışan WCF(Windows Communication Foundation) servisi. Bu servisi kullanan Silverlight 4.0 tabanlı bir istemci. TCP bazlı servisimiz son derece zıpkın bir delikanlı aslında. Nitekim şirketin iç ağı üzerinden Binary tabanlı mesaj formatını kullandığı için ondan daha hızlısı neredeyse yok gibi.https://buraksenyurt.com/pingback.axdhttps://buraksenyurt.com/post.aspx?id=6cdbd87f-c34f-492c-9796-e71bd8c2c35d0https://buraksenyurt.com/trackback.axd?id=6cdbd87f-c34f-492c-9796-e71bd8c2c35dhttps://buraksenyurt.com/post/TCP-Bazlc4b1-WCF-Service-ve-Silverlight-Istemcileri#commenthttps://buraksenyurt.com/syndication.axd?post=6cdbd87f-c34f-492c-9796-e71bd8c2c35dhttps://buraksenyurt.com/post/Silverlight-JSON-ile-CalismakSilverlight - JSON ile Çalışmak2010-08-13T01:15:00+00:00bsenyurt<p><img style="float: right;" src="/pics/2010%2f4%2fblg177_Giris.jpg" alt="" />Merhaba Arkadaşlar,</p>
<p>Uzun süredir şöyle deliksiz uyuyamıyordum. Malum evde bir afacan var. Pek uyumayı sevmeyen, sürekli hareket halinde olmak isteyen S(h)arp Efe izin verdiğinde, eşim ve ben dinlenmek için çeşitli işlere dalıyoruz. Ben uzun süredir <strong>Bulmacalara </strong>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.</p>
<p>Televizyonda yandaki resimde görülen adam vardı ve ismi <strong>Jason</strong>' dı. Açıkçası <strong>Jason Statham</strong>' ı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 <strong>Audi </strong>marka arabalar gelmekteydi. Her neyse...Filme fazla takılmadım ama <strong>Jason, Jason</strong> derken, bu isim <strong>JSON </strong>diye dudaklarımdan süzülmeye başladı. Pek tabi bunun doğal sonucu olarak bilgisayarımın başına oturdum ve <strong>JSON </strong>ile ilişkili bir şeyler yazmaya karar verdim. İşte başlıyoruz <img title="Wink" src="/editors/tiny_mce3/plugins/emotions/img/smiley-wink.gif" alt="Wink" border="0" /></p>
<p>Bildiğiniz üzere <strong>HTTP </strong>bazlı <strong>WCF </strong>servislerinden(<a class="postheader taggedlink" href="https://buraksenyurt.com/admin/app/editor/post/WCF-WebHttp-Services-JSON-Formatli-Response-Uretmek" target="_blank">WCF WebHttp Services - JSON Formatlı Response Üretmek</a>) <strong>JSON(JavaScript Object Notation) </strong>formatında çıktılar yayınlanabilmektedir. Bazı durumlarda istemci tarafı, <strong>JSON </strong>veri içeriği ile çalışmayı tercih edilebilir. Özellikle <strong>XML </strong>ile karşılaştırıldığında, <strong>JSON</strong> 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 <strong>WCF WebHttp Service </strong>tarafından yayınlanan <strong>JSON </strong>formatlı veri çıktısının, örnek bir <strong>Silverlight </strong>istemcisi tarafından nasıl ele alınabileceğini incelemeye çalışıyor olacağız.</p>
<p>Silverlight tarafında <strong>JSON </strong>içeriği ile çalışabilmek adına geliştirilmiş <strong>JsonArray, JsonObject, JsonPrimitive </strong>gibi tipler bulunmaktadır. Bu tipler sayesinde <strong>JSON </strong>veri kümesinde yer alan <strong>string, number, Boolean </strong>gibi veri türleri kod içerisinde ele alınabilir. Ayrıca tek <strong>JSON </strong>nesnesi veya bir <strong>JSON </strong>nesne listesinin ele alınması da sağlanabilir. Bu geliştiriciler için önemlidir. Nitekim Web ortamında gelen <strong>JSON </strong>içeriğinin <strong>Parse </strong>edilme işlemleri ile uğraşılmasına gerek kalmamaktadır.</p>
<p>Dilerseniz hiç vakit kaybetmeden örnek bir <strong>Silverlight </strong>uygulaması üzerinden ilerlemeye çalışalım. İşe ilk olarak <strong>IIS</strong> üzerinde <strong>host </strong>edeceğimiz<strong> WCF Rest Service Application</strong> projesini ve aşağıdaki kod içeriğine sahip <strong>LogService </strong>servis örneğini geliştirerek başlayabiliriz.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">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; }
}
}</pre>
<p><strong>LogService </strong>içerisinde yer alan <strong>GetAllLogs </strong>isimli servis operasyonu <strong>Log </strong>tipinden bir kaç eleman içeren basit bir <strong>List<Log> </strong>koleksiyonunu geriye döndürmektedir. Çalışma zamanında oluşturulacak olan bu içerik, istemci tarafına <strong>JSON </strong>formatında gönderilecektir. Bunun için dikkat edileceği üzere <strong>ResponseFormat </strong>özelliğinin değeri <strong>WebMessageFormat.Json </strong>sabiti olarak belirlenmiştir. Servisimizi bu haliyle test etmek istediğimizde adres satırından <strong>http://localhost:12043/LogService/Logs/All </strong>gibi bir çağrı yapmamız yeterli olacaktır. Bunun sonucunda aşağıdaki <strong>JSON </strong>içeriği üretilecektir.</p>
<pre class="brush:xml;auto-links:false;toolbar:false" contenteditable="false">[{"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"}]</pre>
<p>Bu işlemin ardından servisi <strong>IIS </strong>alınta <strong>Publish </strong>etmemiz yeterlidir. <strong>Publish </strong>ayarlarını aşağıdaki resimde görüldüğü gibi belirleyebiliriz.</p>
<p><img src="/pics/2010%2f4%2fblg177_PublishProfile.gif" alt="" /></p>
<p>Eğer <strong>Publish </strong>işlemi başarılı olduysa<em>(<strong>IIS </strong>üzerinden ilgili uygulamanın <strong>Web Application </strong>olarak set edilmesine</em><em>-Convert to Application seçeneği dikkat ederekten) </em>herhangibir tarayıcı uygulamadan, <strong>http://localhost/TraceLogServiceApplication/LogService/Logs/All </strong>şeklinde bir çağrıda bulunabiliyor olmamız gerekmektedir ki bu çağrının sonucu olarakta, yukarıdaki <strong>JSON </strong>içeriğine tekrardan ulaşabiliyor olmalıyız.</p>
<p><a href="https://buraksenyurt.com/pics/2010%2f4%2fTraceLogServiceApplication.rar">TraceLogServiceApplication.rar (30,47 kb)</a> <strong>[Örnek Visual Studio 2010 Ultimate sürümü üzerinde test edilmiştir]</strong></p>
<p>Tabi yapmamız gereken bir işlem daha bulunmaktadır. Hatırlayacağınız üzere <strong>Silverlight </strong>istemcileri için <strong>Cross-Domain Policy </strong>sorunsalı mevcuttur. Bu nedenle <strong>IIS </strong>üzerinde daha önceki yazılarda değindiğimiz <strong>ClientAccessPolicy.xml </strong>dosyasının içeriğini aşağıdaki gibi düzenlememiz ve <strong>TraceLogServiceApplication </strong>için gerekli <strong>garanti haklarını(grant-to)</strong> belirlememiz gerekmektedir.</p>
<p><img src="/pics/2010%2f4%2fblg177_CAPFile.gif" alt="" /></p>
<p>Artık <strong>Silverlight 4.0</strong> tabanlı istemci uygulamamızı geliştirmeye başlayabiliriz. Bu amaçla, <strong>JsonConsumer </strong>isimli <strong>Silverlight </strong>uygulamamız içerisindeki <strong>MaingPage.xaml </strong>ve kod içerikleri aşağıdaki gibi geliştirilebilir.</p>
<p><strong>MainPage.xaml içeriği;</strong></p>
<pre class="brush:xml;auto-links:false;toolbar:false" contenteditable="false"><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></pre>
<p><strong>MainPage.xaml.cs içeriği;</strong></p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">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; }
}
}</pre>
<p>Hatırlayacağınız üzere <strong>WCF WebHttp Service</strong> örneklerine yapılacak olan istemci çağrıları için <strong>WebClient </strong>tipinden yararlanılmaktadır. Bu amaçla<strong> Button </strong>kontrolüne basıldığında, asenkron olarak söz konusu servise bir talepte bulunulmaktadır<strong>(OpenReadAsync)</strong>. Talep sonuçlandığında ise geri bildirim olay metodu devreye girmektedir<strong>(OpenReadCompleted)</strong>. İşte bu olay metodu içerisinde <strong>JSON </strong>veri içeriğinin ele alınması için gerekli işlemler gerçekleştirilmektedir.</p>
<p>Bu metoda ait kod parçasındaki en büyük yardımıcı <strong>JsonArray </strong>tipi ve <strong>Load </strong>fonksiyonudur . Bu fonksiyon, parametre olarak <strong>LogService </strong>isimli <strong>WCF WebHttp Servisine </strong>gönderilen talep sonucu, istemci tarafına indirilen <strong>Stream</strong> referansını kullanmaktadır. Sonuç daha sonradan basit bir <strong>LINQ </strong>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.</p>
<p><img src="/pics/2010%2f4%2fblg177_Runtime.gif" alt="" /></p>
<p>Görüldüğü gibi <strong>JSON </strong>formatındaki içerik <strong>Silverlight </strong>tarafında başarılı bir şekilde ele alınmış ve <strong>veri bağlı bir kontrol(DataGrid)</strong> ile ilişkilendirilebilmiştir. Böylece geldik bir yazımızın daha sonuna. Tekrardan görüşünceye dek hepinize mutlu günler dilerim.</p>
<p><a href="https://buraksenyurt.com/pics/2010%2f4%2fJsonConsumer.rar">JsonConsumer.rar (1,92 mb)</a> <strong>[Örnek Visual Studio 2010 Ultimate sürümü üzerinde test edilmiştir]</strong></p>2010-08-13T01:15:00+00:00silverlightwcfwcf webhttp servicesjsonbsenyurtSilverlight 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.https://buraksenyurt.com/pingback.axdhttps://buraksenyurt.com/post.aspx?id=3c19fb81-50f0-4a9c-91ee-e93549217c2f2https://buraksenyurt.com/trackback.axd?id=3c19fb81-50f0-4a9c-91ee-e93549217c2fhttps://buraksenyurt.com/post/Silverlight-JSON-ile-Calismak#commenthttps://buraksenyurt.com/syndication.axd?post=3c19fb81-50f0-4a9c-91ee-e93549217c2fhttps://buraksenyurt.com/post/Silverlight-Tarafindan-Feed-OkumakSilverlight Tarafından Feed Okumak2010-07-22T09:05:00+00:00bsenyurt<p><img style="float: right;" src="/pics/2010%2f4%2fblg176_Giris.jpg" alt="" />Merhaba Arkadaşlar,</p>
<p>Yeni bir maceraya hazır mısınız? Hureyyy dediğinizi duyar gibiyim. Bildiğiniz üzere <strong>Internet </strong>kaynaklarının takibinin kolay bir şekilde yapılabilmesi adına <strong>RSS </strong>veya <strong>Atom </strong>formatındaki <strong>Feed </strong>içeriklerinden sıklıkla yararlanmaktayız. <strong>Blog, Community, News Group </strong>ve benzeri pek çok internet kaynağı, güncel içeriklerini yayınlamak amacıyla global olarak standart hale getirilmiş olan bu formatları kullanmaktalar.</p>
<p>Pek tabi yayınlanan bu içeriklerin takip edilebilmesi içinde çeşitli istemci programlar söz konusu. <strong>FeedReader</strong> bu uygulamalara örnek olarak verilebilecek <strong>Windows </strong>tabanlı iddialı programlardan birisi. <strong>Feed </strong>içerikleri zaman zaman <strong>internet </strong>siteleri üzerinde kontrol şeklinde de barındırılmaktadır. Söz gelimi pek çok <strong>blog </strong>içerisinde bu durum söz konusudur ve hatta hazır <strong>Widget</strong>' lar yardımıyla entegrasyonları son derece kolaydır. Peki maceramız nerede başlıyor? Özellikle ambulans resminin bu konu ile alakası nedir? <img title="Sealed" src="/editors/tiny_mce3/plugins/emotions/img/smiley-sealed.gif" alt="Sealed" border="0" /></p>
<p>Doğruyu söylemek gerekirse sıkıldığım bir ara ne yapayım diye düşünürken <strong>Silverlight 4.0</strong> tabanlı olarak geliştirilen bir uygulamadan <strong>RSS </strong>içeriklerini nasıl okuyabileceğimi düşünmeye başladım. Daha önceden <strong>HTTP </strong>bazlı <strong>Get,Post,Put, Delete</strong> metodlarınaa cevap veren <strong>WCF </strong>tabanlı servislerin tüketilmesi için <strong>WebClient </strong>tipinden nasıl yararlanıldığını incelemiştim(<a class="postheader taggedlink" href="https://buraksenyurt.com/admin/app/editor/post/Silverlight-Tarafinda-HTTP-Bazli-Servisleri-Kullanmak" target="_blank">Silverlight Tarafında HTTP Bazli Servisleri Kullanmak</a> isimli yazıyı incelemenizi öneririm) Yine aynı şekilde devam ederek herhangibir <strong>RSS </strong>içeriğini örnek <strong>Silverlight </strong>uygulamama taşıyabileceğimi düşünerek kolları sıvadım ve heyecanlı bir şekilde aşağıdaki ekran görüntüsü ve <strong>XAML </strong>içeriğine sahip kontrolü oluşturdum.</p>
<p><img src="/pics/2010%2f4%2fblg176_Design.gif" alt="" /></p>
<p>XAML içeriği;</p>
<pre class="brush:xml;auto-links:false;toolbar:false" contenteditable="false"><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></pre>
<p>Aslında teori son derece basitti. Kullanıcı <strong>TextBox </strong>kontrolü üzerinden bir <strong>RSS </strong>adresi girecekti. Sonra düğmeye basarak içeriğin <strong>ListBox </strong>kontrolüne dolmasını seyredecekti. Son derece basit ve masumane bir talep öyle değil mi? <img title="Undecided" src="/editors/tiny_mce3/plugins/emotions/img/smiley-undecided.gif" alt="Undecided" border="0" /> Tabi bu işlemler için kod tarafını da, heyecanlı bir şekilde aşağıdaki gibi geliştirmeye çalıştım.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">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";
}
}
}</pre>
<p>Kod parçasından da görüldüğü üzere <strong>WebClient </strong>tipini kullanarak <strong>TextBox </strong>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 <strong>SyndicationFeed </strong>tipinden yararlanılarak elde edilen <strong>Stream</strong> referansının <strong>Feed </strong>olarak ele alınabilmesi amacıyla gerekli işlemler yapılmaktadır. Son olarak söz konusu içerik nesnesi üzerinden ulaşılan <strong>Items </strong>koleksiyonu, <strong>ListBox </strong>kontrolüne bağlanır.</p>
<p>Şimdi <strong>blog </strong>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 <img title="Undecided" src="/editors/tiny_mce3/plugins/emotions/img/smiley-undecided.gif" alt="Undecided" border="0" /> İşte duvara tosladığımız anda saniyenin milyonda birinde şişen hava yastığı içinden fırlayan <strong>Exception </strong>mesajımız.</p>
<p><img src="/pics/2010%2f4%2fblg176_Exception.gif" alt="" /></p>
<p>Hayda breeeee!!! <img title="Surprised" src="/editors/tiny_mce3/plugins/emotions/img/smiley-surprised.gif" alt="Surprised" border="0" /> İşte hızlı gitmenin doğal sonucu.</p>
<p>Aslında gözden kaçırdığımız çok önemli bir durum söz konusu. O da <strong>Silverlight </strong>tarafında önem arz eden konuların başında gelen<strong> Cross-Domain Policy</strong> vakası. Sonuç itibariyle <strong>RSS </strong>çıktısı için talepte bulunduğumuz <strong>Domain </strong>adresi ile örneği geliştirmekte olduğumuz <strong>Asp.Net Development Server</strong>' ın <strong>port </strong>numarası eşliğine açtığı <strong>Domain</strong> adresleri birbirlerinden farklı. Bu sebepten sunucu tarafının bir <strong>ClientAccessPolicy.xml </strong>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 <strong>Silverlight </strong>istemcileri için <strong>Cross-Domain Policy </strong>desteği vermeyen hiç bir sunucudan <strong>RSS </strong>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.</p>
<p><img src="/pics/2010%2f4%2fblg176_Plan.gif" alt="" /></p>
<p>Biliyoruz ki, <strong>Silverlight </strong>uygulamaları <strong>Asp.Net</strong> gibi Web uygulamaları içerisinde host edilebilmektedir. Planımıza göre<strong> Cross-Domain Policy</strong> sorunu ile karşılaşmayacak olan <strong>WCF Service</strong>' lerinin, <strong>Silverlight </strong>istemcilerinin talep edeceği <strong>RSS </strong>içeriklerini ele alması söz konusudur. Buna göre <strong>Silverlight </strong>istemcileri, <strong>RSS </strong>çıktılarına doğrudan talepte bulunmak yerine söz konusu taleplerini önce arada <strong>Proxy </strong>görevini üstlenen bir <strong>WCF </strong>servisine iletecektir. Bu <strong>WCF </strong>servisi, ilgili adres bilgisini alarak <strong>Feed </strong>çıktısını talep edecek ve elde ettiği içeriği tekrardan <strong>Silverlight </strong>tarafına gönderecektir.</p>
<p>Şekildeki plana göre <strong>1</strong> ve <strong>2 </strong>numaralı iki adet <strong>WCF </strong>servisi söz konusudur. Bunlardan hangisinin seçileceği tamamen tercihe bağlıdır. İstersek <strong>Silverlight </strong>uygulaması ile aynı <strong>Domain </strong>içerisinde yer alan bir <strong>WCF </strong>servisini, istersek <strong>IIS </strong>üzerinde konuşlandırılan ayrı bir <strong>WCF </strong>servisini kullanabiliriz. Tabi <strong>IIS</strong> üzerinde host edilen bir <strong>WCF </strong>Servisi söz konusu ise, <strong>Silverlight </strong>istemcisi ile olan iletişiminin güvenlik sorununa takılmaması için <strong>ClientAccessPolicy.xml </strong>kullanılması gerekecektir. Tercih tamamen geliştiriciye bağlıdır. Ancak aynı Web sunucusu üzerinde yer alan birden fazla <strong>Silverlight </strong>istemcisi söz konusu ise ilgili servisin IIS altında konuşlandırılması daha çok tercih edilebilir.</p>
<blockquote>
<p>Bu noktada hazır olarak Siverlight istemcilerine hizmette bulunabilen Feed servislerinden de yararlanabileceğimizi belirtmek isterim. <a id="viewpost_ascx_TitleUrl" class="title" title="Title of this entry." href="http://timheuer.com/blog/archive/2008/06/03/use-silverlight-with-any-feed-without-cross-domain-files.aspx">Reading data and RSS with Silverlight and no cross-domain policy</a> başlıklı yazıda Tim Heuer söz konusu servislerden bahsetmektedir.</p>
</blockquote>
<p>Ben örneğimizde hız kesemeden devam edebilmek adına, aynı uygulamaya <strong>Silverlight </strong>destekli bir <strong>WCF </strong>servisini ekleyerek ilerlemeyi tercih ettim. İşte <strong>FeedReaderService </strong>isimli <strong>Silverlight </strong>servisinin kod içeriği.</p>
<blockquote>
<p>Silverlight destekli WCF Servicelerinin nasıl geliştirileceğini <a class="postheader taggedlink" href="https://buraksenyurt.com/post/Screencast-Silverlight-Enabled-WCF-Services">Screencast - Silverlight Enabled WCF Services</a> isimli görsel dersten takip edebilirsiniz.</p>
</blockquote>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">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.
}
}</pre>
<p>Servis kodunda dikkat edilmesi gereken en önemli noktalardan birisi, <strong>ReadRss </strong>metodunun geriye <strong>SyndItem </strong>tipinden generic bir <strong>List </strong>koleksiyonu döndürmesidir. Bu noktada akla şu soru gelebilir. Neden <strong>List<SyndicationItem></strong> gibi bir koleksiyon döndürmüyoruz? <img title="Wink" src="/editors/tiny_mce3/plugins/emotions/img/smiley-wink.gif" alt="Wink" border="0" /> Aslında buradaki sorun <strong>SyndicationItem </strong>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 <strong>Surrogate </strong>tip kullanılmaktadır. Bu işlemin ardından artık <strong>Silverlight </strong>tarafı için gerekli geliştirmeler yapılabilir. İlk etapta aynı <strong>Domain </strong>içerisindeki<em>(bir başka deyişle aynı Solution içerisindeki) </em><strong>WCF Servisinin Silverlight </strong>projesine eklenmesi gerekmektedir.</p>
<p><img src="/pics/2010%2f4%2fblg176_AddServiceRef.gif" alt="" /></p>
<p>Sonrasında ise istemci tarafı için gerekli kodlar yazılabilir. Yeni örnekte <strong>XAML </strong>içeriği de aşağıdaki gibi düzenlenmiştir.</p>
<pre class="brush:xml;auto-links:false;toolbar:false" contenteditable="false"><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></pre>
<p>Bu kez <strong>ListBox.ItemTemplate </strong>içerisinde hem <strong>Title </strong>hemde <strong>Link </strong>bilgilerinin gösterilmesi sağlanmıştır. Yeni örnekte <strong>SnydItem </strong>isimli bir <strong>Surrogate </strong>tip söz konusu olduğundan ve bu tipin <strong>Title </strong>özelliği <strong>String </strong>tipten tanımlandığından, bir önceki <strong>XAML</strong> kodunda yer alan <strong>{Binding Title.Text} </strong>eşitlemesi <span style="text-decoration: underline;">kullanılmamalıdır</span>. Gelelim kodlarımıza;</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">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;
}
}
}
}</pre>
<p>Kod içeriğinden de görüldüğü üzere <strong>WCF </strong>servisine ait <strong>Proxy </strong>tipinden yararlanılarak <strong>Feed </strong>içeriğinin asenkron olarak ortama çekilmesi işlemi gerçekleştirilmektedir. İşte örnek çalışma zamanı çıktılarından birisi.</p>
<p><img src="/pics/2010%2f4%2fblg176_Runtime.gif" alt="" /></p>
<p>ve diğer bir örnek;</p>
<p><img src="/pics/2010%2f4%2fblg176_Runtime2.gif" alt="" /></p>
<p>Görüldüğü üzere <strong>RSS </strong>içerikleri başarılı bir şekilde getirilebilmektedir. Elbetteki örnekte eksik olan bir çok kısım vardır. Söz gelimi <strong>RSS </strong>ile ilişkili olarak daha çok verinin getirilmesi daha iyi olacaktır. Söz gelimi <strong>Feed</strong>' in sahibi olan siteye ait bilgiler. Diğer yandan eksik kalan önemli noktalardan biriside <strong>ListBox</strong>' ta bir öğe seçildiğinde ilgili <strong>Feed </strong>adresine nasıl gidileceğidir. Sonuç itibariyle Silverlight uygulaması tarayıcı üzerinde çalışmaktadır ve ilgili <strong>Feed </strong>içeriğinin <strong>Content </strong>verisinin gösterilmesini herkes isteyecektir. İşte size güzel bir araştırma konusu ve ödev <img title="Smile" src="/editors/tiny_mce3/plugins/emotions/img/smiley-smile.gif" alt="Smile" border="0" /> Benden buraya kadar. Bir süre dinlenmeye çalışacağım. Tekrardan görüşünceye dek hepinize mutlu günler dilerim.</p>
<p><a href="https://buraksenyurt.com/pics/2010%2f4%2fRSSReaderim_RTM.rar">RSSReaderim_RTM.rar (1,48 mb)</a><strong>[Örnek Visual Studio 2010 Ultimate RTM sürümünde geliştirilmiş ve test edilmiştir]</strong></p>2010-07-22T09:05:00+00:00silverlightwcfsyndicationwcf eco systembsenyurtBiliyoruz 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.https://buraksenyurt.com/pingback.axdhttps://buraksenyurt.com/post.aspx?id=70a0a3b3-b72a-41b8-afc9-9292e10569240https://buraksenyurt.com/trackback.axd?id=70a0a3b3-b72a-41b8-afc9-9292e1056924https://buraksenyurt.com/post/Silverlight-Tarafindan-Feed-Okumak#commenthttps://buraksenyurt.com/syndication.axd?post=70a0a3b3-b72a-41b8-afc9-9292e1056924https://buraksenyurt.com/post/Silverlight-Tarafinda-HTTP-Bazli-Servisleri-KullanmakSilverlight Tarafında HTTP Bazli Servisleri Kullanmak2010-07-12T00:55:00+00:00bsenyurt<p><img style="float: right;" src="/pics/2010%2f4%2fblg175_Giris.jpg" alt="" />Merhaba Arkadaşlar,</p>
<p>Eğitmenlik yaptığım yıllarda <strong>Microsoft</strong>' un ders kitaplarında yer alan <strong>LAB </strong>ç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.</p>
<p>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 <strong>Silverlight </strong>uygulamalarından, <strong>HTTP </strong>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.</p>
<p><strong>Adım 0 : Mevzumuz</strong></p>
<p>Bilindiği üzere bazı servisler <strong>HTTP </strong>protokolü üzerinden <strong>GET, POST, PUT</strong> veya <strong>DELETE </strong>metod çağrıları ile kullanılabilmektedir. Bu anlamda <strong>WCF Eco System</strong> içerisinde yer alan <a title="WCF WebHttp Services" href="https://buraksenyurt.com/archive.aspx#WCF-WebHttp-Services" target="_blank">WebHTTP servisleri</a>, söz konusu tipteki hizmetleri sunmak üzere WCF alt yapısı üzerine oturmuş bir model sunmaktadır. Çok doğal olarak <strong>Silverlight </strong>tabanlı istemciler de bu servislerin tüketicileri olabilirler. Bu tip servislerin kullanıldığı senaryolarda istemci tarafında herhangibir <strong>Proxy </strong>tipi söz konusu olmadığı için, <strong>HTTP GET,POST,PUT </strong>veya <strong>DELETE </strong>metodlarının manuel olarak hazırlanması ve gönderilmesi gerekmektedir. <strong>Silverlight </strong>tarafında bu işlemler için <strong>WebClient </strong>veya <strong>HttpWebRequest </strong>tiplerinden yararlanılabilmektedir. Biz bu yazımızda <strong>WebClient </strong>tipinden yararlanarak, <strong>IIS(Internet Information Services)</strong> üzerinde konuşlandırılmış basit bir <strong>WebHttp Service</strong> örneğinin nasıl kullanılabileceğini incelemeye çalışıyor olacağız.</p>
<p><strong>Adım 1 : WCF Rest Application Uygulaması ve Entity Data Model' in Oluşturulması<br /></strong></p>
<p>İşe ilk olarak <strong>WCF Rest Service Application</strong> şablonunda bir proje oluşturarak başlayabiliriz. Bildiğiniz üzere bu proje şablonu<strong>(Project Template)</strong> hali hazırda yüklü değilse <strong>Online Template</strong>' ler arasından <strong>install </strong>etmeniz gerekmektedir. Söz konusu örnekte <strong>Chinook </strong>veritabanında yer alan ve çok basit olarak ilerlemek istediğimizden sadece <strong>Album </strong>tablosunu içeren bir <strong>Entity Data Model </strong>kullanabiliriz. Aşağıdaki şekilde örneğimizde kullanmakta olduğumuz Entity Data Model yer almaktadır.</p>
<p><img src="/pics/2010%2f4%2fblg175_Edm.gif" alt="" /></p>
<p> </p>
<p><strong>Adım 2 : WebHttp Service Örneğinin Geliştirilmesi</strong></p>
<p><strong>Entities </strong>isimli <strong>WCF WebHttp Service</strong> sınıfımızın içeriğini ise aşağıdaki gibi düzenlediğimizi düşünebiliriz.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">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;
}
}
}</pre>
<p>Servis tipimiz iki operasyon içermekte olup her ikiside <strong>HTTP Get </strong>çağrılarına cevap verecek şekilde düzenlenmişlerdir. <strong>GetAlbums</strong> metoduna yapılan çağrılarda servis <strong>URL </strong>adresine <strong>Albums/All </strong>takısı eklenmelidir. Diğer yandan ilk harflerine göre albümleri listeleyen <strong>GetAlbumsByFirstLetter </strong>metodu, <strong>URL </strong>adresine <strong>Albums/{firstLetter} </strong>bilgisinin eklenmesini beklemektedir. Her iki metod <strong>ChinookEntities </strong>tipini kullanmakta ve basit <strong>LINQ </strong>sorguları ile sonuç üretmektedir. Servisimizi bu şekilde geliştirdikten sonra IIS altına Publish ederek devam edebiliriz.</p>
<p><strong>Adım 3 : IIS Publish</strong></p>
<p>Publish işlemleri için aşağıdaki şekilde görülen Profile ayarlarını kullanabilirsiniz.</p>
<p><img src="/pics/2010%2f4%2fblg175_PublishProfile.gif" alt="" /></p>
<p>Bu ayarlara göre servisimizin <strong>IIS </strong>üzerinde yer alan <strong>Default Web Site</strong> isimli <strong>Application Pool </strong>altına dağıtılacağı belirtilmiş olunur.</p>
<p><em><strong>Not: IIS </strong>üzerinden <strong>Convert To Application </strong>işlemini yapmanız gerekebilir.</em></p>
<p>Sonuç olarak <strong>IIS </strong>içerisinde aşağıdaki gibi servisin üretilmiş olması gerekmektedir. Bu arada örneği geliştirdiğimiz makinede <strong>Windows 7 Enterprise </strong>işletim sisteminin ve <strong>IIS 7.5.7600.16385 </strong>sürümünün olduğunu belirtelim.</p>
<p><img src="/pics/2010%2f4%2fblg175_IIS.gif" alt="" /></p>
<p>Bu noktadan sonra Silverlight uygulamasının geliştirilmesi aşamına geçilecektir. Ancak öncelikle gerekli testleri yapılmasında yarar vardır.</p>
<p><a href="https://buraksenyurt.com/pics/2010%2f4%2fChinookDataPortal.rar">ChinookDataPortal.rar (46,54 kb)</a><strong> [Örnek Visual Studio 2010 Ultimate RC ortamında geliştirilmiş ve test edilmiştir]</strong></p>
<p><strong>Adım 4 : WebHttp Service Test<br /></strong></p>
<p><strong>Silverlight </strong>tarafındaki uygulamamızı geliştirmeden önce servisimizi <strong>IIS </strong>ü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 <strong>http://localhost/ChinookDataPortal/Entities/help </strong>şeklinde bir talepte bulunduğumuzda, aşağıdaki ekran çıktısı ile karşılaşmış olmalıyız.</p>
<p><img src="/pics/2010%2f4%2fblg175_HelpPage.gif" alt="" /></p>
<p>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<strong> http://localhost/ChinookDataPortal/Entities/Albums/All </strong>ş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.</p>
<p><img src="/pics/2010%2f4%2fblg175_AllAlbums.gif" alt="" /></p>
<p>Son olarak örneğin <strong>Cake </strong>adı ile başlayan albümleri çekmek istediğimizi ve bu amaçla <strong>URL </strong>satırından <strong>http://localhost/ChinookDataPortal/Entities/Albums/Cake </strong>şeklinde bir talep gönderdiğimizi düşünelim. Bu durumda ekran çıktısının aşağıdakine benzer olması gerekmektedir.</p>
<p><img src="/pics/2010%2f4%2fblg175_ByFirstLetter.gif" alt="" /></p>
<p>Eğer bu sonuçları elde edebiliyorsak servisimizin çalıştığını ve <strong>Sliverlight </strong>tarafı için kullanılabilir olduğunu söyleyebiliriz. Lakin dikkat etmemiz gereken bir nokta daha vardır.</p>
<p><strong>Adım 5 : Client Access Policy Ayarları</strong></p>
<p><strong>Silverlight </strong>uygulamamızın farklı bir <strong>Domain </strong>içerisinde <strong>host </strong>edilmesine karşılık, <strong>IIS </strong>üzerinde gerekli<strong> Client Access Policy</strong> ayarlarının bulunması gerekmektedir. Bu nedenle<strong> IIS root </strong>klasörü altında yer alması gereken <strong>ClientAccessPolicy.xml </strong>dosyasının içeriğini aşağıdaki gibi düzenleyebiliriz.</p>
<pre class="brush:xml;auto-links:false;toolbar:false" contenteditable="false"><?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></pre>
<p>Burada görüleceği üzere ChinookDataPortal ve alt yollarına erişim izni verilmiştir. Artık <strong>Silverlight </strong>tarafını geliştirmeye başlayabiliriz.</p>
<p><strong>Adım 6: Silverlight Application Projesinin Oluşturulması</strong></p>
<p>Bu amaçla <strong>Visual Studio 2010</strong> ortamında <strong>ConsumingHTTPBasedServices </strong>isimi ve <strong>Silverlight 4.0</strong> tabanlı bir <strong>Application </strong>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 <strong>MainPage </strong>sayfasına ait <strong>XAML </strong>içeriğini ise aşağıdaki gibi geliştirebiliriz.</p>
<p><strong>Adım 7 : MainPage.Xaml içeriği ve Kodun Yazılması</strong></p>
<pre class="brush:xml;auto-links:false;toolbar:false" contenteditable="false"><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></pre>
<p><strong>MainPage </strong>içerisinde yer alan <strong>ListBox </strong>kontrolü içeriği<strong> A' dan Z' ye harfler</strong> ile doldurulacaktır. Herhangibir harfe basıldığında, <strong>WebHttp Service</strong>' imiz için bir <strong>HTTP Get </strong>talebi oluşturulacak ve sonuçların <strong>ListBox </strong>içerisinde gösterilmesi sağlanacaktır. Bu amaçla kod içeriğini aşağıdaki gibi geliştirmemiz yeterlidir.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">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;
}
}
}</pre>
<blockquote>
<p>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.</p>
</blockquote>
<p><strong>Adım 8 : Silverlight Uygulamasının Test Edilmesi</strong></p>
<p>Dilerseniz uygulamanın çalışma zamanı sonuçlarına hemen bakalım. Böylece çalışma zamanı testlerini yapmış oluruz. Örneğin <strong>A</strong> başlıklı <strong>Button </strong>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.</p>
<p><img src="/pics/2010%2f4%2fblg175_Runtime1.gif" alt="" /></p>
<p>Görüldüğü üzere <strong>ListBox </strong>içeriği baş harfi A olan albüm adları ile doldurulmuştur. Hemen bu işlemin arkasından örneğin <strong>C</strong> başlıklı <strong>Button </strong>kontrolüne basarsak aşağıdaki sonuçlar ile karşılaştığımız görürüz.</p>
<p><img src="/pics/2010%2f4%2fblg175_Runtime2.gif" alt="" /></p>
<p>Süper değil mi? <img title="Wink" src="/editors/tiny_mce3/plugins/emotions/img/smiley-wink.gif" alt="Wink" border="0" /></p>
<p><strong>Özet</strong></p>
<p>Tabi bu örnekte dikkat edilmesi gereken noktalardan birisi de, istemci tarafında herhangibir <strong>Proxy </strong>tipinin olmayışıdır. Bunun yerine <strong>HTTP Get</strong> metodu ile talepte bulunulmuş ve elde edilen <strong>Stream </strong>üzerindeki <strong>XML </strong>içeriği değerlendirilmiştir. Diğer yandan çok doğal olarak <strong>Servis </strong>tarafında kullanılan <strong>Entity Data Model</strong> içerisindeki tiplerin istemci tarafındaki karşılıkları <span style="text-decoration: underline;">bulunmamaktadır</span>. 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 <strong>XML </strong>veya <strong>JSON </strong>tipindeki içeriğinde ilgili tiplere dönüştürülmesi gibi bir işlem söz konusu olacaktır.</p>
<p><strong>Ödev</strong> <img title="Smile" src="/editors/tiny_mce3/plugins/emotions/img/smiley-smile.gif" alt="Smile" border="0" /></p>
<ol>
<li>Servisin <strong>XML </strong>yerine <strong>JSON(JavaScript Object Notation)</strong> formatında bir çıktı vermesi halinde, <strong>Silverlight </strong>tarafında gerekli olan kod düzenlemelerini yapınız.</li>
<li>Servis üzerinden <strong>HTTP Put </strong>metod ile güncelleme işlemi yapabilmenizi sağlayacak bir geliştirmeyi aynı örnek üzerinden yapmaya çalışınız.</li>
</ol>
<p>Böylece geldik bir yazımızın daha sonuna. Tekrardan görüşünceye dek hepinize mutlu günler dilerim.</p>
<p><a href="https://buraksenyurt.com/pics/2010%2f4%2fConsumingHTTPBasedServices_RTM.rar">ConsumingHTTPBasedServices_RTM.rar (273,75 kb)</a><strong>[Örnek Visual Studio 2010 Ultimate RC Sürümü üzerinde geliştirişmiş ve RTM sürümü üzerinde test edilmiştir]</strong></p>2010-07-12T00:55:00+00:00wcf webhttp servicessilverlight 4.0silverlightwcfhttpgetpostputdeletewcf eco systembsenyurtBilindiğ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.https://buraksenyurt.com/pingback.axdhttps://buraksenyurt.com/post.aspx?id=3cbb8ca2-248d-4823-b7b4-d37b2bca42040https://buraksenyurt.com/trackback.axd?id=3cbb8ca2-248d-4823-b7b4-d37b2bca4204https://buraksenyurt.com/post/Silverlight-Tarafinda-HTTP-Bazli-Servisleri-Kullanmak#commenthttps://buraksenyurt.com/syndication.axd?post=3cbb8ca2-248d-4823-b7b4-d37b2bca4204https://buraksenyurt.com/post/Duplex-Service-icin-Silverlight-Istemcisi-GelistirmekDuplex Service için Silverlight İstemcisi Geliştirmek2010-07-05T01:00:00+00:00bsenyurt<p>Merhaba Arkadaşlar,</p>
<p>Hatırlayacağınız üzere <a title="Silverlight istemcileri için duplex service geliştirmek" href="https://buraksenyurt.com/post/Silverlight-Istemcileri-icin-Duplex-Service-Gelistirmek" target="_blank">bir önceki yazımızda</a> <strong>Silverlight </strong>istemcilerinin kullanabileceği <strong>Duplex WCF Service</strong> 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<strong> Visual Studio 2010 Ultimate RC</strong> ortamında <strong>Silverlight 4.0</strong> tabanlı bir uygulama oluşturarak işe başlayabiliriz. Bu işlemin ardından <strong>Proxy </strong>tabanlı bir WCF servis kullanımı için <strong>Add Service Reference </strong>seçeneğine başvurmamız gerekecektir. Yine hatırlayacağınız üzere geliştirdiğimiz <strong>WorldWeatherService </strong>isimli servisi <strong>IIS </strong>üzerine <strong>Publish </strong>etmiştik. Bu sebepten ilgili servis referansına aşağıdaki şekilden de görüldüğü üzere <strong>http://localhost/WorldWeatherService/WeatherDuplexService.svc </strong>adresinden erişebiliriz.</p>
<p><img src="/pics/2010%2f3%2fblg173_AddServiceReference.gif" alt="" /></p>
<p>İstemci tarafında çok basit olarak aşağıdaki <strong>XAML </strong>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.</p>
<pre class="brush:xml;auto-links:false;toolbar:false" contenteditable="false"><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></pre>
<p>İstemci tarafı kodlarına gelince;</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">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);
}
}
}</pre>
<p>Dikkat edileceği üzere <strong>WeatherDuplexServiceClient </strong>tipinden olan <strong>proxy </strong>nesnesi örneklenirken iki önemli parametre bilgisi geçilmektedir. Bunlardan ilki <strong>PollingDuplexHttpBinding </strong>tipinden olan <strong>bağlayıcı tiptir(Binding Type)</strong>. Diğeri ise servise erişilecek olan <strong>Endpoint </strong>adresidir. İstemci tarafı asenkron olarak <strong>SetCity </strong>metoduna erişebilir. Bu nedenle <strong>SetCityCompleted </strong>olay metodu yüklenmiştir. Dikkat çekici noktalardan birisi de <strong>NoticeReceived </strong>isimli bir olayın söz konusu olmasıdır. Bilinen <strong>Completed </strong>son eki yerine <strong>Received </strong>son ekinin gelmesinin de bir anlamı vardır elbette. <img title="Wink" src="/editors/tiny_mce3/plugins/emotions/img/smiley-wink.gif" alt="Wink" border="0" /> Bu, istemcinin servisten gelen <strong>Notice </strong>çağrısını takiben devreye girecek operasyon ile alakalıdır. Bir başka deyişle servis tarafı <strong>Notice </strong>metodunu çağırdıktan ve bu operasyon işleyişini tamamladıktan sonra istemci tarafında <strong>proxy_NoticeReceived </strong>olay metodu devreye girecektir. Ayrıca, bu olay metodunun <strong>NoticeReceivedEventArgs </strong>tipinden olan parametresi üzerinden yakalanan<strong> weather </strong>özelliği yardımıyla, servisin gönderdiği <strong>WeatherStatus </strong>nesnesine ulaşılabilir. Sonuç olarak uygulamayı test ettiğimizde örnek olarak aşağıdakine benzer bir sonuç elde ettiğimizi görebiliriz.</p>
<p><img src="/pics/2010%2f3%2fblg172_Runtime.gif" alt="" /></p>
<p>Üç 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.</p>
<p>Tüm bu çalışma sırasında dikkat edilmesi gereken bir husus da, önceki yazımızda da değinmiş olduğumuz <strong>Client Access Policy</strong> kullanımıdır. Eğer <strong>IIS </strong>root klasörü altında <strong>ClientAccessPolicy.xml </strong>dosyası ve gerekli içeriği olmassa çalışma zamanında aşağıdaki hata mesajı ile karşılaşılacaktır.</p>
<p><img src="/pics/2010%2f3%2fblg172_Exception.gif" alt="" /></p>
<p>Oysaki geliştirdiğimiz örnek<strong> Asp.Net Development Server</strong> üzerinden yayınlanmaktadır<em>(http://localhost:22334/WeatherClientAppTestPage.aspx)</em> ve sorunsuz bir şekilde <strong>IIS </strong>üzerindeki <strong>WorldWeatherService </strong>uygulamasına erişebilmektedir. Dolayısıyla Silverlight uygulamalarında sıkça rastladığımız <strong>Cross Domain </strong>sorunu yaşanmamaktadır. Böylece geldik bir yazımızın daha sonuna. Tekrardan görüşünceye dek hepinize mutlu günler dilerim.</p>
<p><a href="https://buraksenyurt.com/pics/2010%2f3%2fWeatherClientApp.rar">WeatherClientApp.rar (540,12 kb)</a> <strong>[Örnek Visual Studio 2010 RC sürümü üzerinde geliştirilmiş ve test edilmiştir]</strong></p>2010-07-05T01:00:00+00:00wcfsilverlightwcf serviceduplex serviceduplex communicationpushbsenyurtHatı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.https://buraksenyurt.com/pingback.axdhttps://buraksenyurt.com/post.aspx?id=fd409c76-d44b-48f2-8d6c-3643114462e31https://buraksenyurt.com/trackback.axd?id=fd409c76-d44b-48f2-8d6c-3643114462e3https://buraksenyurt.com/post/Duplex-Service-icin-Silverlight-Istemcisi-Gelistirmek#commenthttps://buraksenyurt.com/syndication.axd?post=fd409c76-d44b-48f2-8d6c-3643114462e3