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

Yazılımcı Psikolojisi

Cuma, 5 Şubat 2010 14:10 by bsenyurt

Merhaba Arkadaşlar,

Beni tanıyanlar bloğumdan teknik konular dışında pek paylaşımda bulunmadığımı çok iyi bilirler. Ancak kalıpları biraz olsun esnetmekten kime zarar gelir ki? Belki hepimiz için faydası bile olabilir. Bu yazımda bahsedeceğim konuya başlamadan önce tüm psikologlardan affımı istemek zorundayım. Nitekim bir yazılımcının olağan psikolojisi üzerine gözlemlerimi aktarırken bilimsel hiç bir kurama uyamayacağımdan veya sonuçlara varamayacağımdan oldukça eminim. Aslında bu gibi konularda işi uzmanlarına bırakmak gerekiyor. Ben de aslında onbir seneyi bulan profesyonel iş ve yazılım hayatımdaki gözlemlerimi aktarmaya çalışacağım. İnanıyorum ki yazıyı okuyan her meslektaşım kendisine bir pay çıkartacak ve en azından bir kaç dakika da olsa düşünecektir. Aslında anlatılanların gerçek hayat hikayeleri ile birleştirilmesi sonrasında dramatik bir durum mu oluşmaktadır yoksa traji komik bir dünya mı söz konusudur siz karar vereceksiniz.

Öncelikle yandaki resmin size ne çağırıştırdığını düşünerek başlayalım. Bu resimde birden fazla kişinin olduğunu ama her birinin kendi cam fanusu içerisinde ayakta kaldığını görüyoruz. Ortada silik bir kişilik var ama onun konumuzla ilgisi bulunmamakta. Aslında çalışma ortamlarımızdaki oturma düzenlerimize, masamızın üstündeki malzemelere baktığımızda kendimize ait bir dünya kurduğumuzu hatta kimilerinin deyimiyle bir yaşam alanı oluşturduğumuzu kolayca fark edebiliriz.

Bu yaşam alanının belkide en önemli parçalarından birisi ses kalitesi yüksek olan kulaklıklardır. Şimdi oturduğunuz sandalyeden ayağa kalkın ve çevrenizde kulaklık ile müzik dinleyip bilgisayar ekranına bakan arkadaşlarınızı tespit etmeye çalışın. Sonra da şunu düşünün. Doğa da, seyahatlerde, deniz kenarında, parkta, bahçede, yürüyüşte, sağlık için yapılan koşuda, çevreye bakarken size çok iyi gelen, içinizi rahatlatan veya hissetmek istediğiniz duygularınızı daha çok ortaya çıkaran müzikler nasıl oluyor da 15.4 inch' lik bir dikdörgene bakarken size bir şeyler hissettirebiliyor. Gerçekte müziği mi dinliyorsunuz yoksa yaptığınız işe mi odaklanıyorsunuz.

Yaşananlardan : Çok eskiden çalıştığım bir yazılım şirketinde dış kaynak elemanı olarak bir projede görevlendirilmiştim. Yazılımı geliştirdiğimiz şirkette bize ayrılan yer İstanbul' un en güzel boğaz manzarasına sahip kesimlerinden birisiydi. Çalışma ortamının kurulduğu binanın etrafında kocaman ve yemyeşil bir arazi yer alıyordu. Bu nedenle son derece sessiz ve işinize en yüksek seviyede konsantre olabileceğiniz bir ortam söz konusuydu. Ancak her yazılımcı gibi orada bile, kulaklıkla müzik dinlemeyi başarırdık. Arkadaşlarımızdan birisi ise dışarıdan çalışmasına etki eden en ufak sesten rahatsız olurdu. Çoğu zaman kulaklıklarımdan dışarı çıkan ses nedeniyle ya MSN üzerinden, ya da mail araclığıyla beni uyarırdı. Ama hiç bir zaman arkadaşım yerinden kalkarak yanıma gelip bu uyarıyı yapmamıştı. İki masa arasında 3 metreyi bulmayan mesafeyi kat etmek yerine, online ortamdan haber uçururdu. İronik diyebilir miyiz?

Çalışma ortamlarımızın oluşturulmasında şirketlerin büyük payı var bildiğiniz üzere. Günümüz yazılım projeleri ve ihtiyaçları düşünüldüğünde en popüler oturma düzenlerinden birisi yonca şeklinde olanlar. Ancak bazı şirketlerde sizi oturduğunuz masanın içerisine neredeyse hapsedecek şekilde düzenlerde söz konusu. Örneğin sadece sağınızı ve solunuzu görebildiğiniz ama ön masanızda oturan şahsı göremediğiniz bir oturma düzeni de mevcut. (Daha kötüleri de yok mu? Var tabiki de) Tabi siz de ara sıra sağınızdaki veya solunuzdaki arkadaşınızla sohbet etmek, konuşmak ihtiyacı hissedebilirsiniz. Bu aslında son derece doğal bir insan tepkisidir. Ancak ne zamaki iletişiminiz sadece sabah "Günaydın" ve akşam "İyi Akşamlar" şeklinde olan üç basit ve çoğunlukla size söylemesi dahi zor gelen kelimelere düşer, o zaman bir şeylerin ters gittiğini kabul etmeli ve tedbirlerini almalısınız demektir. Çünkü gittikçe ofis masanızın belirlediği sınırlar içerisinde yaşayan ve o dünyaya inanan birisi olmaya başlamışsınızdır. Aslında o dünya değildir. Size göre koca bir evrendir. 

Buna itiraz edebilirsiniz ve hayır ben böyle bir insan değilim diyebilirsiniz. O zaman size bu durumu doğrulatacak bir deney önerebilirim. Portatif Web kameranızı alın ve masanın uygun bir yerine monte ederek sizin ofisteki bir günlük yaşantınızı çekmesini sağlayın. Sonrasında izleyin. Çok şaşırabilirsiniz. Özellikle çok vahim vakalarda, kaydı hızlı olarak ileri sardığınızda uzun aralıklarda aynı şekilde durduğunuzu fark edebilirsiniz. Özellikle gözlerinizi bir noktaya uzun süre sabitleyebildiğinizi, göz doktorlarının sık sık vurguladığı ve göz kuruluğuna sebebiyet verdiği için göz yaşı damlası kullanmanıza neden olan duruma sık sık düştüğünüzü net bir şekilde izleyebilirsiniz. Eğer bu noktaya geldiyseniz bir de şunu deneyin. Bilgisayarınızı kapatın ve siyah ekranına sadece 15 dakika boyunca bakmaya çalışın. Sıkıldınız mı? Allah allah...Neden acaba? Wink

Yaşananlardan : Bulunduğum yazılım şirketinde pek çok arkadaşımız ne yazık ki bu şekilde yaşıyor. Günün normal olarak 8 saatlik mesai dilimi içerisinde zorunlu olmadıkça kimseyle konuşmayan, çoğunlukla kendi zevkine uygun müziği dinleyen, üzerine yıkılan onlarca iş içerisinde sadece nefes alıp vermekten sorumlu olduğunu sanan ve sanki bir mahkumiyeti acı çekmeden yaşamayan çalışan insanlar görüyorum. Hatta yazımı düzenlediğim şu sırada bile halen görmekteyim.

Basit bir matematik hesabı yaptığımızda durumun vahimliği bir kere daha ortaya çıkıyor. Çevrenizdekiler ile günde sadece 30 kelime konuştuğunuzu düşünün. Haftada 5 gün çalıştığınızı...Ayda 20 gün...Senede 240 gün...240X30=7200 kelime. Bu sadece senelik olan çok kaba ve kötü bir tahmin. Peki ya ayda kaç satır kod yazıyorsunuz ve tahminen kaç kelime kod yazıyorsunuz. Bu şu anlama gelebilir. Çevrenizdekiler ile konuşmaktan çok kelime harcayarak kodlama yapıyorsunuz. Bu çok doğal olarak işiniz bir parçası. Ama artık sizin sadece bilgisayarınız ile konuşmayı tercih ettiğinizin de bir göstergesi. Peki bu disiplini ne kadar devam ettirebilirsiniz. Aslında sizce bu bir disiplin olabilir mi?

Yaşananlardan: Eski şirketimdeki patronum yazılımın bir yaşam tarzı olduğunu belirtmişti. Ancak bu yaşam tarzını devam ettirirken insan olmanın doğasında var olan bazı aktivitelerden de uzaklaşmamak gerektiğini sürekli ifade ederdi. Hatta bir keresinde başarılı yöneticilerin iş yaşantıları dışında spor, müzik gibi alanlarda amatör olarak bile olsa ilgilendiklerini söylemişti. İlgi derken fiili olarak katılmaktan bahsediyordu. Örneğin toplanıp basketbol oynamak, masa tenisi müsabakalarına katılmak, gitar çalmak, flüt üflemek, saksafon çalmak vb...Şu anda çalıştığım şirkette bu profile uyan bir çok arkadaşım var. Tabiri yerinde ise tüm iş streslerini spor veya müzik ile uğraşarak atabiliyorlar. Üstelik bu arkadaşlarımın bazıları(tamamı değil) gerçekten göz göze sosyalleşmeye inanan, sizinle konuşmaktan zevk alabilen ve sürekli masasında oturmayı sevmeyen kimseler. Tabi patronum bu tavsiyede bulunduğunda sene 1999 du ve ortalarda sosyal ağlar(Facebook, Twitter vb... gibi) diye bir gelişim bulunmamaktaydı.

Sosyal ağlar demişken onlara da dokundurmadan geçmemek lazım. Üniversite yıllarındayken ICQ, MIRC gibi programlar çok popüler idi. Hatta bir arkadaşımla BBS' ler üzerinden mesajlaştığımızı bile hatırlıyorum. Acaba kimse bu günlere gelebileceğimizi düşünüyor muydu? Artık Facebook, Twitter gibi sosyal ağlar söz konusu. Bir fayda olarak uzun zamandır görüşemediğiniz, belirli sebeplerden iletişiminizi kaybettiğiniz arkadaşlarınızı bulmak açısından oldukça etkili. Ya da ülkeler arası kendi mesleki gelişiminizle ilişkili olarak bir ağın parçası olmak istediğinizde son derece faydalı. Ancak şu durumu kim açıklayabilir; iki yan masanızdaki arkadaşınızın doğum gününü Facebook üzerinden kutlamak. Undecided Bunu ben dahil pek çoğumuz yapıyor. Şimdi yakın zamanda etrafınızda doğum günü olan ve Facebook listenizde yer alan bir arkadaşınızı düşünün. Eğer onunla küs değilseniz doğum gününde şunu yapın; oturduğunuz yerden kalkın, arkadaşınızın yanına yüzünüzde kocaman bir tebessüm ile gidin, elinizi uzatın tokalaşın ve yanaklarından öperek nice senelere demeyi bir deneyin. Bunu yapabilir misiniz? Yapın ve sonrasında nasıl hissettiğinizi düşünün. Yoksa sandalyenizde oturmayı mı tercih edersiniz.

Doğruyu söylemek gerekirse yazılımcıların dünyası 14.1 inch, bilemediniz 15.4 inch, en kötü ihtimalle 17 inch lik bir dünyadan ibaret olabiliyor. Konuşurken zar zor söyleyebileceğimiz, çekindiğimiz, korktuğumuz cümleleri dijital ortamda rahatlıkla ifade edebiliyoruz. Açıkçası klavyenin başındayken çok güçlüyüz. Ekranımızda istediğimiz gibi bir evren kurabilir galaksinin istediğimiz noktasına sıçrayabiliriz. Tabi yazının bu noktasında oluşan kişilik profiline yazılımcılar dışında pek çok insan uyabilir. Eminim ki çevrenizde gününü bilgisayar başında ve ağırlıklı olarak sosyal ağlar üzerinde geçiren pek çok genç vardır(Sizinde bunun için günde 8 saate yakın mesai harcayan kuzeniniz var mı?). Onları biraz gözlemleyin ve bazı çıkarımlar elde etmeye çalışın.

Yazılımcı psikolojisi ile ilişkili analizlerimi aktarmaya başlarken önce bulunduğumuz masadan başladık ve sosyal ağlara kadar uzandık. Yazılımcının kod tarafında döktürebilirken konuşma güçlüğü çektiğini, gözlerinde sürekli bir problem olduğunu fark ettik. Tabi bilgisayarı başında sakin sakin iş yapan bir yazılımcının çoğu zaman çileden çıkabileceğini ve olur olmadık şeylere kafayı takıp son derece sinir birisi olabileceğini de unutmamak gerekir. Aslında bir psikolog olsam bu durumdaki çoğu yazılımcıya "Agresif Kişilik Bölünmesi" teşhisi koymak isterim ki böyle bir teşhis olmadığından eminim. Yandaki resimde sinir bir yazılımcıyı motiflemeye çalıştım ama bulabildiğim tek resim bu oldu Undecided

Yaşananlardan : Siz hiç programdaki kodlaması sonrasında çalışma zamanında istisna(Exception) alan yazılımcı bir arkadaşınızın masasındaki bazı eşyaları sağa sola fırlattığını gördünüz mü? Daha önceden çalıştığım şirketlerden birisinde yine dış kaynak elemanı olarak bir projeye atanmıştım. O gün farkettiğim tek gerçek, çalışma arkadaşımın ekrana son derece yakın ve sinirli bir şekilde bakmasıydı. Sonunda olan oldu ve kodun çalışması sırasında bir istisna mesajı aldı. "Hay ben senin gibi kodun bippppp!!!" diyerek devam cümlesi bittiğinde göz göze geldik ve bende son derece yanlış bir şekilde "Ne o hocam kod mu patladı?" dedim...Özellikle genç yazılımcı arkadaşları bu konuda uyarmak isterim. Sakın kodunda exception alıp az önce bahsettiğim çılgın psikoloji içerisine giren bir arkadaşınıza bu şekilde yaklaşmayın. Hatta mümkünse kafanızı önünüze eğin ve kendi işinize bakın. Sonuçta olan oldu ve arkadaşım masasında duran 600 sayfalık C# kitabını alıp masama doğru fırlattı. Barmenlerin yaptığı gibi. Sonrasında da şunu söyledi "Al sen yaz o zaman..."

Aslında bu hikaye neredeyse bir yazılımcının hayatının pamuk ipliğine bağlı olabileceğini ifade etmektedir. Yazılımcının bir türlü dizginleyemediği kodunda meydana gelen istisnanın bedeli aslında kendi hayatında kontrol edemediği bir dizi olayın başlamasına neden olur. Çevrenize bakın ve böyle arkadaşlarınız var ise dikkatlice gözlemleyin. Yüzlerinden, üzerlerindeki negatif enerjiyi çok rahat bir şekilde görebilirsiniz. Oysaki yazılımcılar kod tarafında her zaman kral değil midir? En azından kendimizi hep bu şekilde görmez miyiz? Oysaki hayatımızı normal olarak devam ettirmemiz için gerekli şartları bile sağlamaktan, kontrol etmekten çoğu zaman aciz olabiliriz.

Bu noktada yazıyı artık sonlandırma ve bir sonuca bağlamak istiyorum ancak bu çok zor. Belkide sonuçları çıkartması gereken kişi siz değerli okurlarımdır. Yine de değişmez bazı gerçekler olduğunu kabul etmeliyiz. Yazılımcının bulunduğu ortamın koşulları, kendi iç dünyasında ortaya çıkartamadığı duyguları ve başka pek çok faktörün gün içerisinde kendi davranışları üzerinde pek çok etkisi olduğu yadsınamaz bir gerçek. Şimdi birde ekibindeki yazılımcıları yönetmeye çalışan ve bu noktada onların psikolojisinin çok önemli olduğunun bilincine varan teknik liderleri, proje yöneticilerini, müdürleri düşünün. İnanın işleri bizimkinden çok ama çok daha zor. Bence hepimizin eğitim hayatında insan psikolojisi üzerine gerçek hayat pratiklerinin aktarıldığı dersler olmalı.

Yaşananlardan : Üniversiteden mezun olduğumda o dönem popüler olan MBA yüksek lisnans programına katılmıştım. İK dersine giren hocamız çok değerli birisiydi. Pek fazla kitap kullanmazdı. Zaten ağzından çıkan cümleler düzgün, akademik ve tecrübe dolu olurdu. Açıkçası o cümleleri not ettiğinizde her dönem için elinizde bir kitap zaten yazılırdı. Bir gün dersimizde şunları söyledi ve bu sözler aklımda hiç çıkmadı; "Arkadaşlar...Konuşun...Konuşmaktan, insanlarla iletişim kurmaktan çekinmeyin. Fikirlerinizi düşüncelerinizi elinizden geldiğince düzgün ifadeler ile agresifleşmeden karşınızdakine aktarın. Dinlemesini bilin. Ve tabi en önemlisi; Güler yüzlü olmaktan, mevzularınızı tebessümle aktarıp dinlemekten çekinmeyin. Bunu öğrenin..." Aşağı yukarı bu şekilde bir konuşmaydı. Ana fikri/fikirleri görebildiniz mi? Belki biz yazılımcıları, işletme gibi alanlarda ilerleyen kişilerden ayırabilecek noktalar yer almakta. Güler yüzlü olmak, konuşlanlık, sevecenlik, sabırlılık, agresif olmamak vs...

Bir daha bu şekilde teknik dışı bir yazı yazar mıyım bilemiyorum ama bu tespitlerin sizi bir kaç dakikalığına düşündüreceğine inanıyorum. Hepimiz insanız ve bulunduğumuz Dünya' nın yaşayan birer parçasıyız. Hayatımız uzun görünse dahi çok kısa ve aslında her birimiz evrende bir toz parçasının milyarda birinden daha küçük olan parçaların yaşam süresinden bile kısa yaşıyoruz.

Bu yazıyı başarılı bir şekilde okudunuz. Şimdi sağınızdaki veya solunuzdaki, önünüzdeki veya arkanizdaki bir arkadaşınıza bakın ve tebessüm ederek gülümseyin, bir şeyler söyleyin. En azından halini hatırını sorun yahu!Laughing

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

WCF WebHttp Services - Client Tarafını Geliştirmek [Beta 2]

Cuma, 5 Şubat 2010 09:45 by bsenyurt

Merhaba Arkadaşlar,

Sanırım pek çoğumuz piyangodan veya diğer şans oyunlarından kendilerine tonlarca para çıksa ne yapacağını düşünmüş veya hayal etmiştir. Açıkası kendi adıma hayat etmediğimi dile getirsem yalan söylemiş olurum. Ancak ben pek çoğumuz gibi yan yana bir kaç Ferrari' yi dizmektense bir kaç yere yatırım yapmayı hayal etmişimdir hep. Örneğin dünyanın sayılı bir kaç futbol kulübünün(Barcelona, Manchester United vb...) ve yazılım şirketinin(Microsoft, IBM vb...) hisselerinden satın alır ve şöyle güzel bir fon sepeti oluştururum. Neyse...Sözü niye piyangodan açtığımıza gelince...

Hatırlayacağınız üzere bir önceki yazımızda WCF WebHttp Service' leri ile tanımaya çalışmış ve konuyu pekiştirmek amacıyla basit bir Merhaba Dünya uygulaması geliştirmiştik. Tabi bu örneğimizde HTTP protokolünün yanlızca Get metodunu kullanmıştık. Dolayısıyla operasyonlarımızda sadece WebGet niteliklerinin uygulandığına şahit olduk. Ancak HTTP protokolüne göre Get dışında Post, Put ve Delete metodlarını da kullanabileceğimizi biliyoruz. Dikkat çekici bir diğer noktada örneğimizde Get metoduna göre talepte bulunurken basit bir tarayıcı uygulamadan faydalanmış olmamızdı. Oysaki kendi istemci uygulamamızı yazmak isteyebiliriz. Bu durumda istemci tarafından HTTP protokolünün Get, Post, Put ve Delete metodlarına uygun talepleri nasıl gerçekleştirebiliriz? Aslında olay servis tarafının istediği mesaj paketlerini istemci tarafında oluşturup göndermekten başka bir şey değildir. Yani talebin(Request) içeriğini hazırlamak ve dönen cevabı(Response) değerlendirmek.

İşte bu yazımızda söz konusu durumları ele alaraktan hem Post, Put, Delete metodlarının kullanımına bir örnek verecek hemde istemci tarafını geliştirmeye çalışacağız. Tabi öncesinde servis tarafını hazırlamamız gerekiyor. Bu örneğimizde herhangibir işe yaramasada konuyu anlamamızı kolaylaştıracak bir senaryomuz da olacak. Senaryomuza göre bir Piyango servisi tasarlayacağız. Wink Bu servis, istemcilerin yeni bir piyango bileti üretebilmesine, var olan piyango biletlerini çekebilmelerine, isterlerse biletlerini silmelerine veya güncellemelerine izin veren operasyonlar içerecek. Tabiki bu operasyonlarda HTTP protokolünün Get, Post, Put ve Delete metodları göz önüne alınıyor olacak. Dilerseniz hiç vakit kaybetmeden WCF REST Service Application uygulamasını oluşturarak işe başlayalım. Uygulamamızda bilet bilgilerinin saklanması ve depolanması amacıyla basit text dosyasından yararlandığımızı belirtmek isterim. Diğer taraftan bilet bilgileri için Ticket isimli yardımcı bir sınıfımızda yer almaktadır.

using System;

namespace Lesson2
{
    public class Ticket
    {
        public string Number { get; set; }
        public string Owner { get; set; }
        public DateTime TicketDate { get; set; }
        public bool NewOrUpdated { get; set; }

        public override string ToString()
        {
            return String.Format("{0}|{1}|{2}|Is New? {3}", Number, Owner, TicketDate.ToString(),NewOrUpdated.ToString());
        }
    }
}

LotteryService isimli WCF WebHttp Service içeriği ise aşağıda görüldüğü gibidir.

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

namespace Lesson2
{
    [ServiceContract]
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
    public class LotteryService
    {
        string filePath = HttpContext.Current.Server.MapPath("~\\Tickets.txt");

        [WebGet(UriTemplate = "Lottery/{Name}/{LastName}")]
        public List<string> GetMyTickets(string Name,string LastName)
        {
            return (from line in File.ReadAllLines(filePath)
                    where line.Contains(Name + LastName)
                    select line).ToList();
        }

        [WebInvoke(UriTemplate = "Lottery/Create/{Name}/{Surname}", Method = "POST")]
        public Ticket CreateTicket(string Name,string Surname)
        {
            Ticket createdTicket=new Ticket
            {
                Number = Guid.NewGuid().ToString(),
                Owner = String.Format("{0}{1}",Name,Surname),
                TicketDate = DateTime.Now
            };

            File.AppendAllLines(filePath,new String[]{createdTicket.ToString()});
            return createdTicket;
        }

        [WebInvoke(UriTemplate = "Lottery/Update/{TicketNumber}", Method = "PUT")]
        public string UpdateMyTicketNumber(string TicketNumber)
        {
            string ticket = (from line in File.ReadAllLines(filePath)
                         where line.Contains(TicketNumber)
                         select line).First();
            string[] infos=ticket.Split('|');

            string updatedTicket=string.Join("|",Guid.NewGuid().ToString(),infos[1],infos[2],"Is New? ",true.ToString());

            File.AppendAllLines(filePath, new string[]{updatedTicket});

            return updatedTicket;
        }

        [WebInvoke(UriTemplate = "Lottery/Delete/{TicketNumber}", Method = "DELETE")]
        public void DeleteMyTicket(string TicketNumber)
        {
            string[] newLines = (from line in File.ReadAllLines(filePath)
                           where !line.Contains(TicketNumber)
                           select line).ToArray();

            File.WriteAllLines(filePath, newLines);
        }
    }
}

Kod parçamızda HTTP Get,Post,Put ve Delete metodlarının kullanımlarına örnek olması açısından çeşitli servis operasyonlarının yer aldığı görülmektedir. Kritik olan noktalar WebGet ve WebInvoke niteliklerinin nasıl kullanıldığıdır. Servisimizin yardım sayfasına bakıldığında, istemci tarafında oluşturulması gereken Request paketlerinin nasıl olacağıda kolaylıkla görülebilir. Tabi Post ve Put metodlarında bir Request Body kullanılmamıştır. Bir başka deyişle Put ve Post işlemleri için gerekli bilgiler servis tarafına URL satırından gönderilmektedir.

Gelelim istemci tarafına.

İstemciyi basit bir WinForms uygulaması olarak tasarlayacağız. Önemli olan nokta ise, az önce tasarlanan WCF WebHttp Service' ini nasıl kullanabileceğimiz. Sonuçta HTTP Get, Post, Put ve Delete metodlarının istemci tarafından hazırlanması ve gönderilmesi gerekmekte. Üstelik servise ait bir WSDL içeriği ve dolayısıyla Proxy üretimi de söz konusu değil. Bu noktada WebChannelFactory, HttpWebRequest ve WebClient tiplerinden yararlanabileceğimizi biliyoruz. Ne varki WCF Rest Starter Kit Preview 2 ile birlikte gelen HttpClient sınıfı tamda bu tip servislerin tüketilmesi için geliştirilmiş durumda. Elbette bu kit içeriğinin, .Net Framework 4.0' ın final sürümü ile birlikte içeriye doğrudan dahil edileceğini tahmin etmekteyiz. Şimdilik Starter Kit ile gelen tipi kullanacağız. Bu sebepten Windows uygulamamıza gerekli referansları aşağıdaki şekildende görüleceği üzere eklememiz gerekiyor.

Dikkat edilmesi gereken noktalardan biriside istemci uygulamanın hedeflediği Framework profilidir. Söz konusu Starter Kit referansları ile çalışabilmek için istemci tarafının hedef profilinin .Net Framework 4.0 Client Profile değil(ki varsayılanı budur) .Net Framework 4.0 olması gerekmektedir.

Artık istemci için gerekli tüm ön hazırlıklar yapılmıştır. Şimdi dilerseniz servis fonksiyonelliklerini icra edebilmek amacıyla Form içeriğini aşağıdaki gibi düzenleyelim.

Form üzerindeki kontrolleri kullanarak bilet üretebilecek, bir bileti silip güncelleyebilecek yada var olan biletlerimizi görebileceğiz. Tabiki tüm bu fonksiyonellikler LotteryService isimli WCF HttpWeb Service üzerinden gerçekleştiriliyor olacak. İlk olarak bir kişinin sahip olduğu tüm biletleri listlemeye çalışalım. Bu noktada servis tarafındaki GetMyTickets operasyonu için bir çağrı yapılması gerekiyor. Söz konusu çağrı örneğin http://localhost:16088/LotteryService/Lottery/Coni/Vayt şeklinde olabilir. Nitekim WebGet niteliğinde belirtilen URI bilgisi bu şekildedir. Bu durumda istemci tarafında aşağıdaki kodlamayı yapabiliriz.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using System.Xml.Linq;
using Microsoft.Http;

namespace ClientApp
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void btnGetMyTickets_Click(object sender, EventArgs e)
        {
            // REST Starter Kit Preview 2' den gelen HttpClient tipi oluşturulur.
            // Parametre olarak WCF WebHttp Service' in base address bilgisi verilir.
            using (HttpClient client = new HttpClient("http://localhost:16088/LotteryService/"))
            {
                // Get talebi için gerekli URI bilgisi oluşturulur
                string requestUri = String.Format("Lottery/{0}/{1}", txtName.Text, txtSurname.Text);
                // Get metodu ile ilgili talep oluşturulur.
                HttpResponseMessage responseMessage=client.Get(requestUri);
                // Operasyonun başarılı olmaması halinde bir exception fırlatılması isteniyorsa EnsureStatusIsSuccessful metodu kullanılmalıdır.
                responseMessage.EnsureStatusIsSuccessful();
                // İçerik XML tipinden geldiği için bunu kod tarafında kolayca ele almak adına ReadAsXElement genişletme metodu(Extension Method) kullanılabilir. Lakin bu metod için System.Xml.Linq isim alanının referans edilmesi gerekir.
                XElement response = responseMessage.Content.ReadAsXElement();
            }
        }
    }
}

Servisin çalıştığını varsaydığımızda yukarıdaki metodun icra edilmesi sonucu debug zamanında aşağıdaki sonuçlara ulaştığımızı görebiliriz. (Tickets.txt dosyasında bazı ticket bilgileri olduğunu  ve Name için Coni, Surname için Vayt bilgilerinin girildiğini varsayıyoruz)

Dikkat edileceği üzere servis tarafındaki operasyonda List<string> tipinden olan operasyon dönüş tipi istemci tarafına, ArrayOfstring ve string isimli alt elementlerden oluşan bir XML içeriği olarak aktarılmıştır. Elbette XElement içeriğinin bu şekilde elde edilebiliyor olması yeterli değildir. Bu içeriğin ListBox kontrolü içerisine serpiştirilmesini de bekliyoruz. Dolayısıyla aşağıdaki kod ilavesini de yapmamız gerekiyor.

XElement response = responseMessage.Content.ReadAsXElement();

lstMyTickets.Items.Clear();
var tickets = from node in response.Elements()
                  select node.Value;

foreach (var ticket in tickets)
{
    lstMyTickets.Items.Add(ticket);
}

Dikkat edileceği üzere basit bir XLINQ sorgusu ile tüm elementlerin Value değerleri çekilmiştir. Elbette bu XLINQ sorgu ifadesini belirleyen kriter, servis tarafından dönen XML içeriğinin şemasıdır. Çalışma zamanında örnek bir kullanıcı için Get sorgusunu gerçekleştirdiğimizde aşağıdaki ekran görüntüsündekine benzer sonuçları elde ettiğimizi görürüz.

Şimdi yeni bir biletin oluşturulması için gerekli kodları yazalım.

private void btnCreateTicket_Click(object sender, EventArgs e)
        {
            using (HttpClient client = new HttpClient("http://localhost:16088/LotteryService/"))
            {
                string requestUri = String.Format("Lottery/Create/{0}/{1}", txtName.Text, txtSurname.Text);

                // Yeni bir Ticket oluşturmak için gerekli istek HTTP Post metoduna göre yapılmaktadır. Bu sebepten HttpClient tipinin Post metodu kullanılmıştır.
                // Gönderilen talepte herhangibir Request Body içeriği olmadığından HttpContent tipinin CreateEmpty metodu kullanılmıştır.
                HttpResponseMessage responseMessage = client.Post(requestUri, HttpContent.CreateEmpty());
                responseMessage.EnsureStatusIsSuccessful();
                // Oluşturulan yeni Ticket bilgisi istemci tarafına yine bir XML içeriği olarak dönmektedir. Üretilen içerik bilgi amaçlı olarak kullanıcıya gösterilir.
                XElement createdTicket = responseMessage.Content.ReadAsXElement();
                MessageBox.Show(createdTicket.ToString());
            }
        }

Get kullanımına benzer olmakla birlikte bu kez HttpClient tipinin Post metodundan yararlanılmaktadır. Post ve Put gibi metodlarda Request Body' sinin olması gerekebilir. Ancak bizim servis operasyonlarımız Request Body kullanmamaktadır. Bu nedenle ilgili parametreler HttpContent.CreateEmpty() metodu ile geçilmektedir. Bu kod parçasına göre çalışma zamanında bir bilet üretmek istediğimizde geriye aşağıdakine benzer sonuçların aktarıldığını görebiliriz.

Peki ya silme ve güncelleme işlemlerinden ne haber? Bu operasyonlar için istemci tarafında aşağıdaki kodları yazmamız yeterli olacaktır.

private void btnDeleteTicket_Click(object sender, EventArgs e)
        {
            // Öncelikle ListBox' ta seçili bir öğe olup olmadığına bakılır
            if (lstMyTickets.SelectedItem != null)
            {
                // Biletin numarası yani GUID bilgisi alınır.
                string ticketNumber = lstMyTickets.SelectedItem.ToString().Substring(0, 36);
                using (HttpClient client = new HttpClient("http://localhost:16088/LotteryService/"))
                {
                    // HTTP Delete metoduna göre bir talepte bulunulur.
                    string requestUri = String.Format("Lottery/Delete/{0}", ticketNumber);
                    // Delete talebi için HttpClient tipinin Delete metodundan yararlanılır. Bu metodun kullanımına göre herhangibir HTTP Request Body içeriği bildirilmesi gerekli değildir.
                    HttpResponseMessage responseMessage = client.Delete(requestUri);
                    responseMessage.EnsureStatusIsSuccessful();
                   
                }
            }
        }

        private void btnUpdateTicket_Click(object sender, EventArgs e)
        {
            if (lstMyTickets.SelectedItem != null)
            {
                string ticketNumber = lstMyTickets.SelectedItem.ToString().Substring(0, 36);
                using (HttpClient client = new HttpClient("http://localhost:16088/LotteryService/"))
                {
                    // Update talebi hazırlanır
                    string requestUri = String.Format("Lottery/Update/{0}", ticketNumber);
                    // Güncelleme isteği aslında HTTP Put metoduna karşılık gelmektedir. Bunun için HttpClient tipinin Put metodundan yararlanılır.
                    HttpResponseMessage responseMessage = client.Put(requestUri, HttpContent.CreateEmpty());
                    responseMessage.EnsureStatusIsSuccessful();
                    // Put metodunun çalıştırılması sonucu üretilen çıktı bu kez string bazlı olacak şekilde bir MessageBox aracılığıyla gösterilir.
                    MessageBox.Show(responseMessage.Content.ReadAsString());
                }
            }
        }

Örneğin var olan bir biletimizi güncellemek istediğimizi düşünelim. Örnek çalışma zamanı görüntüsü aşağıdakine benzer olacaktır.

Tabi uygulamamızın pek çok yerinde bug ve iş mantığı hatası vardır. Üstelik tam anlamıyla bir istisna yönetimide(Exception Handling) yapılmamaktadır. Ancak odaklanmamız gereken yada dikkat etmemiz gereken noktalar WCF WebHttp Service üzerinden Post, Put, Delete operasyonlarının nasıl sunulduğu ve istemci tarafında bunların nasıl ele alındığıdır. Özellikle istemci tarafında kullandığımız tekniklere baktığımızda şu sonuçlara varabiliriz;

  • Herhangibir proxy tipi kullanılmamıştır. Bilindiği üzere servisleri tüketmenin yollarından birisi istemci tarafında gerekli proxy tipinin üretilmesidir. HTTP Get,Post,Put ve Delete metodlarının kullanıldığı teknikte ise sadece paketlerin oluşturulup gönderilmesi ve cevapların değerlendirilmesi gerekmektedir.
  • Sonuçların ilgili formata göre istemci tarafına gönderilmesi söz konusudur. Varsayılan olarak XML tipinden içerik döndürülmektedir. Ancak JSON formatında da dönüşler olabilir.
  • Get metodundan elde edilen XML formatlı içerikler istemci tarafında XElement tipi ile ele alınabilir ve XLINQ kullanılarak ayrıştırılabilir.
  • İstemci tarafından yapılan güncelleştirme çağrıları için Put, ekleme işlemlerine ait talepler için Post, silme işlemlerine ait talepler için Delete metodları kullanılır.
  • Post ve Put metodları parametre olarak eğer gerekliyse bir Body içeriği sunmak zorunda olabilirler. Eğer sunmuyorlarsa HttpContent.CreateEmpty() metodu ile boş içerik gönderileceğinin belirtilmesi gerekir.

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

Lesson2.rar (196,61 kb)

WCF WebHttp Services - Tanışma [Beta 2]

Pazartesi, 1 Şubat 2010 14:50 by bsenyurt

Merhaba Arkadaşlar,

Nihayet taşlar yerli yerine oturmaya başladı. 2008 yılında düzenlenen Microsoft PDC' de  tanıtılan sürüm ile başlayan macerada Beta 1, Beta 2 versiyonları derken yavaş yavaş RC, RTM sürümlerinin çıkacağı günlere gelmekteyiz. Elbette hepizimin beklentisi bir an önce stabil bir sürüme kavuşabilmek. Bu günlerde çok doğal olarak .Net Framework 4.0 ve Visual Studio 2010 ürünlerinin sınırlarının daha da netleştiğini görmeye başladık. Her ne kadar henüz yayınlanmış yeni bir sürüm olmasa da, pek çok güncel ve geçerli kaynaktan okuduğumuz kadarı ile bu böyle. Taşların yerli yerine oturmaya başladığı ve herşeyin biraz daha belirginleştiği alanlardan biriside Windows Communication Foundation 4.0.

Hatırlayacağınız üzere WCF Eco System' i anlattığımız yazımızda, WCF alt yapısı üzerine geliştirilen ve amaca yönelik olarak farklılaştırılan servis geliştirme modellerine değinmiştik. Bunlardan biriside WebHttp Services idi. Bu yazımız ile birlikte WebHttp Service' lerini tanımaya çalışacağız. Aslında WCF 3.5 sürümüne kazandırılan Web programlama teknikleri sayesinde zaten uzun bir süredir farkında olduğumuz non-SOAP bazlı bir modelden bahsediyoruz. Bildiğiniz üzere WebGet ve WebInvoke isimli nitelikler(attribute) yardımıyla servis operasyonlarının HTTP Get,Post,Put ve Delete metodlarına cevap verebilecek şekilde tasarlanması mümkün. Ancak zaman ilerledikçe REST(REpresentational State Transfer) modeline göre WCF servislerinin daha kolay geliştirilmesini sağlayan ve WCF 4.0 içerisinde gömülecek yeni özelliklerin bir ön görünümünü bizlere sunan WCF REST Starter Kit ile karşılaştık.

WCF Eco System' in bir parçası olan WebHttp Service' ler, .Net Framework 3.5 ile gelen Web Programlama modeli, REST Starter Kit ile tanıtılan kabiliyetler ve bunlara ek yeni özelliklerin .Net Framework 4.0 içerisinde ele alınmasını sağlayan bir geliştirme alt yapısı olarakta düşünülebilir. Şimdi dilerseniz WebHttp Service' lere bir merhaba demeye çalışalım. Örneğimizi Visual Studio 2010 Ultimate Beta 2 sürümü üzerinden geliştirmeye çalışıyor olacağız. Ancak yazıyı hazırladığım tarihte araştırdığım MSDN, The .NET Endpint vb blog sitelerinde yer alan bilgilere göre Visual Studio 2010' un o anki sürümü üzerinde WebHttp Service' leri için bir proje şablon(project template) bulunmamaktaydı. Bu nedenle öncelikli olarak online template' lerden WebHttp Service için olanları indirmemiz gerekiyor. Bu amaçla Visual Studio 2010 ortamında Tools->Extensions Manager->Online Gallery kısmına geçiş yapıp WCF ile ilişkili olan şablonlardan WCF Rest Service Template' i indirmemiz gerekiyor. Ben 4.0 versiyonunun C# programlama dili destekli olanını indirdim. Son sürümde büyük ihtimalle şu anda online olarak indirdiğimiz bu şablonun ve başka diğer şablonların Visual Studio 2010 içerisine gömülü olarak geleceğini ümit etmekteyim.

Download ve Install işlemlerinin ardından yolumuza devam edebiliriz. Bu amaçla, Visual Studio 2010 ortamında yeni bir proje oluşturup, Web sekmesinde yer alan WCF Rest Service Application şablonunu seçmemiz yeterli olacaktır. Bu şablonun kurulum işlemi sonrasında çıkmaması olasıdır. Bu durumda New Project iletişim kutusunda yer alan Enable the loading per-user extensions bağlantısını kullanarak etkinleştirme işlemini yapmamız yeterli olacaktır. Visual Studio 2010 ortamımızı tekrardan açtığımızda aşağıdaki ekran görüntüsünde olduğu gibi yeni proje şablonunun kullanabilir olduğunu göreceğiz.

HelloWebHttp isimli servis uygulamasına ait Solution içeriğinin ilk etapta otmatik olarak aşağıdaki gibi oluşturulduğunu gözlemleyebiliriz.

Service1.cs isimli örnek dosya içerisinde servis sözleşmesi(Service Contract) yer almaktadır. Bu sözleşme içerisinde yer alan metodlara WebGet ve WebInvoke niteliklerinin(Attributes) uygulandığı görülmektedir. Bu nitelikler bildiğiniz üzere servis operasyonlarına HTTP Post,Get,Put,Delete çağrılarının yapılabilmesi için gereklidir. Sınıf içerisine Get, Post, Put ve Delete metodlarının her biri için örnek nitelik kullanımları serpiştirilmiştir. Ayrıca serileştirilebilir örnek bir tipte yer almaktadır(SampleItem). GetCollection operasyonu SampleItem tipinden generic bir listeyi HTTP Get metoduna göre döndürmektedir. Create servis operasyonu ile yeni bir SampleItem nesnesinin HTTP Post metoduna göre örneklenmesi sağlanır. Tek bir SampleItem nesne örneğinin elde edilmesi için Get isimli servis operasyonunun aşırı yüklenmiş diğer bir versiyonu kullanılmaktadır. Update servis operasyonu ile HTTP Put metoduna göre güncelleme işlemi yapılmakta olup, Delete servis operasyonuda bir SampleItem nesnesinin HTTP Delete metoduna göre silinmesini sağlamaktadır. Solution içerisinde dikkat çekici bazı noktalar bulunmaktadır;

  • Örneğin web.config içeriği. Burada WCF 4.0 ile birlikte gelen basitleştirilmiş konfigurasyon(Simplified Configuration) özelliklerine yer verilmiştir. Bu sebepten daha sade, okunaklı ve fonksiyonel bir konfigurasyon içeriği oluşmuştur.
  • Dikkat çekici bir diğer noktada global.asax dosyasının var olmasıdır. Aslında bu bir web uygulaması olduğu için son derece normaldir. Lakin gözden kaçırılmaması gereken bir gerçek vardır; svc uzantılı bir servis dosyası fiziki olarak yoktur. Çünkü Asp.Net 4.0 Routing özelliği kullanılmaktadır. Çok tabi olarak yönlendirme işlemleri için global.asax dosyasında yapılması gereken bazı işlemler vardır. Bu nedenle hazır olarak global.asax içeriği aşağıdaki gibi üretilmektedir.

  • Yine servis kodlarına baktığımızda OperationContract niteliğinin kullanılmadığını görürüz. Oysaki .Net 3.5 ve WCF Rest Starter Kit sürümlerine baktığımızda WebGet ve WebInvoke nitelikleri dışında OperationContract niteliğininde kullanılması gerektiğini bilmekteyiz. OperationContract servis sözleşmelerinden sunulan operasyonların WCF çalışma zamanına bildirilmesinde rol oynadığı için bu son derece doğaldır. Ne varki WCF WebHttp servislerinde OperationContract niteliği opsiyoneldir.

Şimdi servis uygulamamız üzerinde bir kaç küçük değişiklik yapalım. Öncelikli olarak hayatımızı kolaylaştırmak adına Entity Framework' ten yararlanalım ve meşhur Chinook veritabanını ve işlemleri basit bir biçimde ele almak için sadece Artist tablosunu kullanmak istediğimizi düşünelim. Gerçi bu noktadan sonra biraz WCF Data Service' lere doğru kaymaya başlamış oluyoruz ancak amacımız tabiki HTTP Get,Post,Put ve Delete işlemlerini kendi kontrolümüz altında geliştirmek. 

Not : Tam bu noktada geliştirilen uygulamanın Data Service' ten veya RIA Service' ten ne farkı kaldığı sorusu akla gelebilir. Wink WCF WebHttp servislerinde asıl nokta operasyonun non-SOAP olacak şekilde sunulması(yani HTTP Get,Post,Put,Delete) ayrıca URI, format, protocol gibi bilgilerin tamamen geliştirici kontrolü altında olmasıdır.

Şimdi servis kodlarını aşağıdaki gibi geliştirdiğimizi varsayalım.

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

namespace HelloWebHttp
{
    [ServiceContract]
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
    public class HelloService
    {
        [WebGet(UriTemplate = "ArtistList")]
        public List<Artist> GetAllArtists()
        {
            ChinookEntities entites = new ChinookEntities();
            return entites.Artist.OrderByDescending(a => a.Name).ToList();
        }

        [WebGet(UriTemplate = "Artist/{name}")]
        public List<Artist> FindArtists(string name)
        {
            ChinookEntities entities = new ChinookEntities();

            return (from artist in entities.Artist
                    where artist.Name.Contains(name)
                    select artist).ToList();
        }

        [WebGet(UriTemplate = "Artist/InRange?idFirst={firstId}&idSecond={secondId}")]
        public List<Artist> GetArtistsInRange(int firstId, int secondId)
        {
            ChinookEntities entities = new ChinookEntities();
            return (from a in entities.Artist
                   where a.ArtistId >= firstId && a.ArtistId <= secondId
                   select a).ToList();
        }
    }
}

Dikkat edileceği üzere sadece HTTP Get metodlarının çalışmasına yönelik 3 örnek operasyon yer almaktadır. WebGet niteliklerinde UriTemplate özelliklerine atanan değerler yardımıyla HTTP Get taleplerinin nasıl olması gerektiği belirlenmektedir. Servise gelen taleplerin Routing sürecine dahil olması için global.asax.cs kodlarında da aşağıdaki değişiklikleri yapmamız yeterli olacaktır.

using System;
using System.ServiceModel.Activation;
using System.Web;
using System.Web.Routing;

namespace HelloWebHttp
{
    public class Global
        : HttpApplication
    {
        void Application_Start(object sender, EventArgs e)
        {
            RegisterRoutes();
        }

        private void RegisterRoutes()
        {
            RouteTable.Routes.Add(new ServiceRoute("Chinook", new WebServiceHostFactory(), typeof(HelloService)));
        }
    }
}

Dikkat edileceği üzere http://makineadı:portnumarası/Chinook üzerine gelen talepler sonrasında, WebServiceHostFactory' nin ayağa kaldırılması ve HelloService sınıfının örneklenerek işleme alınmasının sağlanması gerçekleştirilmektedir. Artık testlerimize başlayabiliriz.

Not: Dilerseniz Web uygulamasını IIS üzerinden host ederekte deneyebilirsiniz. Ancak ister Asp.Net Development Server ister IIS olsun, URL satırında RegisterRoutes metodunda yer alan Chinook bilgisini kullanmamız servise ulaşmamız için yeterli olacaktır.

Tabi test derken ilk etapta servis operasyonlarını nasıl çağırabileceğimizi bilemeyebiliriz. Yada bulmak için araştırmaya üşenebiliriz. Embarassed İşte bu amaçla WCF tarafına gelen Auto Help yetenekleri sayesinde çalışma zamanında yardım sayfasına gidebilir ve servis operasyonlarını nasıl çağırabileceğimizi, içeriklerinin ne olacağını görebiliriz.. Aynen aşağıdaki ekran görüntüsünde olduğu gibi.

İlk olarak belirli bir kelimeyi içeren Artist listesini elde etmek istediğimizi düşünelim. Örneğin adında Milton kelimesi geçenleri bulmak istiyoruz. Bu durumda URL satırında Chinook/Artist/Milton yazmamız yeterli olacaktır. Sonuçlar aşağıdaki ekran görüntüsünde olduğu gibidir.

Eğer tüm Artist listesini elde etmek istiyorsak bu durumda URL satırından Chinook/ArtistList bilgisini girmemiz yeterli olacaktır. Bu durumda elde edilen sonuçlar aşağıdaki ekran görüntüsünde olduğu gibidir.

Son olarak ArtistId değer aralığına göre Artist listesini elde etmek istediğimizi düşünelim. Bu durumda URL satırından Chinook/Artist/InRange?idFirst=155&idSecond=158 gibi bir URL satırı girmemiz yeterli olacaktır ki buna göre örneğin ArtistId değerleri 155 ile 158 aralığında olanların listesini aşağıdaki ekran görüntüsünde olduğu gibi elde edebiliriz.

Oldukça basit ve etkili değil mi? WCF WebHttp Service' ler ile ilişkili incelemelerimize devam ediyor olacağız. Tekrardan görüşünceye dek hepinize mutlu günler dilerim.

HelloWebHttp.rar (31,23 kb) [Örnek Visual Studio 2010 Ultimate Beta 2 sürümü üzerinde geliştirilmiştir]

Correlation Nedir? Yenir mi? İçilir mi?

Pazartesi, 1 Şubat 2010 09:25 by bsenyurt

Merhaba Arkadaşlar,

Bazen bir kavramı yada konuyu anlamakta inanılmaz zorlandığınızı hatırlayın. Ne yaparsınız? Kimisi kendisini yemeğe verir. Kimisi hayat küsermişçesine bir köşeye çekilir. Kimisi kendiyle baş başa kalır ve çığlık çığlık haykırır. Kimisi de daha akıllı davranıp bir süre tatile çıkar veya anlayamadığı kavramla ilişkili herhangibir dökümanı bir süreliğine araştırmamaya, okumamaya karar verir. Neredeyse unuturcasına bir zaman koyar araya. Sonrasında ise aynı konuyu tekrar araştırmaya karar verir. İnanın başarılı olma şansı bir önceki denemeye göre çok daha yüksek olacaktır. Önemli olan noktalardan birisi, yılmadan bu iterasyona devam edebilmektir. Okudunuz, hala anlamadınız...Kısa bir ara daha...Sonra tekrar aynı konu ama mümkünse farklı kaynaklarla...Wink

Bende bir süredir Workflow Service' lerde oldukça önemli olan konulardan birisi üzerinde araştırmalarımı tamamen durdurmuştum. Correlation. Çünkü; Matematikte "bağlılaşım/korelasyon", ekonomide "bağlanım", nükleer bilimlerde "bağlantı/eş ilişki", denizbilimde "kaçınım", tıpta "Aferent uyarıların gerekli cevabı oluşturmak üzere beyinin ilgili merkezinde birleşmesi" olarak çevirileri yer alan bu kavramın, Workflow Services içerisinde ne anlama geldiğini anlamak için epey bir süre tepinmem gerekmişti. Geçtiğimiz günlerde aynı konu üzerinde yeniden durmaya ve araştırmaya ve edindiğim bilgileri sizlere paylaşmaya karar verdim. İşte elde ettiğim sonuçlar;

Correlation kavramını mesajları bir arada gruplamanın bir yolu olarak düşünülebilir ilk etapta. Örneğin bir talep(Request) ve bu talebe karşılık gönderilen cevap(Reply) arasındaki ilişki Correlation olarak ifade edilmektedir. Özellikle WCF(Windows Communication Foundation) tarafında Session bazlı haberleşmelerde mesajlar arasında bir Correlation oluştuğu söylenebilir. Ancak Correlation' ın farklı bir yönü daha vardır. Bir servis örneği(Instance) ile bir oturum(Session) arasında da bağıntı kurulabilir. Yani bir SessionId değerine ait olaraktan hareket eden mesajların içeriğinde yer alan bazı veri parçalarının, Session ile alakalı bir servis örneği ile eşleştirilmesi mümkün olabilir. Bir başka deyişle aynı SessionId değeri altındaki mesajların her zaman için aynı servis örneğine ait olduğunun anlaşılmasında, SessionID=Service Instance ID eşitliğinin sağlanması da Correlation olarak ifade edilebilir. Bu ilişki çoğunlukla bilinçsiz(Implicit) olarak sağlanır. Yani, geliştiricinin çoğu zaman bir aksiyonda bulunmasına gerek yoktur. Aslında bu durumu aşağıdaki şekilde olduğu gibi canlandırabiliriz.

Ancak Workflow örneklerinde uzun zaman süren süreçlerin ele alınması da söz konusudur(Long Running Process). İstemcilerin, Workflow Service' ler ile olan haberleşmelerinde aradaki oturumu kapatıp ayrılmaları bu tip süreçlerde son derece yaygındır. Buna göre istemci ile servis arasındaki oturumun her an sonlanabilir olması önemli bir sorunu ortaya çıkarmaktadır; Correlation nasıl sağlanacak? Undecided

Bu amaçla Workflow Service' lerde Correlation' ın sağlanması için kullanılan çeşitli teknikler mevcuttur. Aslında burada da tam bir kavram kargaşası vardır. En güvenilir kaynaklardan birisi olarak ele alacağımız MSDN, Correlation' ı Content-Based ve Protocol-Based olmak üzere iki çeşide ayırmıştır. Protocol-Based Correlation' da kendi içerisinde Context ve Request-Reply isimli iki farklı Correlation tekniğini daha barındırmaktadır.

Content-Based Correlation' da service örneği(Instance) ile ilişkili olan veri(Map edilmiş veri olaraktan da düşünebiliriz) mesajın içeriğinde yer alır. Örneğin mesajın Header veya Body kısımlarında bulunabilir. Dolayısıyla Correlation' ı sağlayan arabirimlerin XML tabanlı olan bu veri içeriğini kontrol edebilmesi gerekmektedir. İşte bu noktada XPath gibi sorgu teknikleri devreye girmektedir. Protocol-Based Correlation ise iletişim mekanizmasını baz alır ve buna göre mesajlar arasında yada mesajlar ile doğru servis örneği arasında gerekli eşleştirmeyi sağlar.

Bu giriş yazımızda özellikle Content-Based Correlation üzerinde durabiliriz. En çok örneklenen model genellikle budur. Workflow Service' lerde servise gelen ve servisten giden mesajların içerdiği veri parçaları ile çalışma zamanındaki servis örnekleri arasında eşleştirme yapmak(yani Correlation' ı sağlamak) son derece kolaydır. Tüm mesajlaşma aktivitelerinin CorrelationInitializers isimli bir koleksiyonu bulunur. Bu koleksiyon içerisinde Key-Query çiftleri yer almaktadır. Tahmin edileceği üzere Key değerleri ile Query' lerin birbirlerinden ayrıştırılması sağlanır. En önemli nokta ise Query' dir. Query içerisinde yer alan XPath sorgusu ile Content içerisindeki veri işaret edilir. Tabi bir mesajlaşma aktivitesi, diğer bir mesajlaşma aktivitesinin başlattığı Correlation' u takip edebilmelidir. Bunun içinde CorrelationWith isimli özellikten yararlanılır. Bu bilgilere göre Correlation' ın bir şekilde başlatılması(Initialize) ve takip edilmesi(Follow) gerektiği anlaşılmaktadır. Başlatılan bir Correlation' ın takip edilmemesi halinde mesajlar ve servis örneği arasında bir eşleştirmenin yapılması söz konusu olmayacaktır. Elbette Workflow Foundation 4.0 içerisinde bir Correlation' ın başlatılmasını kolaylaştıran bir aktivitede gelmektedir. InitializeCorrelation bileşeni.

Sanırım şu ana kadar anlattıklarımız ile kafamızda Correlation' ın ne olduğuna dair bir fikir oluşmuştur. Tabi konuyu kavramak tek başına yeterli değildir. Pratiğe dökmemizde yarar vardır. Ancak şu an için bu konudaki araştırmaya ara verip tatile çıkmak niyetindeyim. Tekrardan görüşünceye dek hepinize mutlu günler dilerim.

Hayati Şenyurt

Cuma, 29 Ocak 2010 00:00 by bsenyurt

Arkadaşlarının ve dostlarının kıymetini bil. Onlara iyilik yapmaktan, onları sevindirmekten çekinme...Karşılık görmesen bile...












Burak Selim Şenyurt' un babası Hayati Şenyurt (1936 - 1999)

 

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

WCF RIA Services - Alan Bazlı(Field Based) Rol Kontrolü [Beta 2]

Çarşamba, 27 Ocak 2010 11:33 by bsenyurt

Merhaba Arkadaşlar,

Hani 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ı. 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 Amerika' daki bir arkadaşımın hediye ettiği Newyork Nicks 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 NASA'ya bile gidebilirim diye düşünüyordum Tongue out 

Ancak gecenin ilerleyen saatlerinde değil NASA, 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ı. Laughing 

İşte geçen gün yine böyle dahiyane bir fikir gelir mi aklıma diye düşünürken, WCF RIA Servis operasyonlarından dönen Entity 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 görülmemesi veya kullanılamaması istenebilir. Malum DomainService tipi içerisinde yer alan operasyonlarda Login 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 AdventureWorks veritabanı içerisinde yer alan SalesOrderDetail isimli tabloyu kullanmak istediğimizi düşünelim.

Örnek senaryomuzda çok doğal olarak Authentication alt yapısınında tesis edilmiş olması gerekmektedir. WCF RIA Service' lerinde Authentication Domain Service kullanımından daha önceki yazılarımızda bol bol bahsettiğimizden bu detayları atlıyoruz. Ancak ASP.NET Membership tabanlı olarak kurulan Authentication alt yapısı içerisinde testler için iki rol olduğunu söyleyebiliriz. AuthorizedSalesPerson ve JuniorSalesPerson. Senaryomuza göre güya AuthorizedSalesPerson rolünden gelen kullanıcılar UnitPriceDiscount alanının değerini görebilirken, JuniorSalesPerson rolündeki kullanıcılar göremeyecektir. Başlangıçta AdventureDomainService isimli Domain Service sınıfını aşağıdaki gibi düzenlediğimizi düşünelim.

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);
        }
    }
}

Kişisel Not: 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 edilmemelidir. 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.

İ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.

Ancak örnek senaryomuza göre JuniorSalesPerson rolünde olan bill isimli kullanıcının UnitPriceDiscount alanını görmemesi veya anlamlandıramaması gerekmektedir.(Anlamlandıramamasının ne kadar zor olduğunu biraz sonra anlayacağız) 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? Wink 

Sonuçta istemci ve sunucu tarafında eş olan SalesOrderDetail Entity 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. (Bu konu ile ilişkili olaraktan yaptığım araştırmalarda, blog girdisini hazırladığım tarih itibariyle Brad Abrams' ın ilgili yazısında bu tip bir teknik uygulandığını gördüm) Tabi örneğimizdeki alan null değer almamaktadır. Buna göre belki -1 değer atanması sağlanabilir. Ama bu durumdada alan yine görülebilir olacaktır. Undecided Aşağıdaki kod parçasını göz önüne alalım.

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;
        }

Bu kod parçasında görüldüğü gibi resultSet gönderilmeden önce her bir satırı taranmakta ve ServiceContext üzerinden elde edilen güncel kullanıcının rolüne bakılarak UnitPriceDiscount alanına -1 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.

Peki istediğimiz bu muydu?

Kesinlikle değil. Bizim hayalimiz ilgili alanın istemci tarafından görülmemesini sunucudaki operasyon üzerinden sağlamaktı. Oysaki öğrenebildiğimiz sadece şu oldu; servis tarafındaki operasyondon dönen resultSet içeriğindeki veriyi istersek Login 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. Sealed

Peki ya çözüm?

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...

Anlaşılan bu konuda WCF RIA Services 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.

WCF RIA Services - Custom Authorization [Beta 2]

Salı, 26 Ocak 2010 10:52 by bsenyurt

Merhababa Arkadaşlar,

Geç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. 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ı. 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 WCF RIA Service' lerinde özel yetkilendirme niteliklerinin(Custom Authorization Attributes) 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 MSDN' di.

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, nitelik(Attribute) yardımıyla çalışma zamanının nasıl davranacağının belirlenebilmesidir. Ancak bazen, yetkilendirme(Authorization) 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. Ç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 türetme(Inherit) 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.

Senaryomuza göre Domain Service 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 ASP.NET Membership 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 XML veya basit Text formatında dahi tutulabilir. Hatta olayı biraz abartıp Windows Registry ayarlarında dahi tutulması ve benzer saklama alanları düşünülebilir. 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 Text dosyada düzenli olarak tutulduğunu varsayıyor olacağız. Buna göre System.Web.DomainServices.AuthorizationAttribute niteliğinden türettiğimiz tipin içerisinde yer alan Authorize 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 CheckBannedListAttribute sınıfı içeriğimiz.

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);
        }
    }
}

CheckBannedListAttribute sınıfı AuthorizationAttribute tipinden türemektedir. Buna göre sınıf diagramı görüntüsünden de fark edileceği üzere, abstract olan Authorize metodunu ezmek zorundadır. Authorize metodu, niteliğin uygulanacağı metodlar için gerekli yetki kontrolü operasyonunu üstlenmektedir. Dikkat edileceği üzere Authorize metodu parametre olarak tanıdık bir arayüzü almaktadır; IPrincipal. 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. Biz örnek senaryomuza göre Login olan kullanıcı adının yasaklı listenin tutulduğu text tabanlı dosya içerisinde olup olmadığını incelemeyi hedefliyoruz. Bu sebepten Authorize metodu içerisinde BannedList isimli ve sunucu proje içerisinde tutulan text tabanlı dosyanın tüm satırlarının yüklenmesi ve elde edilen dizi içerisinde olup olmadığına göre bool tipte bir sonucun döndürülmesi söz konusu. Yazmış olduğumuz özel nitelik tipini sunucu tarafında yer alan ChinookDomainService sınıfı içerisinde uygulamamız ise son derece kolay.

Kişisel Not :Örneğimizin kalan kısmı daha önceki yazımızda geliştirdiğimiz ile benzer. Bu nedenle detaya girerek konudan uzaklaşmamayı tercih etmekteyim.

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;
        }
    }
}

Görüldüğü gibi CheckBannedList niteliği, yasaklı liste yetki kontrolü yapılmak istenen operasyonun üzerinde uygulanmaktadır. Buna göre çalışma zamanında Login olan bir kullanıcının söz konusu GetAlbums operasyonunu talep etmesi halinde devreye girecektir. Söz gelimi text dosyamız içerisinde aşağıdaki isimlerin yer aldığını varsayalım.

Buna göre örneğin buraks isimli kullanıcı ile Login 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.

Görüldüğü üzere Access Denied 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(örneğin senaryomuza göre bill olabilir) 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.

Sonuç olarak eklediğimiz özel authorization niteliği yardımıyla, Domain Service ü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.

CustomAuthorization.rar (1,29 mb)

Karlı Kış Günü Reçetem

Pazar, 24 Ocak 2010 20:20 by bsenyurt

Malum bu hafta sonu hepinizin bildiği üzere tüm yurdu karlı ve sert rüzgarlı bir hava etkisi altına aldı. Yarı yıl tatiline giren çocuklar için mutluluk verici bir olay olmakla birlikte, benim gibi böylesine karlı, buzlu, tipi şeklinde rüzgarlı bir havada dışarı çıkmayı tercih etmeyen/üşenen ve sıcak bir ortamda olduğuna, elinin altında bir sürü imkan bulunduğuna şükredenlerin olduğuna da eminim.

Peki bir yazılımcı olarak böyle bir günde neler yapılabilir? Gerekli malzemelerimizin başında elbetteki sağlam bir internet bağlantısı olmasında yarar var. Ancak tüm vaktimizi internette geçireceğimizi de söylemiyorum elbette. Buna ilaveten çay, kahve gibi bilimum içeçeklerimiz de varsa eğer süper. Deymeyin keyfimize...

Sizde güne benim gibi son okuduğunuz kitaba devam etmek isteyerek başlayabilirsiniz. İşlerin yoğunluğundan uzun zaman önce tedarik ettiğim ama bir türlü bitiremediğim sevgili Randy Pausch' un Son Konuşma isimli kitabı örneğin...Gerçekten Çocukluk Hayallerinizi Gerçekleştirmek ile ilgili olan bu kitabın büyük çoğunluğunu okumuştum ama kalanını tamamlamak için bu hava süper bir fırsat oluşturmuştu. Odanın camını iyice açıp içeriye dolan bembeyaz parlak ışık ve etraftaki derin sessizlik, kitap okumak için iyi bir neden sunuyordu. Henüz aranızda bu kitabı okumamış olanlar var ise mutlak suretle tedarik etmelerini şiddetle öneririm. Hayata bakış açınızın gerçekten değişeceğine emin olabilirsiniz. Kitabın kalan kısmınıda tamamladıktan sonra yüzümdeki mutluluğun bir kaç dakika gitmediğini çok iyi hatırlıyorum.

Peki ya sonra. Biraz bilgisayarın başına geçip internetten yararlanarak çevrede olan biteni takip etmek gerekebilir. Dünyadan ve yurdumuzdan ana haber başlıklarına bakılır. Özellikle ve ağırlıklı olarak haftanın ilk iş gününde hava durumunun nasıl olacağı öğrenilerek işe gitmek için uygun güzergah ve vasıtalar seçilir, yetkililerin uyarılarına kulak verilir. Sonrasında belki günün anlam ve önemine dair Windows 7 masa üstümüz için bir sürü duvar kağıdı resmine bakılabilir. Bu konuda sıkça kullandığım ve sayısız duvar kağıdını indirdiğim bir site var. Örneğin duruma uygun olarak snow, snowman veya tezat olarak sea, sundown gibi kelimeleri aratıp sonuçlarına bakabilirsiniz. Tamamen serbest üyelik gerektiren sitede, kullanmak istediğiniz resimler için lisans bilgileri de yer almakta ve çoğu serbest kullanıma açık. Tabi en iyi olanları için belirli bir ücret ödemeniz gerekiyor.

Şimdi de beki biraz müzik dinlenebilir. Ses kalitesi iyi olan MP3 çalarınız ile orjinal olarak satın aldığınız lisanslı Müzik CD' nizden MP3 çalarınıza aktardığınız bir albüm pekala dinlenebilir. Örneğin NedirTv?com adına hazırladığım pek çok görsel derste(Screencast) giriş kısmında çaldırdığım intro' ları içeren ve çok sevdiğim ünlü gitarist Joe Satriani' ninde dahil olduğu Chikenfoot grubunun ilk albümü. Zaten bir albümü baştan sona dinlemek size gün için gerekli enerjiyi verecektir diye düşünüyorum. Tabi müzik dinleme olayına bilgisayarınız başındayken de devam edebilirsiniz. Ancak müziği dinlerken bilgisayar ekranına değil, dışarıdaki karlı manzaraya bakmak ve derin düşüncelerde yol almak daha güzel olabilir.

Bilgisayar başındayken çok sık yaptığım ve hatta iş yerine geldiğimde her sabah düzenli olarak icre ettiğim işlerden biriside takip ettiğim internet günlüklerine(blog) eklenen yeni girdileri okumaktır. Burada yardımcı bir programdan faydalanabilirsiniz. Örneğin ben müzik eşliğinde FeedReader programını kullanarak feedreader.opml (19,77 kb) dosyasındaki OPML içeriğinde yer alan günlükleri takip ederim. Bu tip feed okuma programları sayesinde takip ettiğiniz pek çok alan ile ilişkili bilgilere gazete okuyormuşçasına ulaşabilirsiniz.

Önemli bir kaç günlük girdisi mutlaka dikkatinizi çekecektir. Eğer yetmiyorsa her zaman takip ettiğiniz bir yazılım kitabı baş ucunuzda olmalıdır. Böyle karlı bir hafta sonu için sizi çok fazla sıkmayacak, kalın olmayan, anlatımı düşünce yapınıza uygun bir kitabın tercih edilmesinde yarar vardır. Şahsen ben bu hafta sonu The Art of Unit Testing with Examples in .NET isimli kitabı tercih ettim. Bildiğiniz üzere test, yazılım geliştirme süreçlerinde çok önemli bir yere sahip. Geliştirdiğimiz ürünün 0 hata ile çıkmasından, geliştirme süreci için belirtilen sürelere uyulabilmesine kadar pek çok noktada test tekniklerinin uygulanışına rastlamaktayız. Hatta Test Driven Development isimli bir geliştirme süreci dahi bulunmakta. Ancak işin ana fikri her zaman için fonksiyonel birimlerin testiye başlanmasında yatıyor. Bu 320 sayfalık kitapta basit birim testi geliştirilmesinden, mock nesnelerin kullanımına kadar pek çok bilgi yer almakta. En azından bir bölümünü okuyup bilgisayar başında test etmekte yarar var.

E birazda sosyalleşmek lazım. Tabi interaktif dünyada yaşayan biz yazılımcılar için sosyalleşme genellikle Facebook veya Twitter gibi alanlar anlamına geliyor. Bu şekilde davranabileceğimiz gibi, bir kaç kat giyinip bize en yakın AVM sinemasına gidebilir ve Avatar' ı seyretmeye çalışabiliriz. Ama ben 70' lin ortasında doğmuş ve 80' li yıllarda çocukluğunu geçirmiş birisi olarak Avatar' a gidinceye kadar herkesin önce bir Star Wars serisini tamamlamasını öneririm. Bu nedenle dışarı çıkıp Avatar' a gitmektense, CNBC-E de bugün saat 22:00' da başlayacak olan efsaneye kadar StarWars sitesinde gezinmeyi ve bunu yaparken de Soundtrack albümünü dinlemeyi tercih ederim.

Belki de tamamen yanlış düşüncelere bürünmüş bir yol içerisindeyimdir. Belki de yavaş yavaş Dark Side' a geçiyorumdur. O yüzden tüm bunları bir kenara bırakıp kardan adam yapmayı da tercih edebileceğinizin farkındasınızdır. Belki yandaki resimdeki kadar etkili bir kardam adam olmayacaktır yaptığınız ama mahallenin küçükleri ile geçireceğiniz ve sizi çocukluğunuza götürecek o eşsiz dakikaların değeri de paha biçilemezdir.

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

WCF RIA Services - Authentication Domain Service - Attribute Bazlı Yetkilendirme [Beta 2]

Cuma, 22 Ocak 2010 09:45 by bsenyurt

Merhaba Arkadaşlar,

Bildiğ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.

Bu noktada iki önemli niteliğin olduğunu söyleyebiliriz. Bunlardan birisi Domain Service sınıfının Authentication işlemleri çerçevesinde değerlendirilmesi gerektiğini çalışma zamanına söyleyen RequiresAuthentication niteliğidir. Bu niteliği Domain Service tipine uygulamak yeterlidir. Diğer taraftan, Domain Service içerisinde tanımlanmış olan operasyonların hangi yetkiler altında çalıştırılabileceğini belirtmek için RequiresRole isimli nitelikten yararlanılmaktadır. RequiresRole niteliği string tipinde birden fazla parametre alabilmektedir. Bu parametre değerleri tahmin edileceği üzere rolleri ifade etmektedir. Bu teoriye göre bir Domain Service operasyonunun rol bazlı olaraktan yetki altında çalıştırılmasının sağlanması mümkündür.

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 Album 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.

Kişisel Not : Başlamadan önce Silverlight uygulamasında daha önceki yazılarda sıklıkla anlattığımız şekilde Form bazlı doğrulama(Form-Based Authentication) için gerekli ayarları yapmamız gerektiğini hatırlatalım. Özellikle role 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 buraks ve bill isimli kullanıcıları değerlendiriyor olacağız. Bunlardan birisi Employee rolünde iken diğeri Finance rolündedir. Amaçlanan sadece Finance rolündekilerin, albüm listesini çekebilmesini sağlamaktır. Tabiki Chinook isimli veritabanını Silverlight uygulamasında kullanabilmek için gerekli Domain Service(ChinookDomainService) öğ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 Wink

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.

Şekildende görüleceği üzere sunucu uygulama tarafında Chinook veritabanına ulaşılmasını ve üzerinde CRUD işlemleri yapılabilmesini sağlayan(ki bu örnekte sadece veri çekiyor olacağız) ChinookDomainService isimli Domain Service tipi bulunmaktadır. Bu tip arada Ado.Net Entity Framework modelini kullanmaktadır. Diğer taraftan doğrulama işlemleri için ASP.NET Membership alt yapısı kullanılmakta olup istemci tarafının bu hizmeti değerlendirebilmesi için birde Authentication Domain Service(ChinookDomainService) öğ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 ChinookDomainService sınıfını aşağıdaki şekilde oluşturduğumuzu göz önüne alalım.

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;
        }
    }
}

Dikkat edileceği üzere ChinookDomainService tipine RequiresAuthentication niteliği uygulanmıştır. Bununla birlikte sadece Finance rolündekilerin kullanımına sunulan GetAlbums isimli bir operasyon yer almaktadır. Yazımızın başında da belirttiğimiz üzere RequiresRole 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 MainPage.xaml içeriğini ve kod kısmını aşağıdaki gibi geliştirdiğimizi düşünelim.

MainPage.xaml;

<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>

MainPage.xaml.cs;

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;
        }
    }
}

Aslında istemci tarafında yapılan tek şey kullanıcının Login ve Logout 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 Domain Service sınıfı üzerinden ele alınmaktadır. Buna göre Login olan geçerli kullanıcının Finance rolünde olmaması halinde albüm bilgilerini getirememesi 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;

Hata mesajında yer alan Access Denied kelimeleri olayı tüm çıplaklığıyla özetlemektedir. Diğer yandan senaryomuza göre Finance rolünde yer alan bill isimli kullanıcı ile Login olunup albüm listesi çekilmek istendiğinde, bilgilerin başarılı bir şekilde DataGrid kontrolüne çekildiği gözlemlenir. Aynen aşağıdaki şekilde görüldüğü gibi.

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ı script error olarak tarayıcı uygulama üzerinden yakalanmaktadır. Ancak en basit haliyle attribute bazlı olaraktan yetkilendirme işlemi sunucu tarafında yer alan servisler kanalıyla gerçekleştirilebilmiştir. Bu noktada vurgulanması gereken durumlardan biriside kendi Authorization niteliklerimizi(Attribute) 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.

WCF RIA Services - Authentication Sample.rar (1,31 mb) [Dosya boyutunun küçük olması için ASPNETDB.mdf içeriği çıkartılmıştır]

WCF RIA Services - Authentication Domain Service - Profile ve Role

Perşembe, 21 Ocak 2010 01:10 by bsenyurt

Merhaba Arkadaşlar,

Yandaki resmin bir renk cümbüşü oluşturup sizlere çok güzel göründüğüne eminim. Hatta bu resmin biraz sonra anlatacağımız konu ile olan ilgisini merak ediyor olabilirsiniz. Ne yazıkki yok. Sadece renk cümbüşünün benide etkilediğini ve yazının hoş görünmesi için eklediğimi itiraf edebilirim. Gelelim asıl mevzumuza.

Bir önceki yazımızda Authentication Domain Service konusunu incelemeye başlamış ve RIA(Rich Internet Application) çeşitlerinden olan Silverlight uygulamalarında Form tabanlı doğrulamanın standart ASP.NET Membership kaynakları üzerinden nasıl sağlanabileceğini görmüştük. RIA uygulamaları ile ilişkili konulardan bir diğeride rol ve profil yönetimidir. WCF RIA Service' lerde kullanılan Authentication Domain Service' lerden yararlanarak Role ve Profile yönetimide yapılabilir. Çok doğal olarak WCF RIA Service' leri, Asp.Net mimarisinin rol ve profil alt yapısını kullanmaktadır.

Doğrulanan bir kullanıcının(Authenticated User), sistem içerisinde yapabileceklerini belirlerken rolüne bakılarak karar verilmesi tercih edilen yöntemlerdendir. Örneğin Administrator rolündeki bir kullanıcı ile Guest rolündeki bir kullanıcının sistem içerisinde yapılabilecekleri kuvvetle muhtemel farklıdır. Burada açık bir şekilde role göre yetkinin mertebesinin belirlendiğini düşünebiliriz. Diğer yandan sistem içerisinde yer alan tüm kullanıcılar için ortak tanımlanabilecek özellikler, çalışma zamanında farklı(bazende benzer, hatta aynı) değerler alarak, her kullanıcının sistem için bir profilinin oluşmasında kullanılabilirler. Çok doğal olarak bu profil özellikleri sistemden sisteme farklı şekillerde tanımlanabilir ve kullanılabilirler. Söz gelimi RIA uygulamasına dahil olan kullanıcıların ünvanları, doğum tarihleri, göz renkleri, cep telefonlarının gsm operatörleri, son giriş zamanları her kullanıcı için birer profil özelliği olarak değerlendirilebilir.

Bu yazımızda bir önceki örneğimizi devam ettirerek rol ve profil özelliklerinin nasıl değerlendirilebileceğini ele almaya çalışıyor olacağız. Temel amacımız rol ve profil bilgilerinin özellikle istemci tarafında nasıl kullanılabileceğini görmek olduğundan çok işe yarar bir örnek geliştirmeyeceğimizi şimdiden belirtmek isterim Wink Öncelikli olarak sunucu uygulama tarafında rol ve profil yönetimi için gerekli ayarları yapmamız gerekiyor. Bir önceki örneğimizde kullandığımız buraks ve bill isimli kullanıcıları sırasıyla Developer ve Administrator isimli rollere atadığımızı düşünerek devam edeceğiz. Hatta testlerimizi daha iyi yapabilmek adına bill isimli kullanıcının her iki rol atlında da bulunması sağladığımızı düşünelim. Bu şekilde istemci tarafına birden fazla rol bilgisinin nasıl aktarıldığını değerlendirme fırsatımız olacaktır. Bildiğiniz üzere rol atama işlemleri için ASP.NET Configuration aracını kullanabiliriz. Rol atama işlemlerinin ardından örneğimizde kullanacağımız bir kaç profil özelliğini tanımlayarak devam edebiliriz. Bu amaçla Web.config dosyamızın içeriğinde aşağıdaki değişiklikleri yaptığımızı düşünelim.

<configuration>
  <system.web>
    <httpModules>
      <add name="DomainServiceModule" type="System.Web.Ria.Services.DomainServiceHttpModule, System.Web.Ria, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
    </httpModules>
    <authentication mode="Forms" />
    <roleManager enabled="true" />
    <profile enabled="true">
      <properties>
        <add name="Title"/>
        <add name="LastAccessTime" type="System.DateTime"/>
      </properties>
    </profile>
    <compilation debug="true" targetFramework="4.0" />
  </system.web>
...

roleManager ve profile elementleri içerisinde enabled niteliklerine true değer atandığına dikkat edelim. Bu sayede RIA uygulamamızda role ve profile kullanımını etkinleştirmiş oluyoruz. profile elementi içerisinde yer alan properties alt elementi içerisinde ise Title ve LastAccessTime isimli özellik tanımlamalarının yapıldığını görmekteyiz. Özellikle LastAccessTime niteliği için DateTime bildiriminin de yapıldığına dikkat edelim. Nitekim profil özellikleri varsayılan olarak string tipindendir. Bu sebepten string harici tipleri bildirmemiz gerekmektedir.

Web.config dosyasında yapılan bu bildirimler ASP.NET tarafındaki role ve profile alt yapıları için gereklidir. Ne varki RIA uygulaması tarafında da ilgili profil özelliklerinin kullanımı için gerekli bildirimlerin yapılması gerekmektedir. Üstelik kullanıcının rol bilgilerinin istemci tarafında yer alan kod kısmında nasıl ele alınabileceği de şu anda soru işaretidir. Panik ve heyecan yapmadan sakin bir şekilde adım adım ilerleyelim. Öncelikli olarak OurAuthenticationService ismiyle oluşturduğumuz Authentication Domain Service dosyasını açalım ve User isimli tipin içeriğini aşağıdaki gibi düzenleyelim.

using System;
using System.Web.Ria;
using System.Web.Ria.ApplicationServices;

namespace SilverlightApplication7.Web
{
    [EnableClientAccess]
    public class OurAuthenticationService
        : AuthenticationBase<User>
    {
    }

    public class User
        : UserBase
    {
        public string Title { get; set; }
        public DateTime LastAccessTime{ get; set; }
    }
}

Dikkat edileceği üzer profile elementi altında tanımlanan özellikler, User isimli tip içerisinde de property olarak bildirilmiştir. Buraya kadar yaptığımız işlemlerin ardından uygulamayı build ettiğimizde, istemci tarafında otomatik olarak üretilen sınıf içerisinde yer alan User tipininde aşağıdaki şekilde görüldüğü gibi oluşturulduğunu fark edebiliriz.

Dikkat edileceği üzere sunucu tarafında tanımladığımız Title, LastAccessTime isimli özellikler için istemci tarafındaki User sınıfı içerisinde de gerekli bildirimler yapılmıştır. Üstelik rol işlemleri içinde IEnumerable<string> tipinden bir özellik(Roles) olduğu görülmektedir. Buna göre birden fazla rolün kod tarafında değerlendirilmesi mümkündür. Hatta LINQ ifadeleri ile Roles özelliği üzerinden sorgular atılabilir. Artık istemci tarafında rol ve profil işlemlerini değerlendirebilecek alt yapı hazırlıklarını tamamlamış bulunuyoruz. Şimdi MainPage.xaml içeriğini aşağıdaki gibi güncelleyerek yolumuza devam edebiliriz.

MainPage.xaml içeriği;

<UserControl x:Class="SilverlightApplication7.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="325" d:DesignWidth="420" xmlns:dataInput="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data.Input" xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation">
    <Grid x:Name="LayoutRoot" Background="White" Height="325">
        <dataInput:Label Height="28" HorizontalAlignment="Left" Margin="8,14,0,0" Name="label1" VerticalAlignment="Top" Width="120" Content="Username" />
        <dataInput:Label Height="28" HorizontalAlignment="Left" Margin="10,48,0,0" Name="label2" VerticalAlignment="Top" Width="120" Content="Password" />
        <TextBox Height="23" HorizontalAlignment="Left" Margin="134,14,0,0" Name="txtUsername" VerticalAlignment="Top" Width="197" />
        <PasswordBox Height="23" HorizontalAlignment="Left" Margin="134,50,0,0" Name="txtPassword" VerticalAlignment="Top" Width="197"/>           
        <Button Content="Login" Height="23" HorizontalAlignment="Left" Margin="337,14,0,0" Name="btnLogin" VerticalAlignment="Top" Width="75" Click="btnLogin_Click" />
        <dataInput:Label Height="28" HorizontalAlignment="Left" Margin="12,82,0,0" Name="lblLoginStatus" VerticalAlignment="Top" Width="196" />
        <Button Content="Logout" Height="23" HorizontalAlignment="Left" Margin="337,53,0,0" Name="btnLogout" VerticalAlignment="Top" Width="75" Click="btnLogout_Click" />
        <dataInput:Label Height="28" HorizontalAlignment="Left" Margin="214,82,0,0" Name="lblProcess" VerticalAlignment="Top" Width="198" />
        <Border BorderBrush="#FFFF3B00" BorderThickness="3" Height="185" HorizontalAlignment="Left" Margin="10,124,0,0" Name="brdProfile" VerticalAlignment="Top" Width="395" CornerRadius="10" Background="{x:Null}">
            <Canvas Height="170" Name="canvas1" Width="380">
                <dataInput:Label Canvas.Left="6" Canvas.Top="6" Height="13" Name="label3" Width="43" Content="Title" />
                <dataInput:Label Canvas.Left="8" Canvas.Top="40" Height="15" Name="label4" Width="101" Content="Last Access Time" />
                <TextBox Canvas.Left="56" Canvas.Top="7" Height="23" Name="txtUserTitle" Width="309" />
                <dataInput:Label Canvas.Left="116" Canvas.Top="42" Height="28" Name="lblUserLastAccessTime" Width="249" />
                <dataInput:Label Canvas.Left="10" Canvas.Top="83" Height="15" Name="label5" Width="39" Content="Roles" />
                <dataInput:Label Canvas.Left="60" Canvas.Top="83" Height="28" Name="lblRoles" Width="305" />
                <Button Canvas.Left="290" Canvas.Top="130" Content="Save Profile" Height="23" Name="btnSaveProfile" Width="75" Click="btnSaveProfile_Click" />
            </Canvas>
        </Border>
    </Grid>
</UserControl>

Tasarımın biraz fakir olmasına aldırmadan ilerlemeye devam edelim. Undecided Senaryomuzu şu şekilde işletiyor olacağız; Kullanıcı Login işlemini başarılı bir şekilde gerçekleştirdiyse eğer, WebContext.Current.User özelliğinden elde edeceğimiz kullanıcı bilgilerini Border alanı içerisindeki kontrollerde göstereceğiz. Sembolik olarak Title ve LastAccessTime değerlerini ve dahil olduğu rollerin adlarını değerlendireceğiz. Sonrasında kullanıcı isterse Save Profile başlıklı düğmeye basaraktan yeni Title ve LastAccessTime bilgilerini kaydedebilecek. Bu basit senaryo için bir önceki yazımızda geliştirdiğimiz MainPage.xaml.cs içeriğini aşağıdaki gibi güncellememiz yeterli olacaktır.

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Ria.ApplicationServices;

namespace SilverlightApplication7
{
    public partial class MainPage : UserControl
    {
        // Kullanıcının doğrulanması, profil özelliklerinin yüklenmesi ve kaydedilmesi için gerekli fonksiyonellikleri sunan tiptir.
        AuthenticationService authSrv = WebContext.Current.Authentication;
      
        public MainPage()
        {
            InitializeComponent();

            btnLogout.IsEnabled = false;
            brdProfile.Visibility = Visibility.Collapsed;

            authSrv.LoggedIn += new System.EventHandler<AuthenticationEventArgs>(authSrv_LoggedIn);
            authSrv.LoggedOut += new System.EventHandler<AuthenticationEventArgs>(authSrv_LoggedOut);
        }

        private void btnLogin_Click(object sender, RoutedEventArgs e)
        {
            lblLoginStatus.Content = String.Empty;
            lblProcess.Content = String.Empty;

            if (!authSrv.IsLoggingIn) // Eğer asenkron olarak devam eden bir Login operasyonu yoksa
            {
                // Login işlemi için AuthenticationService tipinin Login metodu çağırılır.
                // ilk parametre ile kullanıcı adı ve şifre bilgisi gönderilir. Bu metodun aşırı yüklenmiş versiyonları mevcuttur.
                // ikinci parametrede Login metodunun işleyişini tamamlaması sonrası devreye giren metodun işaret edilmesi sağlanmaktadır(Action<LoginOperation> temsilci tipi ile işaretleme yapılır). Bu metod içerisinde işlemin iptali, exception üretmesi gibi durumlarda ele alınmaktadır.
                authSrv.Login(
                    new LoginParameters(txtUsername.Text, txtPassword.Password),
                    opt =>
                    {                       
                        if (opt.IsCanceled)
                            lblProcess.Content = "Login işleminde iptal";
                        else if (opt.Error != null)
                            lblProcess.Content = opt.Error.Message;
                        else if (opt.IsComplete)
                            lblProcess.Content = "Login işlemi tamamlandı";
                    }
                    , null
                    );
            }
        }

        private void btnLogout_Click(object sender, RoutedEventArgs e)
        {
            lblLoginStatus.Content = String.Empty;
            lblProcess.Content = String.Empty;

            if (!authSrv.IsLoggingOut) // Eğer asenkron olarak devam eden bir Logout operasyonu yoksa
            {
                // Logout işlemi kullanılan metod çağrısı
                // işlem tamamlandığında devreye girecek olan metod Action<LogoutOperation> temsilcisi ile işaret edilir.
                authSrv.Logout(
                   opt =>
                   {
                       if (opt.IsCanceled)
                           lblProcess.Content = "Logout işleminde iptal";
                       else if (opt.Error != null)
                           lblProcess.Content = opt.Error.Message;
                       else if (opt.IsComplete)
                           lblProcess.Content = "Logout işlemi tamamlandı";
                   }
                , null);
            }
        }

        // Kullanıcı başarılı bir şekilde Logout olduğunda tetiklenir
        void authSrv_LoggedOut(object sender, AuthenticationEventArgs e)
        {
            lblLoginStatus.Content = String.Format("{0} {1} zamanında çıkış yaptı", e.User.Identity.Name, DateTime.Now.ToLongTimeString());

            btnLogin.IsEnabled = true;
            btnLogout.IsEnabled = false;
            brdProfile.Visibility = Visibility.Collapsed;
        }

        //AuthenticationService nesnesine ait Login metodunun çalıştırılması sonrasında kullanıcı başarılı bir şekilde doğrulandıysa çalışır
        void authSrv_LoggedIn(object sender, AuthenticationEventArgs e)
        {
            lblLoginStatus.Content = String.Format("{0} {1} zamanında giriş yaptı", e.User.Identity.Name, DateTime.Now.ToLongTimeString());

            btnLogin.IsEnabled = false;
            btnLogout.IsEnabled = true;
            brdProfile.Visibility = Visibility.Visible;
            lblRoles.Content = String.Empty;
            txtUserTitle.Text = String.Empty;

            // Login işlemi tamamlandıktan sonra WebContext.Current üzerinden giriş yapan User bilgileri alınır
            Web.User currentUser = WebContext.Current.User;
            // Profil özelliklerinin değerleri ilgili kontrol özelliklerine atanır
            lblUserLastAccessTime.Content = currentUser.LastAccessTime.ToLongTimeString();
            txtUserTitle.Text = currentUser.Title;

            // Kullanıcının dahil olduğu tüm roller Label kontrolü içerisinde ardışıl olarak yazdırılır
            foreach (var role in currentUser.Roles)
            {
                lblRoles.Content += role + "|";
            }
        }

        private void btnSaveProfile_Click(object sender, RoutedEventArgs e)
        {
            // Save işleminde yine WebContext.Current üzerinden elde edilen User tipinin özelliklerinden yararlanılır
            Web.User currentUser = WebContext.Current.User;
           
            // Bu kez profile özelliklerine, kontroller üzerindeki değerler atanır
            currentUser.Title = txtUserTitle.Text;
            currentUser.LastAccessTime = DateTime.Now;

            // Kaydetme operasyonu için AuthenticationService nesne örneğinin SaveUser metodu çağırılır. Bu metodun çalıştırılması sırasında bir istisna olduğunda bunun ortama fırlatılması için parametre olarak true değeri verilmiştir.
            SaveUserOperation operation=authSrv.SaveUser(true);
            // Kaydetme operasyonu tamamlandığında SaveUserOperation tipinin Completed olay metodu devreye girer.
            operation.Completed +=
                (snd, arg) =>
                {
                    lblProcess.Content = "Profil Save is OK";
                };
        }
    }
}

(Eklediğimiz önemli kısımlar bold olarak işaretlenmiştir)

Hemen şu noktayıda vurgulayalım. İstenirse profil özelliklerinin kontrollere bağlanması sırasında Binding imkanlarından da yararlanılabilir. Bu durumda Login işlemini takiben otomatik olarak User bilgilerinin ilgili kontrollere bağlanması söz konusudur. Biz örneğimizde profil bilgilerinin gösterilmesi işlemlerini kod tarafında değerlendirmeye çalıştık. Ancak Binding işlemi ile aynı fonksiyonelliklerin uygulamaya nasıl kazandırılabileceğini incelemenizi öneririm. Dilerseniz uygulamamızın çalışma zamanını test ederek ilerleyelim. Ben bill isimli kullanıcı için daha önceden bir profil bilgisini test amacıyla kaydetmiştim. Bu durumda login işleminden sonra aşağıdaki görüntüye benzer sonuçlarla karşılaştım.

Şimdi profil bilgilerini değiştirip kaydettiğimizi düşünelim. Bu durumda Save Profile işleminin başarılı bir şekilde gerçekleştirildiğini görebiliriz.

Bu işlemin ardından tekrar Logout olup yeniden Login işlemini gerçekleştirirsek(ki uygulamayı kapatıp yeniden başlatmakta söz konusu olabilir) bill isimli kullanıcı için az önce kaydedilen profil bilgilerinin getirildiğini görebiliriz. Buda çalışmanın başarılı olduğunu bir ispatı olarak düşünülebilir.

Böylece geldik bir yazımızın daha sonuna. Bu yazımızda WCF RIA Service - Authentication Domain Service hizmetini kullanaraktan Silverlight uygulamalarında Role ve Profile alt yapılarının nasıl değerlendirilebileceğini incelemeye çalıştık. Tekrardan görüşünceye dek hepinize mutlu günler dilerim.

SilverlightApplication7RoleAndProfile.rar (780,46 kb) [Dosya boyutunun küçük olması için ASPNETDB.mdf içeriği çıkartılmıştır]