İlkay İlknur

just a developer...

C# - Interceptors ve Castle Windsor İle Gerçekleştirimi

Merhaba Arkadaşlar,

Bu yazımızda bizim de kurumsal projelerde uzun süredir kullandığımız ayrıca diğer projelerde de sıklıkla kullanıldığını gözlemlediğim Interceptor konusunu inceliyor olacağız.

Yazımızın başında öncelikle interceptor konusuna giriş yapmadan önce sanırım Aspect Oriented Programming konusuna kısaca değinmemizde fayda bulunmakta. Bu şekilde Interceptor’ların tam olarak ne işe yaradığını, hangi konularda bizlere yardımcı olacağını daha iyi anlayabiliriz.

Aspect Oriented Programming

Aspect Oriented yaklaşımını incelediğimizde aslında temel olarak birbiri ile çakışan ilgilerin(cross-cutting) ayrılmasını hedeflediğini görmekteyiz. Peki bu ilgiler neler olabilir ?

Örneğin, geliştirdiğimiz yazılımlara baktığımızda aslında gerçekleştirdiğimiz business işlemleri sırasında bir takım işlemleri sürekli olarak gerçekleştirdiğimizi görmekteyiz. Hep beraber itiraf etmemiz gerekirse gerçekleştirmiş olduğumuz business metotlarının hemen hemen aşağıdaki gibi akışları bulunmakta. :)

static void OylesineBirBusinessMetot()
{
 try
{
 MetodaGelenIlkIstegiLogla();
 IstegiYapanKullanicininYetkisiVarmi();
 //Business İslemlerini Gerçeklestir
IslemSonucunuLogla();
 }
 catch (Exception ex)
 {
 HatayiLogla(ex);
 }
}

Yukarıda görmüş olduğumuz akışlar ve bu akışlara ek olarak metotlar içerisinde yapabileceğimiz bir takım diğer ortak işlemler aşağıdakiler olabilmekte.

  • Loglama İşlemleri
  • Validasyon İşlemleri
  • Authentication İşlemleri
  • ExceptionHandling İşlemleri
  • Caching İşlemleri

Yukarıdaki işlemlere baktığımızda aslında business katmanında yaptığımız işlemlerin büyük bir kısmını oluşturduğunu görmekteyiz. Hatta bu işlemleri çıkarttığımızda business metotlarımızın oldukça kısaldığını ve yalınlaştığını görebiliriz.

İşte aspect oriented programming yaklaşımı da yukarıda gördüğümüz gibi birbirlerinden farklı olan ancak çoğu kez kesişen ilgilerin birbirlerinden ayrılmasını amaçlamakta. Böylece geliştirilen yazılımların bakımlarının kolaylaştırılması, ölçeklendirilebilmesi amaçlanmakta.

Peki yukarıda gördüğümüz birbirleri ile çakışan ilgileri birbirlerinden nasıl ayıracağız ? İşte bu noktada karşımıza interceptor yapıları çıkmakta. Interceptor’lar belirli noktalarda metot çağrımları sırasında araya girerek bizlerin çakışan ilgilerimizi işletmemizi ve yönetmemizi sağlamakta. Böylece metotların çalışmasından önce veya sonra bir takım işlemleri gerçekleştirebilmekteyiz. Şimdi gelin artık kod tarafına,  yani işin eğlenceli kısmına geçiş yapalım. :)

Bu arada Aspect Oriented Programming konusu yukarıda okuduğumuz gibi tek başlıkta geçilebilecek bir konu değil. Pek çok farklı noktaları bulunmakta. Bu nedenle Aspect Oriented Programming konusu ile ilgili daha fazla bilgi edinmek isterseniz Arda Çetinkaya’nın bu konuda yazmış olduğu makeleleleri okuyabilirsiniz.

Interceptors

Şimdi sıra geldi yukarıda bahsetmiş olduğumuz metotların çalışması sırasında araya  girecek olan interceptor yapısının nasıl geliştirileceğine. Aslında bu şekilde bir yapı geliştirmek için .Net Framework içerisinde bir takım tipler bulunmakta. Ancak baktığımızda hızlı bir şekilde geliştirme yapmak bu tipler ile oldukça güç ve acı çektirici :) Bunun yerine gerek Microsoft Enterprise Library içerisinde bulunan Unity bloğu olsun gerekse Castle Windsor gibi kütüphaneler olsun bizlere Interceptor yapısını sağlamak için bir takım kütüphaneler sunmaktalar. Biz bu yazımızda öncelikle daha önceki IoC Containers yazımızda olduğu gibi Castle Windsor kütüphanesi üzerinden ilerleyeceğiz.

Yazımız boyunca ilerleyeceğimiz örnek ise yine önceki IoC yazılarımızda kullandığımız yapı üzerinden olacak. Bu nedenle öncelikle buradaki adresten yaptığımız örneğin son halini indirebilirsiniz. Uygulamayı indirdiğinizde aşağıdaki gibi bir yapı ile karşılaşıyor olacaksınız.

Önceki yazılarımızda gerçekleştirdiğimiz uygulamayı tekrardan kısaca açıklamamız gerekirse, uygulamamız içerisinde bulunan Processor tipi içerisinde business işlemlerinin yönetimi yapılmakta. Ayrıca IoC kullanarak gerçekleştirdiğimiz bağımlılıkların konfigüre edilmesi özelliği sayesinde Processor metodu IoC Container tarafından sunulan tipleri kullanarak ilgili çağrımları gerçekleştirmekte. (IoC ile ilgili detaylı bilgi için buradaki yazımı okuyabilirsiniz.)

Şimdi gelin mevcut uygulamamıza Interceptor yapısı ekleyelim :)

Interceptor Gerçekleştirimi

Castle Windsor kullanarak Interceptor geliştirmek için bilmemiz gereken bir nokta bulunmakta. Bu nokta da Interceptor metodunu içerisinde bulunduran tipin IInterceptor interface’ini implemente etmesi gerektiği. Şimdi mevcut uygulamamız içerisinde Interceptor isimli bir sınıf yaratalım ve IInterceptor interface’ini implemente ettirelim.

public class Interceptor : IInterceptor     
{     #region IInterceptor Members         
 public void Intercept(IInvocation invocation)
 {
 throw new NotImplementedException();
 }     #endregion
}

Evet aslında gördüğünüz gibi herşey oldukça basit. İşte size Interceptor metodumuz. :) Burada artık istediğimiz işlemleri gerçekleştirebiliriz. IInterceptor interface’i içerisinden gelen Intercept metodu gördüğünüz gibi IInvocation tipinden bir parametre almakta. Bu parametre aslında esas olarak çağrılan metot ile ilgili tüm bilgileri içerisinde barındırmakta. Bu nedenle metot çağrımından önce yazacağımız interceptor devreye gireceğinden dolayı çağrılan metodu çalıştırmak yada çalıştırmamak bizim elimizde. Bunun için de eğer istek yapılan metodu çağırmak istersek IInvocation tipi içerisinde bulunan Proceed metodunu çağırmamız gerekmekte.

Şimdi isterseniz interceptor metodunu biraz daha anlamlı hale getirelim.

public void Intercept(IInvocation invocation)
{
 try
{
 //Metoda gelen ilk isteği logluyoruz.
Console.WriteLine("{0} isimli metoda istek geldi}", invocation.Method.Name);

 //İsteği yapan kullanıcının işlemi yapma yetkisi var mı yok mu 
 //kontrolünü burada yapabiliriz.

//Eğer yetkisi varsa metodu çalıştır.
invocation.Proceed();

 // Yoksa Exception fırlatılabilir.

Console.WriteLine("{0} isimli metodun çalışması sona erdi.}", 
 invocation.Method.Name);             
 }
 catch (Exception ex)
 {
 Console.WriteLine("Hata oluştu {0}",ex.ToString());
 }
}

Şu anda biraz daha anlamlı bir Interceptor metodunu gerçekleştirmiş durumdayız. Şimdi sıra geldi yazdığımız interceptor metodunu Castle Windsor tarafına bildirmeye. IoC Container kısmından da hatırlayacağımız gibi Castle Windsor konfigürasyon için hem fluent bir arayüz hem de config dosyası desteği sunmakta. Biz ilk olarak fluent arayüz implementasyonunu gerçekleştireceğiz.

Mevcut uygulamamıza baktığımızda IoCUtil sınıfı içerisinde zaten fluent olarak IoC tanımlamalarının yapıldığını görmekteyiz. Bizim ek olarak yapmamız gereken interceptor tanımlamamızı yapmak. Bunun içinde yapmamız gereken ilk şey Interceptor’ı container içerisine register etmek ve yaşam süresini belirlemek. Bu şekilde Interceptor sınıfının kullanımını belirleyeceğiz. Daha sonrasında ise tiplerimiz üzerinde araya girecek olan Interceptor’ı yine container üzerinden bildireceğiz.

private static IWindsorContainer BootstrapContainer() {    return new WindsorContainer().Register(        
 Component.For<Interceptor>().LifeStyle.Transient,  Component.For<IMessageSender>().ImplementedBy<MailSender>()
 .Interceptors<Interceptor>(),
 Component.For<ILogger>().ImplementedBy<DBLogger>()
 .Interceptors<Interceptor>()   
 ); }

Yukarıdaki bildirimde ilk olarak Interceptor tipimizin yaşam süresi ile ilgili bir bildirim yaptık. Burada yaptığımız Transient bildirimi ile bu tipin her resolve sırasında tekrardan yaratılacağını bildirdik. Daha sonra ise IoC tanımlamalarımız sırasında bu tipler üzerinden yapacağımız çağrılarda geliştirmiş olduğumuz Interceptor tipini kullanacağımızı bildirmiş olduk.

Şimdi ise uygulamamızı çalıştıralım ve Interceptor’ın çalışmasını gözlemleyelim.

Gördüğümüz gibi Interceptor devreye girdi ve gerekli çıktıları ekrana yazdı. Şimdi de config dosyası tarafından tanımlamalara bakalım.

Config Dosyası Kullanılarak Interceptor Register Etme

Config dosyasından yapacağımız register işleminde de aslında fluent olarak izlediğimiz yaklaşımla aynı olacak. Öncelikle Interceptor tipimizi register ettikten sonra register ettiğimiz tipi interceptor olarak ilgili IoC tanımlamalarımıza eklememiz gerekmekte. Bu işlemleri gerçekleştiren XML konfigürasyonu aşağıdaki gibi olacak.
<castle>     
<components>       
<component id="logger" service="Windsor.Project.Interfaces.ILogger, Windsor.Project"      
type="Windsor.Project.Loggers.DBLogger, Windsor.Project">         
<interceptors>           
<interceptor>${interceptor}</interceptor>         
</interceptors>       
</component>       
<component id="repository" service="Windsor.Project.Interfaces.IMessageSender, Windsor.Project"      
type="Windsor.Project.MessageSenders.SMSSender, Windsor.Project">         
<interceptors>           
<interceptor>${interceptor}</interceptor>         
</interceptors>       
</component>       
<component id="interceptor" type="Windsor.Project.Interceptor, Windsor.Project
lifestyle="transient">       
</component>     
</components>   
</castle>

Bitirirken

Evet arkadaşlar bu yazımızda da yine uygulamalarımızı geliştirme sırasında kullanmış olduğumuz interceptor yapısını incelemeye çalıştık. Interceptorlar bir çok noktada bizlerin yazmış olduğu kodları oldukça kısaltmakta ve kodu daha temiz hale getirmekte. Bunun yanında yukarıda bahsetmiş olduğum bir takım işlemleri interceptor tarafına taşıyabileceğiniz gibi ayrıca bizim son uygulamamızda kulllandığımız üzere dinamik olarak farklı assemblyler üzerinden reflection kullanarak özelleşmiş metot çağrımları gibi senaryoları  da gerçekleştirebilirsiniz. Aslında burada önemli olan bu gibi imkanları gerektiği durumlarda nokta vuruşu yaparak kullanmak. ;)

Yazımızda yaptığımız örneği aşağıdan indirebilirsiniz.

Bir sonraki yazımızda görüşmek üzere,

Hoşçakalın

Windsor.Project



Yorum Gönder


Yorumlar

  • profile

    Tolgahan BAHADIR

    • 28
    • 4
    • 2016

    Eline Sağlık hocam.Source gayet açıklayıcı ,sade ve başarılı olmuş.

  • profile

    İlkay İlknur

    • 26
    • 1
    • 2014

    WCF Integration Facility adında bir projeleri vardı sanırım. http://docs.castleproject.org/Default.aspx?Page=WCF-Integration-Facility&NS=Windsor&AspxAutoDetectCookieSupport=1 buna bir bak istersen. Eski şirketlerimden birinde kullanımıştık WCF içerisinde.

  • profile

    Tolga

    • 23
    • 1
    • 2014

    Yazınız şu an da şirkette üzerinde çalıştığım projede tam olarak yapmaya çalıştığım şeyi anlatıyor, teşekkürler. Ancak bir farkla ben bu sistemi WCF web service metodlarımla entegre etmek istiyorum. Mümkün müdür?

  • profile

    İzzet

    • 8
    • 3
    • 2013

    Faydalı ve güzel bir yazı olmuş. Devamını bekliyoruz. tşk