https://www.buraksenyurt.com/Burak Selim Şenyurt - WCF RIA Services2018-11-09T08:08:09+00:00Matematik Mühendisi Bir Bilgisayar Programcısının NotlarıBurak Selim SenyurtBlogEngine.Net Syndication Generatorhttps://www.buraksenyurt.com/opml.axdBurak Selim SenyurtMatematik Mühendisi Bir Bilgisayar Programcısının Notlarıtr-TRBurak Selim Şenyurt0.0000000.000000https://www.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://www.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://www.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://www.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://www.buraksenyurt.com/pingback.axdhttps://www.buraksenyurt.com/post.aspx?id=70a0a3b3-b72a-41b8-afc9-9292e10569240https://www.buraksenyurt.com/trackback.axd?id=70a0a3b3-b72a-41b8-afc9-9292e1056924https://www.buraksenyurt.com/post/Silverlight-Tarafindan-Feed-Okumak#commenthttps://www.buraksenyurt.com/syndication.axd?post=70a0a3b3-b72a-41b8-afc9-9292e1056924https://www.buraksenyurt.com/post/Screencast-WCF-RIA-Services-OData-Excel-PowerPivotScreencast - WCF RIA Services, OData, Excel PowerPivot2010-03-23T00:05:00+00:00bsenyurt<p><img style="float: left;" src="http://www.buraksenyurt.com/pics/2010%2f3%2fblg167_GirisY.jpg" alt="" />Merhaba Arkadaşlar,</p>
<p>Yeni bir maceraya daha hazır mısınız? Evet dediğinizi duyar gibiyim. Bu sefer kobay haline getirdiğimiz <strong>Chinook </strong>veritabanına yine <strong>Entity Framework 4.0 </strong>üzerinden bir <strong>WCF RIA Service </strong>yardımıyla erişiyoruz. Ancak<strong> Domain Service</strong> tipini üretirken <a href="http://www.odata.org"><strong>OData(Open Data Protocol) </strong></a>desteği vereceğini belirtiyoruz. Bu durumda servisimizin çıktı olarak ürettiği <strong>Feed </strong>içeriğinin <strong>OData </strong>standartlarını destekleyen herhangibir ürün tarafından kullanılabileceğini belirtmiş oluyoruz. İstemci tarafında ise <strong>Excel 2010 Beta</strong> ürününe bir <strong>AddIn </strong>olarak gelen <strong><a title="PowerPivot" href="http://www.powerpivot.com/" target="_blank">PowerPivot </a></strong>aracından yararlanıyoruz. Buna göre servisin sunduğu veri içeriğini, sadece bir iki hareketle <strong>Excel </strong>üzerinde kullanılabilir halde çekmiş oluyoruz. Söz konusu fonksiyonellik sayesinde veriyi ister ızgara görünümünde bırakabilir ister detaylı grafiklerini çıkartabiliriz. İşin güzel yanı, servisi dünyanın herhangibir noktasındaki bir sunucu üzerinde host edebilecek olmamız. Dolayısıyla <strong>Excel PowerPivot </strong>aracı söz konusu servise bağlanabildiği sürece verinin en güncel halini çekebiliyor olacak. E ne duruyorsunuz? <a title="NedirTv?" href="http://www.nedirtv.com"><strong>NedirTv?</strong></a> sponsorluğundaki görsel dersimizi izlemeye buyrun.</p>
<p><strong>Süre :</strong> 14:52</p>
<p><strong>Boyut : </strong>24.5 Mb</p>
<p><a title="WCF RIA Services, OData, Excep PowerPivot" href="http://www.nedirtv.com/video/WCF-RIA-Services-OData-Excel-PowerPivot.aspx"><strong>Download etmek veya izlemek için</strong></a></p>2010-03-23T00:05:00+00:00screencastwcf ria servicesado.net entity framework 4.0ado.net entity frameworkwcf.net ria servicesodatapowerpivotexcel 2010wcf eco systembsenyurthttps://www.buraksenyurt.com/pingback.axdhttps://www.buraksenyurt.com/post.aspx?id=fe05e62a-2fa6-4dd1-9ab5-8409af84e8710https://www.buraksenyurt.com/trackback.axd?id=fe05e62a-2fa6-4dd1-9ab5-8409af84e871https://www.buraksenyurt.com/post/Screencast-WCF-RIA-Services-OData-Excel-PowerPivot#commenthttps://www.buraksenyurt.com/syndication.axd?post=fe05e62a-2fa6-4dd1-9ab5-8409af84e871https://www.buraksenyurt.com/post/WCF-RIA-Services-Alan-Bazli(Field-Based)-Rol-KontroluWCF RIA Services - Alan Bazlı(Field Based) Rol Kontrolü [Beta 2]2010-01-27T02:33:00+00:00bsenyurt<p><img style="float: right;" src="/pics/2009%2f12%2fblg119_Giris.jpg" alt="" />Merhaba Arkadaşlar,</p>
<p>Hani bazen insanın aklına son derece zekice fikirler gelir ya... <img title="Wink" src="/editors/tiny_mce3/plugins/emotions/img/smiley-wink.gif" alt="Wink" border="0" /> Sene <strong>1992</strong>. Lise öğrencisiyim. Bazı akşamlar yazlığımızdaki odamda üniversiteye hazırlanmaya çalışırdım. Güzel yaz gecelerinde, tertemiz ada ikliminde, mis gibi kokan iyotlu deniz suyunun çok yakınlarında konsantre olmak her ne kadar çok zor olsa da, buna mecburdum. <img title="Sealed" src="/editors/tiny_mce3/plugins/emotions/img/smiley-sealed.gif" alt="Sealed" border="0" /> Odamdaki flöresan ışığını çalışma ortamı için hiç uygun bulmazdım. Bunun yerine sarı ışığı tercih ederdim ve aynen yandaki şekilde görülene benzer bir gece lambam vardı.</p>
<p>Aslında lambanın etrafında şık bir küre bulunmaktaydı fakat sakarlığıyla bilinen bendeniz onu bir ara kırmıştım. Tabi hal böyle olunca şöyle bir sorunla karşılaştım. Işık direkt olarak gözüme geliyor ve çok rahatsız ediyordu. Çözüm olarak nemi yaptım. Dahiyane bir fikirle <strong>Amerika'</strong> daki bir arkadaşımın hediye ettiği <strong>Newyork Nicks</strong> takımının logosunu taşıyan şapkayı, lambanın üstüne güzelce yerleştirdim. Özellikle ışığın gözüme direkt olarak girmesini engelleyen ama etrafı ve okuduklarımı görmemi sağlayan bir açıyı düşünüp, ölçüp biçerek, dikkatlice yerleştirdim. Kendimle gurur duyuyordum. Bu zeka ile <strong>NASA</strong>'ya bile gidebilirim diye düşünüyordum <img title="Tongue out" src="/editors/tiny_mce3/plugins/emotions/img/smiley-tongue-out.gif" alt="Tongue out" border="0" /> </p>
<p>Ancak gecenin ilerleyen saatlerinde değil <strong>NASA</strong>, sıradan bir bölümü bile kazanmamın zor olduğuna kanaat getirdim. Nitekim ampülün zaman içerisinde çevreye yaydığı aşırı ısıyı tahmin edememiştim. Ancak odanın içerisine bir yanık kokusu yayıldığında bir şeylerin ters gittiğinin farkına varabilmiştim. En nihayetinde güzelim şapkanın ortasında kocaman bir yanık izi ve erimiş kumaş parçaları ile kala kaldım. Neredeyse koca bir delik açılmıştı. <img title="Laughing" src="/editors/tiny_mce3/plugins/emotions/img/smiley-laughing.gif" alt="Laughing" border="0" /> </p>
<p>İşte geçen gün yine böyle dahiyane bir fikir gelir mi aklıma diye düşünürken, <strong>WCF RIA Servis</strong> operasyonlarından dönen <strong>Entity</strong> nesnelerinin alanlarını rol bazlı olarak ele alabilir miyiz diye sorgulamaya başladım. Peki neden böyle bir ihtiyacımız olsun? Çok basit bir sebep öne sürebiliriz. Sunucu tarafındaki servisten dönen Entity örnekleri içerisindeki alanlarının bazılarının, Login olan kullanıcı tarafından <span style="text-decoration: underline;">görülmemesi</span> veya <span style="text-decoration: underline;">kullanılamaması</span> istenebilir. Malum <strong>DomainService</strong> tipi içerisinde yer alan operasyonlarda <strong>Login</strong> olan kullanıcının içerisinde bulunduğu rol elde edilebilmektedir. Buna göre sorgunun üreteceği çıktı içerisine dahil edilecek alanların yetkiye göre oluşturulması sağlanabilir...mi acaba? Durumu örnek bir senaryo üzerinden incelersek çok daha anlaşılır olacaktır. Öncelikli olarak <strong>AdventureWorks</strong> veritabanı içerisinde yer alan <strong>SalesOrderDetail</strong> isimli tabloyu kullanmak istediğimizi düşünelim.</p>
<p><img src="/pics/2009%2f12%2fblg119_EntityModelLast.gif" alt="" /></p>
<p>Örnek senaryomuzda çok doğal olarak <strong>Authentication</strong> alt yapısınında tesis edilmiş olması gerekmektedir. <strong>WCF RIA Service'</strong> lerinde <strong>Authentication Domain Service </strong>kullanımından daha önceki yazılarımızda bol bol bahsettiğimizden bu detayları atlıyoruz. Ancak <strong>ASP.NET Membership</strong> tabanlı olarak kurulan <strong>Authentication</strong> alt yapısı içerisinde testler için iki rol olduğunu söyleyebiliriz. <strong>AuthorizedSalesPerson</strong> ve <strong>JuniorSalesPerson</strong>. Senaryomuza göre güya <strong>AuthorizedSalesPerson </strong>rolünden gelen kullanıcılar <strong>UnitPriceDiscount</strong> alanının değerini görebilirken, <strong>JuniorSalesPerson</strong> rolündeki kullanıcılar göremeyecektir. Başlangıçta <strong>AdventureDomainService</strong> isimli <strong>Domain Service</strong> sınıfını aşağıdaki gibi düzenlediğimizi düşünelim.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">namespace RoleBasedFields.Web
{
using System.Linq;
using System.Web.DomainServices;
using System.Web.DomainServices.Providers;
using System.Web.Ria;
[RequiresAuthentication]
[EnableClientAccess()]
public class AdventureDomainService
: LinqToEntitiesDomainService<AdventureWorksEntities>
{
public IQueryable<SalesOrderDetail> GetSalesOrderDetails()
{
return (from sod in ObjectContext.SalesOrderDetails
select sod).Take(50);
}
}
}</pre>
<blockquote>
<p>121317(Yüz yirmi bir bin üçyüz on yedi)...SalesOrderDetail tablosunda bu kadar satır bulunmaktadır. Bu satırların tamamını istemci tarafına göndermek performans açısında tercih <span style="text-decoration: underline;">edilmemelidir</span>. Zaten WCF RIA Service' lerin kullanımı ile ilişkili best practices tiyolarında, üretilen standart sorguların filtreler ile düzenlenmesi önerilmektedir. En azından dönen veri içeriğinin bir gözden geçirilerek gerektiğinde performans için filtrelenmesi düşünülmelidir. Bu sebepten örneğimizde Take genişletme metodundan(Extension Method) yararlanılarak ilk 50 satırın alınması sağlanmıştır.</p>
</blockquote>
<p>İlk etapta sunucu tarafındaki operasyonda herhangibir rol kontrolü yapılmamaktadır. Buna göre Login olan bir kullanıcı için ekran çıktısı aşağıdaki gibi olacaktır.</p>
<p><img src="/pics/2009%2f12%2fblg119_Runtime1.gif" alt="" /></p>
<p>Ancak örnek senaryomuza göre <strong>JuniorSalesPerson </strong>rolünde olan <strong>bill</strong> isimli kullanıcının <strong>UnitPriceDiscount</strong> alanını görmemesi veya anlamlandıramaması gerekmektedir.<em>(Anlamlandıramamasının ne kadar zor olduğunu biraz sonra anlayacağız)</em> Buna göre sunucu tarafında yer alan operasyonun özelleştirilmesi gerekmektedir. Aslında yazımızın ulaşmak istediği tek nokta budur. Peki ama nasıl? <img title="Wink" src="/editors/tiny_mce3/plugins/emotions/img/smiley-wink.gif" alt="Wink" border="0" /> </p>
<p>Sonuçta istemci ve sunucu tarafında eş olan <strong>SalesOrderDetail</strong> <strong>Entity </strong>sınıf bilgisini çalışma zamanında değiştirmemiz şu etapta pek mümkün değildir. Akla ilk gelen yöntem result set çekilirken role göre gösterilmesi istenmeyen alana örneğin null değer atanmasını sağlamak olabilir. <em>(Bu konu ile ilişkili olaraktan yaptığım araştırmalarda, blog girdisini hazırladığım tarih itibariyle <a title="Field Level Access with RIA Services" href="http://blogs.msdn.com/brada/archive/2009/12/08/field-level-access-with-ria-services.aspx" target="_blank">Brad Abrams' ın ilgili yazısında </a>bu tip bir teknik uygulandığını gördüm)</em> Tabi örneğimizdeki alan <strong>null</strong> değer almamaktadır. Buna göre belki <strong>-1</strong> değer atanması sağlanabilir. Ama bu durumdada alan yine görülebilir olacaktır. <img title="Undecided" src="/editors/tiny_mce3/plugins/emotions/img/smiley-undecided.gif" alt="Undecided" border="0" /> Aşağıdaki kod parçasını göz önüne alalım.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">public IQueryable<SalesOrderDetail> GetSalesOrderDetails()
{
var resultSet = (from sod in ObjectContext.SalesOrderDetails
select sod).Take(50);
foreach (var result in resultSet)
{
if (ServiceContext.User.IsInRole("JuniorSalesPerson"))
result.UnitPriceDiscount = -1;
}
return resultSet;
}</pre>
<p>Bu kod parçasında görüldüğü gibi <strong>resultSet</strong> gönderilmeden önce her bir satırı taranmakta ve <strong>ServiceContext</strong> üzerinden elde edilen güncel kullanıcının rolüne bakılarak <strong>UnitPriceDiscount</strong> alanına <strong>-1 </strong>değer atanması sağlanmaktadır. Buna göre çalışma zamanı çıktısı bill isimli kullanıcı için aşağıdaki gibi olacaktır.</p>
<p><img src="/pics/2009%2f12%2fblg119_Runtime2.gif" alt="" /></p>
<p><strong>Peki istediğimiz bu muydu?</strong></p>
<p>Kesinlikle değil. Bizim hayalimiz ilgili alanın istemci tarafından <span style="text-decoration: underline;">görülmemesini</span> <strong>sunucudaki operasyon üzerinden</strong> sağlamaktı. Oysaki öğrenebildiğimiz sadece şu oldu; servis tarafındaki operasyondon dönen <strong>resultSet</strong> içeriğindeki veriyi istersek <strong>Login</strong> olan kullanıcının rolüne göre değiştirebiliriz. İşte şu anda lambaya koyduğumuz şapkanın delindiğini görmekteyiz. Benim NASA hayalleri yine yalan oldu anlayacağınız. <img title="Sealed" src="/editors/tiny_mce3/plugins/emotions/img/smiley-sealed.gif" alt="Sealed" border="0" /></p>
<p><strong>Peki ya çözüm?</strong></p>
<p>En ideal çözüm istemci tarafında kullanıcının rolüne göre ilgili alanının gizlenmesi olarak düşünülebilir. Ancak buda optimal bir çözüm olmayacaktır. Nitekim sunucu tarafında ele alınması gereken güvenlik konulu bir iş mantığını istemeden istemci tarafına taşımak zorunda kalmış oluruz. Tabi en büyük sıkıntılardan birisi şudur. Sunucu tarafında bu rol kontrolünü başarabilsek dahi, istemci tarafında gönderilecek entity örneklerinin dinamik olarak değişebiliyor olması gerekecektir. Oldukça zor bir işlem aslında...</p>
<p>Anlaşılan bu konuda <strong>WCF RIA Services</strong> tarafında bir eksiklik var. Bende en ideal çözüm için araştırmalarıma devam ediyorum. Bakalım lambanın üzerine koyduğumuz şapkadaki deliği kapatabilecek miyiz? Tekrardan görüşünceye dek hepinize mutlu günler dilerim.</p>2010-01-27T02:33:00+00:00wcf ria services.net ria serviceswcfwcf eco systembsenyurtHani bazen insanın aklına son derece zekice fikirler gelir ya... Wink Sene 1992. Lise öğrencisiyim. Bazı akşamlar yazlığımızdaki odamda üniversiteye hazırlanmaya çalışırdım. Güzel yaz gecelerinde, tertemiz ada ikliminde, mis gibi kokan iyotlu deniz suyunun çok yakınlarında konsantre olmak her ne kadar çok zor olsa da, buna mecburdum. Sealed Odamdaki flöresan ışığını çalışma ortamı için hiç uygun bulmazdım. Bunun yerine sarı ışığı tercih ederdim ve aynen yandaki şekilde görülene benzer bir gece lambam vardı.https://www.buraksenyurt.com/pingback.axdhttps://www.buraksenyurt.com/post.aspx?id=819457de-6f41-4f9a-97f6-e1cfb2cb5fa61https://www.buraksenyurt.com/trackback.axd?id=819457de-6f41-4f9a-97f6-e1cfb2cb5fa6https://www.buraksenyurt.com/post/WCF-RIA-Services-Alan-Bazli(Field-Based)-Rol-Kontrolu#commenthttps://www.buraksenyurt.com/syndication.axd?post=819457de-6f41-4f9a-97f6-e1cfb2cb5fa6https://www.buraksenyurt.com/post/WCF-RIA-Services-Custom-AuthorizationWCF RIA Services - Custom Authorization [Beta 2]2010-01-26T01:52:00+00:00bsenyurt<p><img style="float: right;" src="/pics/2009%2f12%2fblg116_Giris.jpg" alt="" />Merhababa Arkadaşlar,</p>
<p>Geçtiğimiz günlerde uzun süredir yemediğim şu meşhur Dunkin & Donuts' tan bir iki kurabiye almak istedim. <img title="Wink" src="/editors/tiny_mce3/plugins/emotions/img/smiley-wink.gif" alt="Wink" border="0" /> Ansızın gelen bu dayanılmaz istek üzerine oturduğumuz semte en yakın dükkanına gidip hem kendim hemde eşim için bir kaç tane aldım. Sonrası malum...Yanında güzel bir kahve ve harika bir tat...Tattıları afiyetle mideye indirdikten sonra evde sessiz ve sakin bir ortamın olduğunu farkettim. Bizim azman ufaklık uyumuş ve gıkı bile çıkmıyorken, yorduğu eşim divanda mışıl mışıl sızmıştı.</p>
<p>Herkesin böyle huzurlu bir ortamı hakkettiğini düşünürken, yağmur damlalarının cama vuruşunu izliyordum. Derken ansızın bir ilham geldi ve bloğuma bir şeyler yazmamın iyi olacağı kanısına vardım. Nede olsa gerekli glikoz yüklemesi fazlasıyla yapılmıştı. Günlüğüme yazılacaklar listeme baktığımda, sıradaki konunun <strong>WCF RIA Service'</strong> lerinde <strong>özel yetkilendirme niteliklerinin(Custom Authorization Attributes)</strong> nasıl yazılacağının anlatılması olduğunu farkettim. Neyseki hafta içi bu konu ile ilişkili olaraktan internetteki az sayıda kaynaktan bilgi edinmiştim. Tabiki en güncel ve geçerli kaynak her zamanki gibi <strong>MSDN'</strong> di.</p>
<p>Hatırlayacağınız gibi bir önceki yazımızda niteliklerden yararlanarak yetkilendirme işlemlerinin nasıl yapılabileceğini basit bir örnek üzerinden incelemeye çalışmıştık. Buna göre önemli olan nokta, <strong>nitelik(Attribute)</strong> yardımıyla çalışma zamanının nasıl davranacağının belirlenebilmesidir. Ancak bazen, <strong>yetkilendirme(Authorization)</strong> kontrolü için özel durumların ele alınması da gerekebilir. Nitekim sadece istemcinin içinde bulunduğu role göre karar vermenin dışında yapılması gereken yetki kontrolleri söz konusu olabilir. Böyle bir durumda çalışma zamanının anlayacağı kendi niteliklerimizi yazmamız gerekmektedir.</p>
<p>Çok doğal olarak bu geliştirme işleminde yazılacak olan nitelik tipinin, çalışma zamanının anlayacağı ve kullanacağı bir veya daha fazla operasyonu uygulaması şarttır. Burada tipik olarak, çalışma zamanının değerlendireceği fonksiyonellikleri barındıran bir ata nitelik sınıfından yapılacak olan <strong>türetme(Inherit)</strong> işleminden bahsettiğimizi ifade edebiliriz. Dilerseniz konuyu daha iyi anlamak için bu yazımızda geliştireceğimiz örnek senaryomuzdan kısaca bahsedelim.</p>
<p>Senaryomuza göre <strong>Domain Service</strong> içerisinde tanımlanmış olan herhangibir operasyonun gerçekleştirilmesi sırasında, talepte bulunan kullanıcının içerisinde bulunduğu role değil, adının özel olarak tutulan yasaklı bir listede bulunup bulunmadığına bakılması durumu ele alınmaktadır. Bu yasaklı listenin <strong>ASP.NET Membership </strong>tarafından hazır olarak tutulan veritabanına extend edilmiş bir tablo içerisinde tutulması mümkündür. Bunun dışında dosya tabanlı olarak <strong>XML</strong> veya basit <strong>Text </strong>formatında dahi tutulabilir. Hatta olayı biraz abartıp <strong>Windows Registry</strong> ayarlarında dahi tutulması ve benzer saklama alanları düşünülebilir.</p>
<p>Hatta bu listenin bir uzak sunucuda duruyor ve ancak servis bazlı bir operasyon yardımıyla kontrollerin yapılabiliyor olması da söz konusu olabillir. Tabiki dosya tabanlı olan saklama şekli bu seçenekler arasında en az güvenli olanıdır. Nitekim dosyanın güvenliğini sağlamak, veritabanı içerisindeki bir tabloya göre çok daha zor olabilir. Ancak amacımız yetkilendirme işlemi için özel nitelik yazılması ve kullanılması olduğundan bebek adımlarıyla ilerleyeceğiz. Bu nedenle geliştireceğimiz örnekte basit olarak yasaklı listenin bir <strong>Text</strong> dosyada düzenli olarak tutulduğunu varsayıyor olacağız.</p>
<p>Buna göre <strong>System.Web.DomainServices.AuthorizationAttribute</strong> niteliğinden türettiğimiz tipin içerisinde yer alan <strong>Authorize</strong> metodu içerisinde gelen kullanıcı adının, yasaklı listede olup olmadığını kontrol etmemiz yeterli olacaktır. İşte sunucu tarafında yer alan <strong>CheckBannedListAttribute</strong> sınıfı içeriğimiz.</p>
<p><img src="/pics/2009%2f12%2fblg116_ClassDiagram.gif" alt="" /></p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System.IO;
using System.Linq;
using System.Web;
using System.Web.DomainServices;
namespace ChinookCustomAuthorization.Web
{
public class CheckBannedListAttribute
:AuthorizationAttribute
{
public override bool Authorize(System.Security.Principal.IPrincipal principal)
{
string filePath = HttpContext.Current.Server.MapPath("~\\BannedList.txt");
string[] bannedList=File.ReadAllLines(filePath);
return !bannedList.Contains(principal.Identity.Name);
}
}
}</pre>
<p><strong>CheckBannedListAttribute</strong> sınıfı <strong>AuthorizationAttribute</strong> tipinden türemektedir. Buna göre sınıf diagramı görüntüsünden de fark edileceği üzere, <strong>abstract</strong> olan <strong>Authorize</strong> metodunu ezmek zorundadır. <strong>Authorize</strong> metodu, niteliğin uygulanacağı metodlar için gerekli yetki kontrolü operasyonunu üstlenmektedir. Dikkat edileceği üzere <strong>Authorize</strong> metodu parametre olarak tanıdık bir arayüzü almaktadır; <strong>IPrincipal</strong>. Bu arayüze gelen çalışma zamanı referansından yararlanarak sisteme giriş yapan kullanıcının adını, hangi rolde yer aldığını, doğrulanıp doğrulanmadığını öğrenebiliriz.</p>
<p>Biz örnek senaryomuza göre <strong>Login</strong> olan kullanıcı adının yasaklı listenin tutulduğu <strong>text</strong> tabanlı dosya içerisinde olup olmadığını incelemeyi hedefliyoruz. Bu sebepten <strong>Authorize</strong> metodu içerisinde <strong>BannedList</strong> isimli ve sunucu proje içerisinde tutulan <span style="text-decoration: underline;">text tabanlı</span> dosyanın tüm satırlarının yüklenmesi ve elde edilen dizi içerisinde olup olmadığına göre <strong>bool</strong> tipte bir sonucun döndürülmesi söz konusu. Yazmış olduğumuz özel nitelik tipini sunucu tarafında yer alan <strong>ChinookDomainService</strong> sınıfı içerisinde uygulamamız ise son derece kolay.</p>
<blockquote>
<p>Örneğimizin kalan kısmı daha önceki yazımızda geliştirdiğimiz ile benzer. Bu nedenle detaya girerek konudan uzaklaşmamayı tercih etmekteyim.</p>
</blockquote>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">namespace ChinookCustomAuthorization.Web
{
using System.Linq;
using System.Web.DomainServices;
using System.Web.DomainServices.Providers;
using System.Web.Ria;
[RequiresAuthentication]
[EnableClientAccess()]
public class ChinookDomainService
: LinqToEntitiesDomainService<ChinookEntities>
{
[CheckBannedList]
public IQueryable<Album> GetAlbums()
{
return this.ObjectContext.Albums;
}
}
}</pre>
<p>Görüldüğü gibi <strong>CheckBannedList</strong> niteliği, yasaklı liste yetki kontrolü yapılmak istenen operasyonun üzerinde uygulanmaktadır. Buna göre çalışma zamanında <strong>Login</strong> olan bir kullanıcının söz konusu <strong>GetAlbums</strong> operasyonunu talep etmesi halinde devreye girecektir. Söz gelimi <strong>text</strong> dosyamız içerisinde aşağıdaki isimlerin yer aldığını varsayalım.</p>
<p><img src="/pics/2009%2f12%2fblg116_Banned.gif" alt="" /></p>
<p>Buna göre örneğin <strong>buraks</strong> isimli kullanıcı ile <strong>Login</strong> olup albüm listesinin yüklendiği operasyonu talep ettiğimizde, tarayıcı uygulama üzerinde aşağıdaki script hatasını aldığımızı görürüz.</p>
<p><img src="/pics/2009%2f12%2fblg116_Error.gif" alt="" /></p>
<p>Görüldüğü üzere<strong> Access Denied</strong> kelimeleri hata mesajı içerisinde yer almaktadır. Çok doğal olarak yasaklı liste içerisinde yer almayan bir kullanıcı ile sistem girdiğimizde<em>(örneğin senaryomuza göre bill olabilir) </em>albüm listesinin başarılı bir şekilde elde edildiği görülecektir. Aynen aşağıdaki ekran görüntüsünde olduğu gibi.</p>
<p><img src="/pics/2009%2f12%2fblg116_Complete.gif" alt="" /></p>
<p>Sonuç olarak eklediğimiz özel <strong>authorization</strong> niteliği yardımıyla, <strong>Domain Service</strong> üzerinden çağırılacak bir operasyon için, sisteme giriş yapan kullanıcının rolüne bakmatan farklı bir yetkilendirme kontrolü gerçekleştirebildiğimizi görmüş olduk. 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://www.buraksenyurt.com/pics/2009%2f12%2fCustomAuthorization.rar">CustomAuthorization.rar (1,29 mb)</a></p>2010-01-26T01:52:00+00:00wcf ria services.net ria serviceswcfwcf eco systembsenyurtGeçtiğimiz günlerde uzun süredir yemediğim şu meşhur Dunkin & Donuts' tan bir iki kurabiye almak istedim. Wink Ansızın gelen bu dayanılmaz istek üzerine oturduğumuz semte en yakın dükkanına gidip hem kendim hemde eşim için bir kaç tane aldım.https://www.buraksenyurt.com/pingback.axdhttps://www.buraksenyurt.com/post.aspx?id=213dc133-2e57-4062-b9e7-7830c3851cb70https://www.buraksenyurt.com/trackback.axd?id=213dc133-2e57-4062-b9e7-7830c3851cb7https://www.buraksenyurt.com/post/WCF-RIA-Services-Custom-Authorization#commenthttps://www.buraksenyurt.com/syndication.axd?post=213dc133-2e57-4062-b9e7-7830c3851cb7https://www.buraksenyurt.com/post/WCF-RIA-Services-Authentication-Domain-Service-Attribute-Bazli-Yetkilendirme-(Attribute-Based-Authorization)WCF RIA Services - Authentication Domain Service - Attribute Bazlı Yetkilendirme [Beta 2]2010-01-22T00:45:00+00:00bsenyurt<p><img style="float: right;" src="/pics/2009%2f12%2fblg115_Giris.jpg" alt="" />Merhaba Arkadaşlar,</p>
<p>Bildiğiniz üzere bir süredir<strong> WCF RIA Service'</strong> lerinde <strong>doğrulama(Authentication), yetkilendirme(Authorization), Role </strong>ve <strong>Profile </strong>yönetimi konularına değinmekteyiz. <strong>WCF RIA Service'</strong> lerinin temel amaçlarından birisininde <strong>RIA</strong> tipindeki uygulamalar için <strong>Ado.Net Entity Framework</strong> gibi kaynaklar üzerinden <strong>CRUD(CreateReadUpdateDelete)</strong> operasyonlarını sağlanması olduğu düşünüldüğünde, servis fonksiyonelliklerinin yetkilendirilmeside güvenlik açısından önem arz eden konuların başında gelmektedir. Bu konu, <strong>WCF RIA Service'</strong> lerinde <strong>nitelikler(Attributes)</strong> yardımıyla ele alınabilmektedir.</p>
<p>Bu noktada iki önemli niteliğin olduğunu söyleyebiliriz. Bunlardan birisi<strong> Domain Service</strong> sınıfının <strong>Authentication</strong> işlemleri çerçevesinde değerlendirilmesi gerektiğini çalışma zamanına söyleyen <strong>RequiresAuthentication </strong>niteliğidir. Bu niteliği <strong>Domain Service</strong> tipine uygulamak yeterlidir. Diğer taraftan<strong>, Domain Service</strong> içerisinde tanımlanmış olan operasyonların hangi yetkiler altında çalıştırılabileceğini belirtmek için <strong>RequiresRole</strong> isimli nitelikten yararlanılmaktadır. <strong>RequiresRole</strong> niteliği <strong>string</strong> tipinde birden fazla parametre alabilmektedir. Bu parametre değerleri tahmin edileceği üzere rolleri ifade etmektedir. Bu teoriye göre bir<strong> Domain Service</strong> operasyonunun rol bazlı olaraktan yetki altında çalıştırılmasının sağlanması mümkündür.</p>
<p>Tabi teoride bahsettiğimiz bu kavramları pratiğe dökmemiz bizim için önemlidir. Bu nedenle yazımızın bundan sonraki kısmında basit olarak Chinook veritabanındaki kobay tablolarımızdan olan <strong>Album</strong> içeriğine ulaşmak için kullanılan bir operasyon üzerinde, rol bazlı yetkilendirme işlemlerini nasıl uygulayabileceğimizi incelemeye çalışacağız.</p>
<p><em><strong>Kişisel Not :</strong> Başlamadan önce <strong>Silverlight</strong> uygulamasında daha önceki yazılarda sıklıkla anlattığımız şekilde <strong>Form bazlı doğrulama(Form-Based Authentication) </strong>için gerekli ayarları yapmamız gerektiğini hatırlatalım. Özellikle <strong>role </strong>yönetimini etkinleştirmemiz gerektiğini ve hem sunucu hemde istemci tarafında gerekli konfigurasyon ve kod ayarlarını uygulamamız gerektiğini unutmayalım. Örneğimizde yine <strong>buraks</strong> ve <strong>bill </strong>isimli kullanıcıları değerlendiriyor olacağız. Bunlardan birisi <strong>Employee</strong> rolünde iken diğeri <strong>Finance</strong> rolündedir. Amaçlanan sadece <strong>Finance</strong> rolündekilerin, albüm listesini çekebilmesini sağlamaktır. Tabiki <strong>Chinook</strong> isimli veritabanını <strong>Silverlight</strong> uygulamasında kullanabilmek için gerekli <strong>Domain Service(ChinookDomainService)</strong> öğesinide eklememiz gerektiğini hatırlatmak isterim. Pek çok hatırlatmada bulundum ama önceki yazıları takip edip uygulayan arkadaşlarımız için örneği bu aşamaya getirmek son derece kolay olacaktır düşüncesindeyim <img title="Wink" src="/editors/tiny_mce3/plugins/emotions/img/smiley-wink.gif" alt="Wink" border="0" /></em></p>
<p>Gelelim örneğimize. İlk olarak durumu kuş bakışı değerlendirmeye çalışalım. Buna göre aşağıdaki şekli göz önüne alabiliriz.</p>
<p><img src="/pics/2009%2f12%2fblg115_Case.gif" alt="" /></p>
<p>Şekildende görüleceği üzere sunucu uygulama tarafında <strong>Chinook</strong> veritabanına ulaşılmasını ve üzerinde <strong>CRUD</strong> işlemleri yapılabilmesini sağlayan<em>(ki bu örnekte sadece veri çekiyor olacağız) </em><strong>ChinookDomainService</strong> isimli <strong>Domain Service</strong> tipi bulunmaktadır. Bu tip arada <strong>Ado.Net Entity Framework</strong> modelini kullanmaktadır. Diğer taraftan doğrulama işlemleri için <strong>ASP.NET Membership</strong> alt yapısı kullanılmakta olup istemci tarafının bu hizmeti değerlendirebilmesi için birde <strong>Authentication Domain Service(ChinookDomainService)</strong> öğesi yer almaktadır. İstemci tarafı veri ve güvenlik işlemleri için bu iki servisten yararlanacaktır. Önemli olan noktalardan biriside hangi operasyonu nasıl yetkilendireceğimizi bilmektir. Bu noktada <strong>ChinookDomainService</strong> sınıfını aşağıdaki şekilde oluşturduğumuzu göz önüne alalım.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">namespace SilverlightApplication8.Web
{
using System.Linq;
using System.Web.DomainServices;
using System.Web.DomainServices.Providers;
using System.Web.Ria;
[RequiresAuthentication] // Servis operasyonlarında Authentication uygulanacağını belirtiyoruz.
[EnableClientAccess()]
public class ChinookDomainService : LinqToEntitiesDomainService<ChinookEntities>
{
[RequiresRole("Finance")] // Sadece Finance rolündekilerin aşağıdaki operasyonu kullanabileceğini belirtmekteyiz.
public IQueryable<Album> GetAlbums(int artistId)
{
// Örnek olarak ArtistId bilgisine göre albüm listesini Title bilgisine göre A...Z sırasında döndürüyoruz
return from a in ObjectContext.Albums
where a.ArtistId == artistId
orderby a.Title
select a;
}
}
}</pre>
<p>Dikkat edileceği üzere <strong>ChinookDomainService</strong> tipine <strong>RequiresAuthentication</strong> niteliği uygulanmıştır. Bununla birlikte sadece <strong>Finance</strong> rolündekilerin kullanımına sunulan <strong>GetAlbums</strong> isimli bir operasyon yer almaktadır. Yazımızın başında da belirttiğimiz üzere <strong>RequiresRole</strong> niteliğine parametre olarak birden fazla rol adı verilebilir. Sunucu tarafında rol bazlı yetkilendirme için yapmamız gerekenler sadece bu kadardır. Gelelim istemci tarafına. Bu amaçla <strong>MainPage.xaml</strong> içeriğini ve kod kısmını aşağıdaki gibi geliştirdiğimizi düşünelim.</p>
<p><strong>MainPage.xaml;</strong></p>
<p><img src="/pics/2009%2f12%2fblg115_MainPage.gif" alt="" /></p>
<pre class="brush:xml;auto-links:false;toolbar:false" contenteditable="false"><UserControl x:Class="SilverlightApplication8.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:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data" xmlns:dataInput="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data.Input">
<Grid x:Name="LayoutRoot" Background="White">
<Button Content="Load Albums" Height="23" HorizontalAlignment="Left" Margin="305,18,0,0" Name="btnLoadAlbums" VerticalAlignment="Top" Width="83" Click="btnLoadAlbums_Click" />
<data:DataGrid AutoGenerateColumns="True" Height="105" HorizontalAlignment="Left" Margin="10,87,0,0" Name="grdAlbums" VerticalAlignment="Top" Width="378" />
<dataInput:Label Height="80" HorizontalAlignment="Left" Margin="10,208,0,0" Name="lblStatus" VerticalAlignment="Top" Width="378" Content="Olası hata mesajı..." />
<dataInput:Label Height="28" HorizontalAlignment="Left" Margin="12,18,0,0" Name="label1" VerticalAlignment="Top" Width="59" Content="Username" />
<TextBox Height="23" HorizontalAlignment="Left" Margin="77,18,0,0" Name="txtUsername" VerticalAlignment="Top" Width="120" />
<dataInput:Label Height="28" HorizontalAlignment="Left" Margin="14,54,0,0" Name="label2" VerticalAlignment="Top" Width="57" Content="Password" />
<PasswordBox Height="23" HorizontalAlignment="Left" Margin="77,54,0,0" Name="txtPassword" VerticalAlignment="Top" Width="120" />
<Button Content="Login" Height="23" HorizontalAlignment="Left" Margin="203,18,0,0" Name="btnLogin" VerticalAlignment="Top" Width="82" Click="btnLogin_Click" />
<Button Content="Logout" Height="23" HorizontalAlignment="Left" Margin="203,54,0,0" Name="btnLogout" VerticalAlignment="Top" Width="82" Click="btnLogout_Click" />
</Grid>
</UserControl></pre>
<p><strong>MainPage.xaml.cs;</strong></p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Ria;
using System.Windows.Ria.ApplicationServices;
using SilverlightApplication8.Web;
namespace SilverlightApplication8
{
public partial class MainPage : UserControl
{
// DomainContext ve AuthenticationService kullanımı için gerekli örnekler
ChinookDomainContext context;
AuthenticationService authSrv;
public MainPage()
{
InitializeComponent();
btnLoadAlbums.IsEnabled = false;
btnLogout.IsEnabled = false;
context = new ChinookDomainContext();
authSrv = WebContext.Current.Authentication;
// Login işlemi olduğunda devreye girecek olay metodu yüklenir
authSrv.LoggedIn += new EventHandler<AuthenticationEventArgs>(authSrv_LoggedIn);
// Logout işlemi tamamlandığında devreye girecek olay metodu yüklenir
authSrv.LoggedOut += new EventHandler<AuthenticationEventArgs>(authSrv_LoggedOut);
}
private void btnLogin_Click(object sender, RoutedEventArgs e)
{
// Login işlemi yapılır
LoginOperation logOp = authSrv.Login(new LoginParameters(txtUsername.Text, txtPassword.Password));
}
private void btnLogout_Click(object sender, RoutedEventArgs e)
{
// Logout işlemi yapılır. Hata var ise exception fırlatılması sağlanır
authSrv.Logout(true);
}
void authSrv_LoggedIn(object sender, AuthenticationEventArgs e)
{
// Login olan kullanıcı için güncel User bilgileri alınır
Web.User currentUser = WebContext.Current.User;
// Kullanıcının dahil olduğu roller ve adı bilgilendirme amaçlı öğrenilir
string roles=String.Empty;
foreach (var role in currentUser.Roles)
{
roles += String.Format("{0}|", role);
}
lblStatus.Content =String.Format("Kullanıcı {0} Rolleri : {1}",currentUser.Name,roles);
btnLoadAlbums.IsEnabled = true;
btnLogout.IsEnabled = true;
btnLogin.IsEnabled = false;
}
void authSrv_LoggedOut(object sender, AuthenticationEventArgs e)
{
btnLogout.IsEnabled = false;
btnLoadAlbums.IsEnabled = false;
btnLogin.IsEnabled = true;
}
private void btnLoadAlbums_Click(object sender, RoutedEventArgs e)
{
// Login olunduktan sonra Album listesinin yüklenmesi aşamasına geçilir.
// DomainService üzerinde yer alan GetAlbums operasyonundaki role gerekliliği nedeni ile sadece Finance rolü için yükleme yapıldığı görülür. Aksi durumda ise bir çalışma zamanı hatası oluşacak ve script error olarak tarayıcı üzerinde görülecektir.
LoadOperation<Album> op = context.Load<Album>(context.GetAlbumsQuery(1));
grdAlbums.ItemsSource = op.Entities;
}
}
}</pre>
<p>Aslında istemci tarafında yapılan tek şey kullanıcının <strong>Login</strong> ve <strong>Logout</strong> olmasını sağlayacak operasyonlar ile örnek olması açısından 1 numaralı şarkıcıya ait albüm listesinin çekilmesini sağlamaktır. Dikkat edileceği üzere yetkilendirme kontrolü istemci tarafında yapılamamaktadır. Bu kontrol sunucu tarafında çalışma zamanı tarafından ele alınan<strong> Domain Service</strong> sınıfı üzerinden ele alınmaktadır. Buna göre <strong>Login</strong> olan geçerli kullanıcının <strong>Finance</strong> rolünde olmaması halinde albüm bilgilerini <span style="text-decoration: underline;">getirememesi</span> gerekmektedir ki bu gerçektende böyledir. İşte yetkisiz bir kullanıcının albümleri yüklemek istemesi halinde çalışma zamanında oluşacak durum;</p>
<p><img src="/pics/2009%2f12%2fblg115_Error.gif" alt="" /></p>
<p>Hata mesajında yer alan <strong>Access Denied</strong> kelimeleri olayı tüm çıplaklığıyla özetlemektedir. Diğer yandan senaryomuza göre <strong>Finance</strong> rolünde yer alan <strong>bill</strong> isimli kullanıcı ile <strong>Login</strong> olunup albüm listesi çekilmek istendiğinde, bilgilerin başarılı bir şekilde <strong>DataGrid</strong> kontrolüne çekildiği gözlemlenir. Aynen aşağıdaki şekilde görüldüğü gibi.</p>
<p><img src="/pics/2009%2f12%2fblg115_AuthorizationOk.gif" alt="" /></p>
<p>Tabi bu yazımızda yetkilendirme nedeniyle oluşan istisna durumu ele kontrol altına alınmamıştır. Uygulama bu istisna ile karşılaştığında sonlandırılmaktadır ve hata mesajı <strong>script error</strong> olarak tarayıcı uygulama üzerinden yakalanmaktadır. Ancak en basit haliyle <strong>attribute</strong> bazlı olaraktan yetkilendirme işlemi sunucu tarafında yer alan servisler kanalıyla gerçekleştirilebilmiştir. Bu noktada vurgulanması gereken durumlardan biriside kendi <strong>Authorization</strong> <strong>niteliklerimizi(Attribute)</strong> yazabileceğimizdir. Söz gelimi role göre değil ama başka bir kritere göre yetkilendirme yapmak isteyebiliriz. Bu durumu ilerleyen yazılarımızda ele almaya çalışıyor olacağım. Tekrardan görüşünceye dek hepinize mutlu günler dilerim.</p>
<p> </p>2010-01-22T00:45:00+00:00wcf ria services.net ria serviceswcfwcf eco systembsenyurtBildiğiniz üzere bir süredir WCF RIA Service' lerinde doğrulama(Authentication), yetkilendirme(Authorization), Role ve Profile yönetimi konularına değinmekteyiz. WCF RIA Service' lerinin temel amaçlarından birisininde RIA tipindeki uygulamalar için Ado.Net Entity Framework gibi kaynaklar üzerinden CRUD(CreateReadUpdateDelete) operasyonlarını sağlanması olduğu düşünüldüğünde, servis fonksiyonelliklerinin yetkilendirilmeside güvenlik açısından önem arz eden konuların başında gelmektedir. Bu konu, WCF RIA Service' lerinde nitelikler(Attributes) yardımıyla ele alınabilmektedir.https://www.buraksenyurt.com/pingback.axdhttps://www.buraksenyurt.com/post.aspx?id=553aa8ce-31a6-4cfc-a583-5052de7fee6f0https://www.buraksenyurt.com/trackback.axd?id=553aa8ce-31a6-4cfc-a583-5052de7fee6fhttps://www.buraksenyurt.com/post/WCF-RIA-Services-Authentication-Domain-Service-Attribute-Bazli-Yetkilendirme-(Attribute-Based-Authorization)#commenthttps://www.buraksenyurt.com/syndication.axd?post=553aa8ce-31a6-4cfc-a583-5052de7fee6f