Pazartesi, Mart 23, 2009

Sınıflandırma Probleminin Yapısı

Desen tanıma (DT) ölçümler ile sınıflandırma "tanıma" veya "açıklama" ile alakalı bir bilim dalıdır. Genel olarak üç temel yaklaşım vardır. İstatistiksel (veya karar teorisi) yaklaşımı. Yapısal yaklaşım. Kapalı kutu veya sinir ağı yaklaşımıdır.


Özet Gösterim



 

 

DT bilgi eşleşmesi veya bilgi etiketleme işleminin karakterize edilmesidir. Bizim görevimiz sınıf elemanları uzayı C ile desen uzayı P arasında bir eşleşme yapmaktır. Bu eşleşme her bir sınıfın olası Gί ilişkisi yolu ile tamamlanmaktadır. Her bir ί inci sınıfı gösteren w ί sınıfı desen uzayında p ί ile gösterilmekte olan "desenler" alt kümesini üretmektedir. Bunu birlikte (benzer) nitelikleri paylaşan farklı sınıflardan desenler p ί alt uzayların üst üste binmesine izin vermektedir. Bir başka ilişki M ise P uzayının alt uzaylarında ki desenlerden m ί ile temsil edilen sunumlar/desen ölçümleri/niteliklere bir eşleşmedir. Şekil 1 ile temsil edilen ve yukarıda anlatılan kavramı kullanarak birçok DT problemini karakterize etmek kolaylaşır.

Her bir
ί için m ί ölçümü verilmektedir. G ί ve M tersine eşleşmeyi yapacak bir metot tanımlamak istemekteyiz. Pratikte bu metot basit bir fonksiyon değildir. Hatta nadiren 1:1 ve ters çevrilebilirdir. Örneğim Şekil 1 göstermektedir ki benzer ölçümler/sunumlar alta yatan farklı sınıflar ile uyuşan farklı p ί sonuçları üretmektedir. Bu önerme potansiyel bir belirsizliktir. Şekil 1 ile alakalı bir diğer önemli kavram M eşleşmesidir. M eşleşmesi seçtiğimiz ölçüm sistemini yansıtmaktadır. DT sisteminde ölçüm sistemi tasarımı daha sonradan ortaya çıkacak olan güzel veya daha az yeterli niteliklerin/özelliklerin sezilmesi için önemlidir.

Tipik Desen Tanıma Sistemi Yapısı

Tipik desen tanıma sistemi yapısı Şekil 2 de gösterilmiştir. Bu sistem sadece bir sensor (örnek olarak resim sensoru, kamera veya veri toplama yazılımı) bir nitelik çıkarım mekanizması / algoritması ve (seçilen yaklaşıma göre) bir sınıflandırma / açıklama algoritmasından oluşmaktadır.


 

ALINTIDIR: Robert Schalkoff Patterns, Recognition Statistical, Structural and Nueral Approaches / 1992 (ISBN 0-471-52974-5)

Salı, Şubat 17, 2009

Sakarya Üniversitesi Semineri

Uzun zamandır ortalarda görünemiyorum. İş/master projeler derken kendimizi unutturduk. Geçen hafta Sakarya Üniversitesinde Dependency Injection üzerine bir seminer gerçekleştirdik. Seminerde ki sumun burada( ve aşağıda) örnek projeler burada ve burada.



Seminerden Görüntüler











Organizatör ve katılımcılara teşekkür ederim...

Perşembe, Temmuz 31, 2008

Parayı Nereye Yatırmalı?

Parayı Nereye Yatırmalı?


Ülkemizde yatırımcılar için bir çok teşfik yasaları mevcuttur. Peki bir yatırımcı parasını yazılım sektörüne yatırmaya karar verdikten sonra kendisine hangi piyasayı hedef seçmelidir? Cevabı büyük oyuncuların gelecek planlarından bulmaya çalışalım.Yazılım sektörünün dev firmalarının Microsoft ve Google gibi firmaların yakın gelecekteki popüler yazılım alanlarını keşfetmeye çalışalım.



İlk olarak sektörün en büyük firması Microsoft’un yakın gelecek planlarını inceleyelim ve yatırımcı için pozisyon arayalım. Henüz .net framework içine girmemiş çok önemli iki proje var. Paralel programing framework (paralel fx)[1] çoklu işlemci desteğini getiren yeni bir uygulama yapısıdır. Paralel programlama esnekliği yazılım sektöründe yatırım yapmak isteyen yatırımcı için kısa vadede bir kazanç kapısıdır. Sunucu servis yazılımları bu projenin tamamlanması ile yeniden yazılma ihtiyacı duyacaktır.


Microsoft robotic sdk [2] ile machine learing temelli uygulamaların önünü açmaktadır. Durup düşünelim .net framework içine alınan robotic sdk ile günlük hayatımızda ne değişir?


Piyasadaki yazılımcıların ve şirketlerin robotik alanında tecrübesiz olduğunu göz önünde tutarsak. Önce aptal hariçi cihazlar (dummy embended machine) artacaktır. Bu cihazlar takip cihazları, kahve makinaları, beyaz eşyalar gibi kendi başına çalışan tek bir işlemi yerine getiren cihazlardır. Aptal cihazların piyasa değeri olan uygulamalara dönüşmesi için zeki sunucu uygulamaları ile desteklenmeleri şarttır. Aptal cihazları desteklemek için zeki sunucu servisleri ve uygulama yazılımları kullanılacaktır.


Microsoft teknolojileri kullanacak şirket yatırımcıları için orta vadede en uygun yatırım sektörü aptal cihazlar ile sahadan veri toplayan otomasyonlar olacaktır. Bu otomasyon zinciri cihaz ithalatı, cihaz üretimi, cihaz programlaması, sunucu servisi, son kullanıcı uygulaması gibi adımlardan oluşmaktadır. Yatırımcı bu adımların herhangi birinde posizyon alabilir. Gelecekte kendi web sitesini bile wireles ortamda yayınlayabilen kendi başına karar alan ve bir çok işi yapabilen cihazlardan/robotlardan söz edilecektir. Fakat şirketler için en büyük satın alma kriteri ihtiyac olduğunu göz önüne alırsak bu tip zeki cihazların bireysel pazarlarda daha şanslı olduğunu söyleyebiliriz. Tabiki de aptal cihazlardan zeki cihazlara geçişte önceden piyasada yer almış olan şirketler daha şanslı olacaktır.



[1] http://msdn.microsoft.com/en-us/concurrency/default.aspx
[2] http://msdn.microsoft.com/en-us/robotics/default.aspx

Yazılım Vadisi

Yeni bir oluşum: Yazılım Vadisi

Ülkemizde ne yazık ki Bilgisayar Mühendisleri odası bulunmamaktadır. Bu sebebten yazılım sektörüne ait bir ulusal strateji geliştirilmesi çok zor olmaktadır. Yazılım sektörünün insan güçü ve kapastesi ülkemizde oldukca yüksek olmasına rahmen uluslar arası alanda ses getiren veya çok büyük paralar kazanan uygulamalarımız yok denecek kadar azdır. Bilgisayar Mühendisliği alanında ki yanlış bakış acıları ve sektörün lisanı olmadan çalışan insanlar tarafından istila edilmesinin sonucu olarak her yeni teknoloji ile tekrar pazarını büyüten global yazılım pastasında ülkemiz pay alamamaktadır.

Yazılım Vadisi ülkemizde ki tüm yazılım firmalarının ortak bir platformdan seslerini duyurma imkanı verme gayreti ile oluşturulmuş bir girişimdir. Bende bu girişimi desteklemekteyim.
www.yazilim-vadisi.org

www.yazilim-vadisi.com

Cuma, Haziran 20, 2008

Unity WCF

Smart client software factory – web client software factory gibi bir çok popüler uygulama mimarisinin kendi object builder nesnesi vardır. Fakat wcf için böyle bir uygulama mimarisi bulunmamaktadır. Buda bir eskikliktir.


Kodlarhttp://www.codeplex.com/wcfunity



Bu projede WCF için unity container nesnesinin nasıl kullanılacağını gösteriyorum. Unity ile policy injection application bloğu bereber kullandım.Böylelikle WCF servisleri için hem dependency injection hemde policy injection yapabilme imkanı doğdu. Tip tanımlarının ayrı config dosyalarında olduğunu varsayarak hareket ettim. Böylelikle hem config dosyasındakarmaşadan kaçındım hemde projeyi uygulama bağımsız olarak kullanma imkanı oluştu.



WCF için unity containerın kullanabilecek en iyi yer WCF servisin life time kontrolünü sağlayan IInstanseProvider arayüzüdür. Öncelikle dependency injection sağlayacak olan yeni bir IınstanceProvider yazdım.


   20 public class DependencyInjectionInstanceProvider : IInstanceProvider {


   47 /// <summary>

   48 /// init dependency container

   49 /// </summary>

   50 /// <param name="dependencyFile">Dependency config file</param>

   51 /// <param name="containerName">Dependency conainer name</param>

   52 private void InitContainer(string dependencyFile, string containerName) {

   53     string currentContainerKey = GetDependencyKey(dependencyFile, containerName);

   54     if (!containers.ContainsKey(currentContainerKey)) {

   55         lock (syncRoot) {

   56             if (!containers.ContainsKey(currentContainerKey)) {

   57                 containers[currentContainerKey] = currentContainer = new UnityContainer();

   58 

   59                 ExeConfigurationFileMap map = new ExeConfigurationFileMap();

   60                 map.ExeConfigFilename = dependencyFile;

   61                 System.Configuration.Configuration config

   62                   = ConfigurationManager.OpenMappedExeConfiguration(map, ConfigurationUserLevel.None);

   63                 UnityConfigurationSection section

   64                   = (UnityConfigurationSection)config.GetSection("unity");

   65 

   66                 section.Containers[containerName].Configure(currentContainer);

   67 

   68                 currentContainer.AddNewExtension<PolicyInjectionExtension>();

   69             }

   70         }

   71     } else {

   72         currentContainer = containers[currentContainerKey];

   73     }

   74 }



 


DependencyInjectionInstanceProvider iki tane parametreye ihtiyaç duymaktadır: dependencyFile tip tanımlamalarımızın bulunduğu config dosyasıdır. containerName config dosyası içinde tanımlı container adıdır. Her bir container sadece bir kez oluşturul ve saklanır. Oluşturulan container nesnelerine son olarak PolicyInjectionExtansion ile Policy injection destekler hale getirilir. PolicyInjectionExtansion sadece EnterpriseLibrary.PolicyInjection.ObjectBuilder altında ki strategy ve policy sınıflarını register eder. Son olarak Dependency injection instance provider'ı servislere uygulayacak yeni bir servis behavior yazmaya.



 



   19 public class DependencyInjectionServiceBehavior : Attribute, IServiceBehavior {


   33 public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) {

   34     foreach (ChannelDispatcherBase cdb in serviceHostBase.ChannelDispatchers) {

   35         ChannelDispatcher cd = cdb as ChannelDispatcher;

   36         if (cd != null) {

   37             foreach (EndpointDispatcher ed in cd.Endpoints) {

   38                 ed.DispatchRuntime.InstanceProvider =

   39                     new DependencyInjectionInstanceProvider(serviceDescription.ServiceType, configFile, containerName);

   40             }

   41         }

   42     }

   43 }



 


Evet artık WCF ile unity dependency container bir arada kullanılabilir.



   10 static void Main(string[] args) {

   11     ServiceHost hostA = null, hostB = null;

   12     try {

   13         hostA = new ServiceHost(typeof(ServiceA));

   14         hostB = new ServiceHost(typeof(ServiceB));

   15         hostA.Open();

   16         hostB.Open();



   20 [ServiceBehavior]

   21 public class ServiceA : IServiceAContract {

   22 

   23     public ServiceA() {

   24 

   25     }

   26     private IServiceBContract serviceB;

   27     private IServiceC serviceC;

   28 

   29     [InjectionConstructor]

   30     public ServiceA(IServiceBContract serviceB) {

   31         Console.WriteLine("Build up Service A");

   32         this.serviceB = serviceB;

   33         Console.WriteLine("Service A constructor calling to serviceB.OperationD");

   34         this.serviceB.OperationD("test1", "test2");

   35     }

   36 

   37     [Dependency]

   38     public IServiceC ServiceC {

   39         set {

   40             serviceC = value;

   41             Console.WriteLine("IServiceC injected to ServiceA parameter ");

   42         }

   43     }


   21 public class ServiceB : IServiceBContract {

   22     public ServiceB() {

   23 

   24     }

   25     private IServiceC serviceC;

   26 

   27     [InjectionConstructor]

   28     public ServiceB(IServiceC serviceC) {

   29         Console.WriteLine("Build up Service B ");

   30         this.serviceC = serviceC;

   31         Console.WriteLine("Service B constructor calling to serviceC.OperationG");

   32         this.serviceC.OperationG("test");

   33         this.serviceC.OperationG("test");

   34     }

Perşembe, Haziran 19, 2008

Unity 1

Unity



Client Software Factory, Web Client Software Factory, Spring.net gibi bir çok popüler application framework yapıları kendilerine ait bir object builder nesnesi içerirler. Bu application framework yapıları asıl güçlerini kendi içinde barındırdıkları object builder nesnesini kulanarak injection yapabilmelerinden kazanırlar.



Injection yapabilme güçünü application framework yapılarından bağımsız olarak kullanma isteği Pattern&Practice gurubunun Unity Dependency Container Application Block ürünü tarafından yerine getirilmiştir. Unity application block zamanla SqlHelper/OracleHelper gibi tüm uygulamalara girecek çok geniş kullanım alanı olan bir uygulama bloğudur. Bu uygulama bloğunu anlamak için harcadığınız zaman kesinlikle boşa harcanmış bir zaman değildir.



Unity Application block projemiz için object builder ve dependency container sağlamaktadır.Unity consructor, property ve methodlara injection yapabilen hafif ve genişletilebilir dependecy container uygulama bloğudur. Builder Strategy ekleyerek uygulamanıza yeni 'aspect'ler kazandırablirsiniz. Builder Policy ekleyerek uygulamanıza yeni politikalar ekleyebilirsiniz. Nesneleri config dosyasından oluştabilirsiniz.



Bu makalede Unity dependency container nasıl kullanıldığını inceleyeceğiz. Bir sonra ki makalede uygulamaya yeni aspect ve politikaların nasıl kazandırıldığını ele alacağız. Sonra ki makalede Unity ve diğer Enterprise Library application bloklarının nasıl bir arada kullanıldığını, policy injection ve dependency injection nasıl bir arada çalıştığını inceleyeceğiz. Serinin son makalesinde Unity ve WCF, ASP.NET, MCV framework gibi uygulamaların içide Unity nasıl kullandığını inceleyeceğiz.


Hemen işe başlıyalım ve unity'nin güçünü sınayalım. Tüm projelerde karşılaştığımız bir senaryo vardır. Bu senaryo benim daha önce burada anlattığım ve injection pattern doğmasına sebeb olan ilişkisel katmanlar problemidir. Business logic katmanı data access, log, security gibi bileşenleri ihtiyaç duymaktadır. Log nesneside data access nesnelerine ihtiyaç duymaktadır. Burada klasik yöntem ile yapılan çözüm nesneler her ihtiyaç anında tekrar tekrar oluşturulmaktadır. Bu da hem lock hem fazla kaynak tüketimi sorunlarna yol açmaktadır.Bizim çözümümüz uygulama içinde ihtiyaç duyulan tüm ortak nesne tanımlarını Unity container üzerinde saklamak ve ihtiyaç duyulan yerde Unity containerdan bu nesneleri talep etmektir. Unity üzerinde nesne tanımları ile birlikte lifetime container, politikalar ve builder stratejileride mevcuttur. Unity her creation işlemini bu politika ve stratejilere göre yapmaktadır.


Şimdi unity container nesnemizi oluşturalım.


   11 // dependency container oluştur

   12
IUnityContainer container = new UnityContainer();


 

Life time manager nesnelerin yaşam döngüsünü yönetirler. Nesnelerin tip tanımlarını yapılırken hangi life time manager ile saklanacağı seçilir.Kendi life time manager sınıfınızı oluşturup nesne yaşam döngülerini yönetebilirsiniz.Unity ile gelen iki adet life time manager vardır:



  • Nesne eğer singleton olarak oluşturulacaksa ContainerControlledLifetimeManager kullanılmalıdır.

  • Nesne eğer her ihtiyaçta tekrar oluşturulacaksa ExternallyControlledLifetimeManager kullanılır. Bu life time manager tip tanımlamasında default olarak kullanılır.


   13 //lifetime manager'ları oluştur

   14
ContainerControlledLifetimeManager singletonLivetime = new ContainerControlledLifetimeManager();

   15
ExternallyControlledLifetimeManager externallyLivetime = new ExternallyControlledLifetimeManager();


 


Artık tip tanımlarımızı ekleye biliriz.


 


   17 // tip tanımlarını ekle

   18 

   19 //externallyLivetime varsayılan life time manager olduğu için parametre olarak eklemeye gerek yoktur

   20 container.RegisterType<IDataAccessA, DataAccessA>(externallyLivetime);

   21 

   22 // interface olmaksızın sadece sınıf tanımı ile containere eklenti yapılabilinir

   23 container.RegisterType<Logger>(singletonLivetime);

   24 

   25 //IBusinessA çağrısı olduğu zaman geçerli BusinessA nesnesini döndür

   26 container.RegisterType<IBusinessA, BusinessA>();

   27 

   28 //IBusinessB çağrısı olduğu zaman geçerli BusinessB nesnesini döndür

   29 container.RegisterType<IBusinessB, BusinessB>();

   30 

   31 //ISecurity çağrısı olduğu zaman  Security nesnesini döndür

   32 container.RegisterType<ISecurity, Security>();

   33 

   34 //IDataAccessB tipinde ve dataAccessB anahtar kelimesi ile çağrı olduğu zaman geçerli DataAccessB nesnesini döndür

   35 container.RegisterType<IDataAccessB, DataAccessB>("dataAccessB");

   36 

   37 //IBusinessB tipinde 'singletonBusinessB' anahtarı ile çağrı olduğu zaman singleton BusinessB nesnesini döndür

   38 container.RegisterType<IBusinessB, BusinessB>("singletonBusinessB", new ContainerControlledLifetimeManager());


 



Şimdi injectionların nasıl yapıldığını inceleyelim. Unity constructor method ve property'lere injection yapabilmektedir. Eğer sınıf tanımında tek bir constructor varsa unity o constructor çağıracak ve gerekli parametreleri enjekte edecektir.



 


   20 public DataAccessA(Logger logger) {

   21     Console.WriteLine("Data access A build up");

   22     logger.AddLogger("Data access A build up");

   23 }



 


Eğer sınıf tanımında birden cok constructor varsa unity'e hangi constructor'un kullanılacağını göstermemiz gerekmektedir.InjectionConstructor attribute ile unity'e nesneyi oluşturmak için kullanılacak constructor gösterilmektedir.



 


   57 public class DataAccessB : IDataAccessB {

   58     public DataAccessB() {

   59         Console.WriteLine("Data Access B build up with default constructor");

   60     }

   61 

   62     [InjectionConstructor]

   63     public DataAccessB(Logger logger) {

   64         Console.WriteLine("Data Access B build up with InjectionConstructor");

   65         logger.AddLogger("Data Access B build up with InjectionConstructor");

   66     }



 




   



Unity constructor'e injection yaptıktan sonra sınıf tanımda injection bekleyen property'lere injection yapar. Tip tanımında isimlendirme var ise injection sırasında istediğimiz nesnenin ismini belirtmeliyiz.



 




 


   98 public class BusinessA : IBusinessA {

   99     public BusinessA() {

  100         Console.WriteLine("Business A build up");

  101     }

  102 

  103     private IDataAccessA dataAccessA;

  104     //property injection için dependency attribute kullanılır

  105     [Dependency]

  106     public IDataAccessA DataAccessA {

  107         set {

  108             this.dataAccessA = value;

  109             Console.WriteLine("IDataAccessA injecting to BusinessA");

  110         }

  111     }




 


  141 public class BusinessB : IBusinessB {

  142     public BusinessB() {

  143         Console.WriteLine("BusinessB build up");

  144     }

  145 

  146     private IDataAccessB dataAccessB;

  147     //IDataAccessB tip tanımını yaparken isimlendirme kullandığımız için

  148     //injection sırasında istediğimiz nesnenin ismini veriyoruz

  149     [Dependency("dataAccessB")]

  150     public IDataAccessB DataAccessB {

  151         set {

  152             this.dataAccessB = value;

  153             Console.WriteLine("IDataAccessA injecting to BusinessA");

  154         }

  155     }



 


Unity object builder creation sırasında en son olarak metotlara injection işlemini uygular.



 


  193 public class Security : ISecurity {      

  194 

  195       [InjectionMethod]

  196       public void Init(IDataAccessA dataAccessA,

  197           [Dependency("dataAccessB")]IDataAccessB dataAccessB,

  198           Logger logger)

  199       {

  200           Console.WriteLine("Security init method");

  201           dataAccessA.GetData1();

  202           dataAccessB.UpdateData4();

  203           logger.AddLogger("security init ok");

  204       }



 


Böylelikle uygulamamıza ait tüm nesnelerimiz tanımladık. Gerekli injection'ları ayarladık. Şimdi uygulamamızı oluşturalım.


 


   42 // security nesnesini getir

   43 ISecurity security = container.Resolve<ISecurity>();

   44 if (security.Check()) {

   45     security.Login();

   46 

   47     // geçerli BusinessA nesnesini getir

   48     IBusinessA businessA = container.Resolve<IBusinessA>();

   49     businessA.DoSameThing1();

   50 

   51     // geçerli BusinessB nesnesini getir

   52     IBusinessB businessB = container.Resolve<IBusinessB>();

   53     businessB.DoSameThing4();

   54 

   55     //singleton BusinessB nesnesi bir kez oluşturulacaktır

   56     IBusinessB singletonBusinessB1 = container.Resolve<IBusinessB>("singletonBusinessB");

   57     singletonBusinessB1.DoSameThing5();

   58     IBusinessB singletonBusinessB2 = container.Resolve<IBusinessB>("singletonBusinessB");

   59     singletonBusinessB2.DoSameThing6();

   60 

   61     // uygulama içinde oluşan nesneleride unity container'a ekleyebiliriz

   62     BusinessA singletonBusinessA = new BusinessA();

   63     singletonBusinessA.DataAccessA = container.Resolve<IDataAccessA>();

   64     container.RegisterInstance<IBusinessA>("singletonBusinessA", singletonBusinessA, new ContainerControlledLifetimeManager());

   65 

   66     //uygulama içinde oluşan singleton BusinessA nesnesi bir kez oluşturulacaktır

   67     IBusinessA singletonBusinessA1 = container.Resolve<IBusinessA>("singletonBusinessA");

   68     singletonBusinessA1.DoSameThing2();

   69     IBusinessA singletonBusinessA2 = container.Resolve<IBusinessA>("singletonBusinessA");

   70     singletonBusinessA2.DoSameThing3();

 


Son olarak childe unity container oluşturalım ve bu childe container ile config üzerinde yer alan tip tanımlamalarına göre yeni nesneler oluşturalım. Childe container ile parent container üzerinde ki tip tanımlamarını kullanabilirsiniz.



 


   72 //childe unity container oluştur

   73 IUnityContainer childeContainer = container.CreateChildContainer();

   74 // config dosyasına ac unity section oku

   75 ExeConfigurationFileMap map = new ExeConfigurationFileMap();

   76 map.ExeConfigFilename = @"D:\Project\ECoskun.UnitySample\ECoskun.UnitySample\dependency.config";

   77 System.Configuration.Configuration config

   78   = ConfigurationManager.OpenMappedExeConfiguration(map, ConfigurationUserLevel.None);

   79 UnityConfigurationSection section

   80   = (UnityConfigurationSection)config.GetSection("unity");

   81 // config dosyasına göre childe container'ı konfigüre et

   82 section.Containers["childe"].Configure(childeContainer);

   83 

   84 // config üzerinde ki tanımlara göre nesneleri oluştur

   85 IServiceA serviceA1 = childeContainer.Resolve(typeof(IServiceA)) as IServiceA;

   86 IServiceA serviceA2 = childeContainer.Resolve<IServiceA>();

   87 IServiceB serviceB = childeContainer.Resolve<IServiceB>();

   88 

   89 serviceA1.OperationA();

   90 serviceA2.OperationB();

   91 serviceA2.OperationC();

   92 serviceB.OperationD();

   93 serviceB.OperationE();



 


Konfigürasyon dosyası kendi kendisini açıklar şekildedir.



 


    9 <unity>

   10   <typeAliases>

   11     <!-- Lifetime manager types -->

   12     <typeAlias alias="singleton"

   13         type="Microsoft.Practices.Unity.ContainerControlledLifetimeManager,

   14             Microsoft.Practices.Unity" />

   15     <!-- User-defined type aliases -->

   16     <typeAlias alias="IServiceA"

   17       type="ECoskun.UnitySample.IServiceA, ECoskun.UnitySample" />

   18     <typeAlias alias="ServiceA"

   19       type="ECoskun.UnitySample.ServiceA, ECoskun.UnitySample" />

   20     <typeAlias alias="IServiceB"

   21       type="ECoskun.UnitySample.IServiceB, ECoskun.UnitySample" />

   22     <typeAlias alias="ServiceB"

   23       type="ECoskun.UnitySample.ServiceB, ECoskun.UnitySample" />

   24   </typeAliases>

   25   <containers>

   26     <container name="childe">

   27       <types>

   28         <type type="IServiceA" mapTo="ServiceA">

   29           <lifetime  type="singleton"/>

   30         </type>

   31         <type type="IServiceB" mapTo="ServiceB" />

   32       </types>

   33     </container>

   34   </containers>

   35 </unity>


 


Bu yapıyı Composite application pattern üzerinde görmüştük. Her bir use case WorkItem nesnelerine denk gelmekteydi. Tüm WorkItem nesnelerini taşıyan bir RootWorkItem nesnesi vardı. Unity container ile yukarda ki örnekte görüldüğü gibi composite application pattern vb yapıları kolaylıkla oluşturabilirsiniz.Artık uygulama nesneleriniz avucunuzun içinde uygulama nesneleri ile istediğiniz gibi oynayabilirsiniz.


restart -i

Uzun zamandır ortalıkta görünmüyordum. Makalelere baya bir ara verdim. Şimdi tekrar başlıyorum. Neler değişti ben yokken: Enterprise Library 4.0 cıktı. Bir yıldır ha cıktı ha cıkıyor diye dört gözle beklediğimiz Unity Application block hinayet geldi. Web service software factory Model edition cıktı. Ben artık wcf kodlarını kesinlikle elle yazmıyorum artık bu factory'i kullanıyorum. Smart Client/Web Client Software Factory 2008 çıktı. Web Client SF ve MVC Framework + Unity karşılaştırmasını ileri ki zamanlarda yazmayı planlıyorum.

Planım Unity'i derinlemesine inceleyen makaleler ile başlamak. Codeplex üzerinde yeni bir proje actım. WCF + Dependency Injection + Policy Injection nasıl bir arada kullanıldığını gösteriyorum. Unity container ile bir kaç farklı projeyide codeplex üzerine taşıyacağım. Aynı zamanda WSSF Model edition için makaleler hazırlıyorum.



Bu ara da Yazgeliştir Smart Client editörü oldum. Smart Client editörlüğü için planım Enterprise Library ile alakalı makaleleri bu kısımda yayınlamak. SOA AOP gibi yaklaşımları örnekleri ile göstermek. Tabii ki bu kısım için herkezden katılım bekliyorum.


Çarşamba, Şubat 06, 2008

Application Block Software Factory 3 New Block Design

Bir önce ki makalede kendimize ait yeni bir uygulama bloğu yazmıştık. Bu makale ile yazdığımız mesaj işlem süreci uygulama bloğunu Visual Studio içerisinde yapılandırma dosyalarında Enterprise Library Configuration Tool ‘u kullanmak için gereken tasarım tarafını oluşturacağız.


Enterprise Library Configuration Tool yapılandırma dosyasını bir TreeList şeklinde gösteren Visual Stdio içerisine Enterprise Library 3.1 kurulumu ile eklenen bir araçtır. Bu araç tüm uygulama bloklarını ve provider nesnelerini arayüzde gösterdiği için yapılandırma dosyası karmaşasında kurtulup daha kolay işlem yapabilmemizi sağlamaktadır.

Uygulama bloğumuzun yapılandırma dosyası tasarım eklentisi TreeNode ve ProviderData sınıflarını birbirine çevirme işleminden başka bir şey değildir. Enterprise Library Configuration Tool ProviderData sınıflarımızı TreeNode olarak ekranda göstermektedir. Dolayısıyla kendi yazdığımız uygulama bloğu için sadece TreeNode ile ProviderData arasında dönüşüm sağlayacak eklenti ile Enterprise Library Configuration Tool destekle hale gelebiliriz. ABSF bu konuda da bize yardımcı olmaktadır.

Uygulama bloğumuzun mimari yapısına benzer bir şekilde ProviderData sınıflarımızı temsil edecek Node sınıfları ABSF yardımı ile oluşturalım.





Evet, bizim için temel Node sınıfını oluşturduk. Şimdi ProcessProviderData için Node sınıfını oluşturalım.







ProcessNode sınıfımız Workers elemanına dikkate almamalıdır.

WorkerNode elemanını oluşturmaya sıra geldi. WorkerNode elemanı en alt seviyede ki elemanımız olduğu için WorkerNode’u ConfigurationNode elemanından türetiyoruz. Ayrıca WorkerNode ProcessNode altında görünmelidir.





WorkerNode içinde WorkerTypeName alanı bir tip ismini saklamaktaydı. Bu alan için özel bir editör kullanmalıyız. Enterprise Library içinde ihtiyaç duyabileceğiniz birçok editör hazır olarak sağlanmaktadır. WorkerTypeName bir tip ismi sakladığına göre bir TypeSelectorEditor ile değer atanabilmelidir. Temsil ettiği tipte BaseType ile gösterilmektedir.



[Editor(typeof(TypeSelectorEditor), typeof(UITypeEditor))]
[BaseType(typeof(IWorker))]
[SRDescription("WorkerTypeNameDescription", typeof(Resources))]
[SRCategory("CategoryGeneral", typeof(Resources))]
public System.String WorkerTypeName {….


Böylelikle ihtiyacımız olan her üç node oluşmuş oldu fakat şu anda tamamen yapılandırma dosyasından bağımsız hareket ediyorlar. (Şu anda her node istediğimiz yerde gözükecektir fakat yapılandırma dosyasında WorkerProviderData ProcessProviderData altında değil bir üst alanda oluşacaktır.) Neler oluştuğuna bir göz atalım.



Enterprise Library Configuration Tool önce AddApplicationBlockSettingsNodeCommand ile acılır menü üzerine yazdığımız uygulama menüsünü eklemektedir. Her bir node üzerinde açılır menüye CommandRegistrar ile oluşturduğumuz node elemanlarını ekleme kodunu oluşturmaktadır. Enterprise Library Configuration Tool yapılandırma dosyasından (diğer bir değişle ProviderData sınıflarından) node elemanlarına dönmek için ApplicationBlockSettingsNodeBuilder sınıfını kullanmaktadır. Node elemanlarında yapılandırma dosyasına dönmek içinse ApplicationBlockSettingBuilder sınıfını kullanmaktadır.
Birinden bağımsız hareket eden node ve yapılandırma dosyamızı ApplicationBlockSettingsBuilder ve ApplicationBlockSettingsNodeBuilder sınıflarına yazacağımız kodlar ile bir birleri ile çalışır hale getirmemiz gerekmektedir. Bizim için problem sadece ProcessProviderData sınıfı içinde ki Workers elemanını yapılandırma dosyasında gösterme durumudur.



sealed partial class ApplicationBlockSettingsNodeBuilder {
private void BuildProcesserProviderProviders() {
foreach (ProcesserProviderData providerData in
blockSettings.ProcesserProviderProviders)
{
ProcesserNode providerNode =
NodeCreationService.CreateNodeByDataType(
providerData.GetType(),
new object[] { providerData }) as ProcesserNode;
if (providerData is ProcessProviderData) {
// her ProcessProviderData için Workers alanını doldur
BuildProcessProvider(providerNode as ProcessNode,
providerData as ProcessProviderData);
}
node.AddNode(providerNode);
}
}

private void BuildProcessProvider(ProcessNode processNode, ProcessProviderData processProviderData) {
WorkerNode workerNode;
foreach(WorkerProviderData workerData in processProviderData.Workers) {
workerNode =
NodeCreationService.CreateNodeByDataType(
workerData.GetType(),
new object[] { workerData }) as WorkerNode;
processNode.AddNode(workerNode);
}
}
}


Böylelikle ProcessProviderData içinde ki Workers elemanlarını node olarak oluşturduk ve TreeList üzerine eklemiş olduk. Node elemanlarını ProviderData’ya ceviren sınıfada kendi eklentilerimizi yazalım.



sealed partial class ApplicationBlockSettingsBuilder {
private void BuildProcesserProviderProviders() {
ProcesserProviderData processerData;
foreach (ProcesserNode node in hierarchy.FindNodesByType(blockSettingsNode, typeof(ProcesserNode))) {
processerData = node.ProcesserProviderData;
if (node is ProcessNode) {
// ProcessProviderData sınıfına ait Workers elemanlarını oluştur
FillWorkersData(processerData as ProcessProviderData, node as ProcesserNode);
}
blockSettings.ProcesserProviderProviders.Add(processerData);
}
}

private void FillWorkersData(ProcessProviderData processerData, ProcesserNode processerNode) {
IList<ConfigurationNode> workersNode = hierarchy.FindNodesByType(processerNode, typeof(WorkerNode));
WorkerProviderData workerData;
WorkerNode workerNode;
foreach (ConfigurationNode currentNode in workersNode) {
workerNode = (currentNode as WorkerNode);
workerData = new WorkerProviderData(workerNode.WorkerProviderData);
processerData.Workers.Add(workerData);
}
}
}


Hepsi bu kadar! Resource üzerinde ki açıklama ve başlık alanlarını da kendimize göre güncelleyelim. Artık tasarım ve uygulama bloğu dll dosyalarını Enterprise Library Bin dizinine ekleyerek istediğim proje içerisinden MessageProcessApplicationBlock kullanabiliriz.



Böylelikle bir makale serisinin daha sonuna geldik. Bu makale serisi ile önce hazır uygulama blokları için yeni Provider kütüphanelerinin nasıl yazıldığını inceledik. Daha sonra kendi uygulama bloğumuzu oluşturduk. En son olarak oluşturduğumuz uygulama bloğunu daha kolay yapılandırılabilmesi için Enterprise Library Configuration Tool ile uyumlu hale getirdik.
Bir sonra ki makale serisi MVC Framework ve Injection Policy Application Block olacak.



Application Block Software Factory 2 New Block

Bir önce ki bölümle Enterprise Library için yeni Provider’lar nasıl eklendiğini inceledik. Ayrıca eklediğimiz yeni providerların Enterprise Library Configuration ile birlikte nasıl kullanıldığını da inceledik. Şimdi Enterprise Library sitilinde yeni bir uygulama bloğu oluşturalım. Aspx page işlemlerinin anımsatacak bir mesaj işlem süreci uygulama bloğu yazıyoruz. Bu uzun makalenin sonuna kadar sabır edebilirseniz kendi Enterprise Library kütüphanenizi yazabilirsiniz.


Mesaj İşlem Süreci Uygulama Bloğu



Muhtemel bir uygulama senaryosu kullanacağız. Bir mesajlaşma uygulama bloğu oluşturacağız. Müşterileriniz sizden bilgi talebinde bulunur. Daha sonra her talep işlenir ve müşteriye geri dönüş yapılacak mesajlar oluşturulur ve müşteriye cevap dönülür. Yazacağımız uygulama bloğu aslında Aspx page işlemlerinin bir benzeri. Uygulama bloğumuz her bir müşterimiz için Request -> Calculate -> Response döngüsü içinde mesaj trafiğini yönetmeli. Uygulama bloğumuz sadece mesaj saklama ve mesaj işlem sınıfları arasında mesajları hareket ettirmeden sorumlu olacak. Bizim uygulama bloğumuzu kullanan projeler her bir mesaj işleme adım için bir veya daha fazla sayıda kendi mesaj işlem sınıfları kullanacak. Hemen konuyu soyut olmaktan kurtaralım.



<MessageProcessApplicationBlock>
<ProcesserProviders>
<add type=" MessageProcessApplicationBlock.ProcessProvider" name="Test Message Process 1">
<Workers>
<add ExecuteTime="60" MessageQPath=".\Private$\colorQ" State="Calculate" WorkerType="ColorCalculaterWorker, ….."
type="MessageProcessApplicationBlock.WorkerProvider,…. "
name="ColorWorker" />
<add ExecuteTime="60" MessageQPath=".\Private$\errorQ" State="Error"
WorkerType="ErrorWorker, ….."
type="MessageProcessApplicationBlock.WorkerProvider, ……"
name="Error" />
<add ExecuteTime="60" MessageQPath=".\Private$\intQ" State="Calculate"
WorkerType="IntCalculater….."
type="MessageProcessApplicationBlock.WorkerProvider, ……"
name="IntWorker" />
<add ExecuteTime="10" MessageQPath=".\Private$\requestQ" State="Request"
WorkerType="RequestWorker,……"
type="MessageProcessApplicationBlock.WorkerProvider……"
name="Request" />
<add ExecuteTime="10" MessageQPath="Private$\responseQ" State="Response" WorkerType="ResponseWorker,……"
type="MessageProcessApplicationBlock.WorkerProvider, ……"
name="Response" />
</Workers>
</add>
</ProcesserProviders>
</MessageProcessApplicationBlock>



Yukarıda ki xml ile göreceğiniz gibi yazmayı amaçladığımız uygulama bloğu her bir müşteri için bir mesajlaşma işlemi açmaktadır. Örneğimizde “Test Message Process 1” ile açılan mesajlaşma işlemi beş adet mesaj işlem sınıfı eklentisine sahiptir. Uygulama bloğumuza mesaj işlem sınıflarını eklerken mesaj işlem sınıfı ile birlikte bir mesaj kuyruğu ve mesaj işlem sınıfının çalışma aralığını da belirtmekteyiz.



<add ExecuteTime="10" MessageQPath=".\Private$\requestQ" State="Request"
WorkerType="RequestWorker,……"
type="MessageProcessApplicationBlock.WorkerProvider……"
name="Request" />


Uygulama bloğu tüm mesaj kuyruklarını, mesajları, mesaj işlemci sınıfları ve mesaj işlemci sınıflarının çalışma zaman aralıklarını yönetmektedir. Mesaj işlemci sınıfları ise sadece kendisine verilen mesajı işlemekle yetinmektedir. Uygulama bloğu her ExecuteTime sürecinde şu şekilde çalışacaktır:
1) Mesaj kuyruğu üzerinde ki tüm mesajlar bitene kadar 2. Adımı tekrar et
2) Mesaj kuyruğunda en üste ki mesajı çek
2)a. Mesaj işlem sınıfına işlemek üzere gönder
2)b. Eğer mesaj işlem sınıfı sonuç mesajı döndürdü ise
2)b.i. Yeni sonuç mesajını bir sonra ki “State “ değerine sahip mesaj işlem sınıfı kuyruğuna ekle
2)b.ii. Eğer ekleyecek bir mesaj işlem kuyruğu bulanamazsa “Error State” özellikli mesaj kuyruğuna ekle

Böylelikle nasıl bir uygulama bloğu yazacağımızı planladık. Şimdi sıra geldi uygulama bloğumuzu oluşturmaya.

New Application Block


Önce projemizi oluşturalım.





Şimdi uygulama bloğumuzu temel işlemlerini gösteren temel sınıfı ve bu soyut temel sınıfı yapılandırma dosyası üzerinde ki verilere göre üretecek factory sınıfını oluşturalım.





Temel sınıf hiçbir ekstra özelliğe sahip değildir. Sadece üst sınıfın adını vermekteyiz. Bu adım ile bizim için birçok sınıf oluşturulmaktadır.



IProcesserProvider bizim uygulama bloğumuzun temel işlevlerini gösteren ara yüzdür. ProcessProviderFactory ise config dosyadan IProcesserProvider ara yüzünü uygulayan ProcesserProvider nesnelerini üreten sınıftır.

Mesaj işleme sürecinin çok basit iki fonksiyonu vardır: Start, Stop



namespace MessageProcessApplicationBlock {
/// <summary>
/// Defines the basic functionality of an ProcesserProvider
/// </summary>
[ConfigurationNameMapper(typeof(ProcesserProviderDataRetriever))]
[CustomFactory(typeof(ProcesserProviderCustomFactory))]
public interface IProcesserProvider {
/// <summary>
/// Mesaj işleme sürecini başlat
/// </summary>
void Start();

/// <summary>
/// Mesaj işleme sürecini bitir
/// </summary>
void Stop();
}
}


Daha sonradan yazacağımız tüm uygulama bloğu provider sınıflarının atası olacak soyut ProcesserProvider sınıfı IProcesserProvider ara yüzünü basit bir uygulamasıdır.




namespace MessageProcessApplicationBlock {
/// <summary>
/// Abstract implementation of the <see cref="IProcesserProvider"/> interface.
/// </summary>
public abstract class ProcesserProvider : IProcesserProvider {
#region IProcesserProvider Members

public abstract void Start();

public abstract void Stop();

#endregion
}
}


Şimdi mesaj işleme sürecini temsil edecek olan ProcessProvider sınıfı oluşturalım.





Süreci temsil edecek olan ProcessProvider sınıfı uygulama bloğumuza ait temel sınıflardan türetilmektedir.

Assembler



Burada durup işin mutfağına küçük bir göz atalım. Uygulama bloğun yazarken en önemli sorun config üzerinde ki xml node elemanları ile kendi uygulama sınıflarımızı oluşturma sorunudur. Injection Pattern’i hatırlayınız. Injection Pattern ile config üzerinde ki xml node bilgilerinden sınıflarımızı oluşturabiliriz. Enterprise Library’de config üzerinde ki xml node bilgilerinden gerçek sınıflar oluşturmak için Injection Pattern kullanmaktadır.

Data(xml node) sınıfları ile gerçek sınıflar arası dönüşümleri IAssembler ara yüzünü uygulayan sınıflar yapmaktadır. AssemblerBasedObjectFactory sınıfı Factory sınıflarımızın temel sınıfıdır. AssemblerBasedObjectFactory nesne oluşturmak için önce oluşturulacak nesneye ait Assembler sınıfı bulur. Daha sonra oluşturulacak nesneye ait Assembler sınıfından nesneyi oluşturması ister.



public abstract class AssemblerBasedObjectFactory<TObject, TConfiguration>
where TObject : class
where TConfiguration : class
{……………………
public virtual TObject Create(IBuilderContext context, TConfiguration objectConfiguration, IConfigurationSource configurationSource, ConfigurationReflectionCache reflectionCache)
{
IAssembler<TObject, TConfiguration> assembler = GetAssembler(objectConfiguration);
TObject createdObject = assembler.Assemble(context, objectConfiguration, configurationSource, reflectionCache);
return createdObject;
}}


Assembler sınıflar ABSF tarafından bizim için oluşturulmaktadır. Sadece tek bir fonksiyona sahiptir. Assemble fonksiyonu önce oluşturulacak nesneye ait yapılandırma bilgisini alır ve nesne için uygun yapılandırma bilgisine çevirir. Daha sonra bu yapılandırma verisi ile nesneyi oluşturur.



IAssembler<MessageProcessApplicationBlock.IProcesserProvider, MessageProcessApplicationBlock.Configuration.ProcesserProviderData> {

public MessageProcessApplicationBlock.IProcesserProvider Assemble(IBuilderContext context, MessageProcessApplicationBlock.Configuration.ProcesserProviderData objectConfiguration, IConfigurationSource configurationSource, ConfigurationReflectionCache reflectionCache) {

ProcessProviderData castObjectConfiguration
= (ProcessProviderData)objectConfiguration;

ProcessProvider createdObject
= new ProcessProvider(castObjectConfiguration);

return createdObject;
}
}


EnterpriseLibraryFactory



Peki nesneler nerede saklanmaktadır. Injection yapacak Builder nerededir?

Tüm nesneler static EnteriseLibraryFactory sınıfı içinde ki Builder nesnesi ile oluşturulmaktadır. ConfiguredObjectStrategy özel Factory sınıfına sahip nesneleri kendi factory sınıfı ile oluşturmaktadır. Böylelikle bizim Assembler sınıflarımız EnterpriseLibraryFactory tarafından fark edilmekte ve yazdığımız provider’lar kendilerine ait assembler sınıfları yardımı ile oluşturulmaktadırlar.



public static class EnterpriseLibraryFactory
{
private static IBuilder<BuilderStage> builder;
private static ConfigurationReflectionCache reflectionCache = new ConfigurationReflectionCache();
static EnterpriseLibraryFactory()
{
builder = new BuilderBase<BuilderStage>();
builder.Strategies.AddNew<ConfigurationNameMappingStrategy>(BuilderStage.PreCreation);
builder.Strategies.AddNew<SingletonStrategy>(BuilderStage.PreCreation);
builder.Strategies.AddNew<ConfiguredObjectStrategy>(BuilderStage.PreCreation);
builder.Strategies.AddNew<InstrumentationStrategy>(BuilderStage.PostInitialization);
}
…………..
public static T BuildUp<T>(IReadWriteLocator locator, string id, IConfigurationSource configurationSource)
{
if (string.IsNullOrEmpty(id))
throw new ArgumentException(Resources.ExceptionStringNullOrEmpty, "id");
if (configurationSource == null)
throw new ArgumentNullException("configurationSource");
return GetObjectBuilder().BuildUp<T>(locator, id, null, GetPolicies(configurationSource));
}
}

public class ConfiguredObjectStrategy : EnterpriseLibraryBuilderStrategy
{
……..
public override object BuildUp(IBuilderContext context, Type t, object existing, string id)
{
…..
ICustomFactory factory = GetCustomFactory(t, reflectionCache);
if (factory != null)
{
existing = factory.CreateObject(context, newId, configurationSource, reflectionCache);
} else{….}
….
return base.BuildUp(context, t, existing, newId);
}
}


Böylelikle Enterprise Library kütüphanesinde ki ve ABSF ile yazdığımız kendi uygulama bloğumuz içinde ki tüm nesneler EnterpriseLibraryFactory sınıfı tarafında yukarıda gösterildiği şekilde üretilmektedir.

WorkerProvider



Mesaj işleme sürecini oluşturduk. Şimdi süreç içerisinde her bir mesaj işleme adımını yerine getirecek olan WorkerProvider sınıfımızı oluşturalım.



ProcessProvider sınıfı birçok WorkerProvider sınıfa ihtiyaç duymaktadır. Süreci yöneten ProcessProvider süreç içinde her bir mesaj işleme adımını sahibi olduğu WorkerProvider elemanlarına yaptırmaktadır.

Öncelikle ProcessProviderData sınıfına sürec işci sınıflarını ekleyelim.



[Assembler(typeof(ProcessProviderAssembler))]
public class ProcessProviderData : MessageProcessApplicationBlock.Configuration.ProcesserProviderData {
……
private const string workersProperty = "Workers";
// konfigürasyon versine Worker sınıflarını ekle
[ConfigurationProperty(workersProperty)]
public NamedElementCollection<WorkerProviderData> Workers {
get {
return (NamedElementCollection<WorkerProviderData>)this[workersProperty];
}
}
}

public class ProcessProviderAssembler : IAssembler<…..> {
public MessageProcessApplicationBlock.IProcesserProvider Assemble(…….) {
ProcessProviderData castObjectConfiguration
= (ProcessProviderData)objectConfiguration;
ProcessProvider createdObject
= new ProcessProvider(castObjectConfiguration);
// konfigürasyonda yer alan Worker sınıflarıne gerçek sınıfa cevir
foreach (WorkerProviderData workerData in castObjectConfiguration.Workers) {
WorkerProvider worker = new WorkerProvider(workerData);
createdObject.Workers.Add(worker);
worker.ProcessWorkers = createdObject.Workers;
}
return createdObject;
}
}

[ConfigurationElementType(typeof(ProcessProviderData))]
public class ProcessProvider : MessageProcessApplicationBlock.ProcesserProvider {
public ProcessProvider(ProcessProviderData configuration) {
_workers = new List<WorkerProvider>();
}
private List<WorkerProvider> _workers;
// konfigürasyonda yer alan Worker sınıflarında taşıyan liste
internal IList<WorkerProvider> Workers {
get {
return _workers;
}
}

public override void Start() {
foreach (WorkerProvider worker in Workers) {
worker.Start();
}
}

public override void Stop() {
foreach (WorkerProvider worker in Workers) {
worker.Stop();
}
}


Böylelikle konfigurasyon dosyasında her bir Process xml node içinede “Workers” node açmış olduk. Workers node içerisinde WorkerProvider node’larını barındırmaktadır. Konfigurasyon sınıfına eklediğimiz “Workers” node’a karşılık olarak ProcessProvider sınıfına “Workers” listesini ekledik. Konfigürasyon ile gerçek sınıflar arasında dönüşümü sağlayan ProviderAssembler sınıfında konfigürasyonda ki “Workers” node bilgisinden gerçek WorkerProvider nesneleri ürettik ve üretilen WorkerProvider nesnelerini ProcessProvider nesnesi üzerinde ki Workers listesine ekledik.

Artık mesaj işlem sürecini yöneten ProcessProvider sınıfımız hazır. Şimdi mesaj işlem adımlarını yerine getirecek WorkerProvider sınıfmızı tamamlamalıyız. WorkerProvider sınıfı mesaj işleme adımının çalışma periyodunu ve mesaj kuyruğu işlemlerini yönetmektedir.Mesaj işleme adımında mesajı değerlendirecek ve sonuç mesajı üretecek olan IWorker arayüzünü uygulayan sınıflardır. IWorker sınıfları sürec yönetimini düşünmeden sadece mesajı alır işler ve sonuç mesaj üretir.



/// <summary>
/// mesaj işleme arayüzü
/// </summary>
public interface IWorker {
/// <summary>
/// <see cref="message"/> bu işlemci tarafında işlenebilir mi testini yapar
/// </summary>
/// <param name="message">test edilecek mesaj nesnesi</param>
/// <returns><see cref="message"/> işlenebilirse true değilse false</returns>
bool CanExecute(object message);

/// <summary>
/// <see cref="message"/> nesnesini işle ve sonuc mesajı döndür
/// </summary>
/// <param name="message">işlenmesi gereken mesaj</param>
/// <returns>eğer sonuc mesaj varsa return edilmeli yoksa null</returns>
object Execute(object message);

/// <summary>
/// yeni bir mesaj oluştu uygun mesaj kuyruğunu bul ve kuyruğa ekle
/// </summary>
/// <remarks>
/// Her hangi bir mesaj kuyruk üzerine eklenmesi gerekiyorsa bu event çağrılır.
/// Event MessageProcessApplicationBlock tarafında handle edilmektedir.
/// Mesajı alır ve CanExecute true olan WorkerProvider'ın mesaj kuyruğune ekle
/// </remarks>
event EventHandler<MessageEventArgs> AddMessageQ;

/// <summary>
/// Her executetime sürecinde bir defa çağırılır
/// </summary>
void Check();

/// <summary>
/// Mesaj kuyruğunda her hangi bir değişiklik olduğu zaman
/// istemciyi uyarmak için kullanılır
/// </summary>
/// <param name="messages">kuyruk üzerinde ki tüm mesajlar</param>
void OnMessageQChanged(Message[] messages);
}


WorkerProvider dışarıdan IWorker arayüzünü uygulayan nesneyi almaktadır. Ayrıca mesaj kuyruğunu ve çalışma periyotlarını bilmesi gerekmektedir.



[Assembler(typeof(WorkerProviderAssembler))]
public class WorkerProviderData : MessageProcessApplicationBlock.Configuration.ProcesserProviderData {
………
[ConfigurationProperty(executeTimeProperty, IsRequired = true, DefaultValue = 30)]
public int ExecuteTime { get {…} set {…}}

[ConfigurationProperty(messageQPathProperty, IsRequired = true, DefaultValue = "")]
public string MessageQPath { get {…} set {…}}

[ConfigurationProperty(stateProperty, IsRequired = true, DefaultValue = MessageState.None)]
public MessageState State { get {…} set {…}}

[ConfigurationProperty(workerTypeProperty, IsRequired = true)]
public string WorkerTypeName { get {…} set {…}}

public Type WorkerType {
get {
return (Type)typeConverter.ConvertFrom(WorkerTypeName);
}
set {
WorkerTypeName = typeConverter.ConvertToString(value);
}
}
}



WorkerProviderData sınıfına bilmemiz gereken tüm verileri ekledik.WorkerProivderData sınıfı çalışma zamanı periyotunu, mesaj kuyruğunu ve IWorker arayüzünü uygulayan sınıfı almaktadır. Uygulama bloğumuzun asıl işini oluşturan WorkerProvider sınıfını yazalım.



[ConfigurationElementType(typeof(WorkerProviderData))]
public class WorkerProvider : MessageProcessApplicationBlock.ProcesserProvider {
private Timer _timer;
private MessageQ _messageQ;
private IWorker _workerClient;
private IList<WorkerProvider> _workers;
private MessageState _state;

public WorkerProvider(WorkerProviderData configuration) {
_state = configuration.State;
_timer = new Timer();
_timer.Interval = configuration.ExecuteTime * 1000;
_timer.Elapsed += new ElapsedEventHandler(_timer_Elapsed);
_messageQ = new MessageQ(configuration.MessageQPath);
_workerClient = (IWorker)Activator.CreateInstance(configuration.WorkerType);
_workerClient.AddMessageQ += delegate(object sender, MessageEventArgs e) {
AddMesssageQ((MessageState)(_state + 1), e.Message);
};
_messageQ.MesssageQChanged += delegate(object sender, MessageQEventArgs e) {
_workerClient.OnMessageQChanged(e.Messages);
};
}

internal IList<WorkerProvider> ProcessWorkers {
set {
_workers = value;
}
}

……
void _timer_Elapsed(object sender, ElapsedEventArgs e) {
Message message;
object resultMessage;
while (_messageQ.MessageCount > 0 && _timer.Enabled) {
message = _messageQ.Recive();
resultMessage = _workerClient.Execute(message.Body);
if (resultMessage != null)
AddMesssageQ((MessageState)(_state + 1), resultMessage);
}
if (_timer.Enabled)
_workerClient.Check();

}

private void AddMesssageQ(MessageState nextState, object message) {
if (message != null) {
if (nextState == MessageState.None) {
nextState = (MessageState)(_state + 1);
}
foreach (WorkerProvider worker in _workers) {
if (worker.State == nextState && worker.Worker.CanExecute(message)) {
worker.MessageQ.Send(this, message);
System.Threading.Thread.Sleep(1000);
return;
}
}
AddErrorQ(message);
}
}
……….

public override void Start() {
_timer.Start();
}
public override void Stop() {
_timer.Stop();
}
}


İşte bu kadar! Mesaj işleme süreci uygulama bloğumuzu tamamladık. Aşağıda ki diyagramda görüldüğü gibi mesaj işlem süreci uygulama bloğumuz IProcesserProvider ile belirtilen işleri yerine getiren bir uygulam bloğudur. IProcesserProvider arayüzüne ait işleri özet ProcesserProvider sınıfı uygulamaktadır. Ata sınıfımız olan ProcesserProvider sınıfını uygulayan ProcessProvider mesaj işlem adımlarını yerine getirecek olan bir çok WorkerProvider sınıfına sahiptir. WorkerProvider sınıfı süreç kontrollerini yerine getirirken mesaj işleme işini IWorker arayüzünü uygulayan eklenti sınıfından beklemektedir.



Bir program ile uygulama bloğumuzu deneyelim. Bizim için rasgele mesajlar üreten bir RequestWorker sınıfı yazalım. Bu sınıf Color, Int ve String tipinde mesajlar oluştursun. ColorWorker, IntWorker ve StringWorker sınıfları bu mesajları alsın ve string tipine dönüştürüp ResponseWorker sınıfına versin. ResponseWorker sınıfı her ExecuteTime ile kendi mesaj kuyruğunda ki tüm mesajları silsin. Tüm mesaj kuyrukları girdler ile ekranda gösterelim ve ne olup bittiğini ekran üzerinden takip edelim.




public class RequestWorker : IWorker{
public RequestWorker() {
}

#region IWorker Members

public bool CanExecute(object message) {
// request worker hiçbir mesajı q üzerine kabul etmez
return false;
}

public object Execute(object message) {
// q üzerinde mesaj olmadı için mesajları işlemez
return null;
}

public event EventHandler<MessageEventArgs> AddMessageQ;

public void Check() {
// her çalışma zamanında random renkler, sayılar ve stringler üretir ve MessageQ üzerine ekletir
Random random = new Random();
random.Next();
int randomColorCount = random.Next(1,5);
Color color;
int type;
for (int i = 0; i < randomColorCount; i++) {
lock (this) {
type = random.Next(1, 4);
switch (type) {
case 1:
color = ColorTranslator.FromWin32(random.Next());
AddMessageQ(this, new MessageEventArgs(color));
break;
case 2:
AddMessageQ(this, new MessageEventArgs(random.Next()));
break;
case 3:
AddMessageQ(this, new MessageEventArgs(random.Next(10000, 1000000000).ToString("X")));
break;
case 4:
AddMessageQ(this, new MessageEventArgs(random.Next(1, 1000) / random.Next(2, 488)));
break;
}
}


}
}

public void OnMessageQChanged(Message[] messages) {
// mesaj kuyruğu değişti ekran üzerinde göster
Program.MainForm.SetRequestMessages(messages);
}

#endregion
}


public class IntCalculater : IWorker{
public IntCalculater() {
}
#region IWorker Members

public bool CanExecute(object message) {
return message is int;
}

public object Execute(object message) {
return message.ToString();
}

public event EventHandler<MessageEventArgs> AddMessageQ;

public void Check() {

}
public void OnMessageQChanged(Message[] messages) {
Program.MainForm.SetIntMessages(messages);
}
#endregion

}

Konfigürasyon üzerinde makalenin en başında ki gibi tanımlanmış sürec bilgilerine ekleyelim. Artık tüm bu süreci başlatmak ve dururmak gerçekten çok kolay.

public partial class Form1 : Form, IMainForm {
…..
IProcesserProvider _processer = null;
private void _btnStart_Click(object sender, EventArgs e) {
if (_btnStart.Text == StartString) {
_processer = ProcesserProviderFactory.CreateProcesserProvider("Test Message Process 1");
_processer.Start();
_btnStart.Text = StopString;
} else {
_processer.Stop();
_btnStart.Text = StartString;
}
}


Bir sonra ki makalede konfigürasyon dosyasına kendi uygulama bloğumuzu elle eklemek yerine, ABSF kullanarak Visual Stidio icerisinde ki Enterprise Library Configuration tool ile nasıl eklenildiğini inceliyeceğiz.



Pazartesi, Ocak 21, 2008

Application Block Software Factory 1 Provider Lib

Her birimiz kendi projemizin uygulama alanına ait bir takım kütüphaneler oluşturmuşuzdur. Bu kütüphanelerin Enterprise Library ile birlikte çalışmasını ve Enterprise Library gibi yapılandırma ve iş kısımlarının bir birinden ayrılmasını ister misiniz? Enterprise Liblary sitilinde uygulama blokları nasıl yazıldığını inceliyoruz.


İlk kısımda Enterprise Library’e ait mevcut Application Block’lar üzerine yeni Provider’lar Application Block Software Factory(ABSF) ile nasıl eklendiğini inceleyeceğiz. İkinci kısımda kendi Block’larımızı yazmak için gerekli kavramları göreceğiz ve yeni bir uygulama bloğu yazacağız.
İlk kısımda uygulama olarak kendimize çok muhtemel bir senaryoyu ele alalım. Program üzerinde oluşan kritik hataları yazacağımız/oluşturacağımız ExceptionHanding Application Block Provider ile yakalayalım ve Logging Aplication Block Provider ile kayıt etmek üzere bir web servise yönlendirelim. Tabi bu arada Cripto Apllication Block ile web servis iletişimini şifreleyelim ve web servi üzerinde veri tabanına kayıt etmek için Data Access Application Block kullanalım.

Öncelikle buradan Enterprise Library son sürümünü Guidance Automation Extensions (GAX) ve Guidance Automation Toolkit (GAT) bilgisayarınıza yükleyiniz.
Şimdi önce Aplication Block Software Factory ile yeni bir Provider Library oluşturalım.



Smart Client Software Factory serisini takip etti iseniz projeyi oluşturmadan önce Software Factory’nin sizden projenin genel özellikleri sormasını beklersiniz.



Evet Finish ile Provider kütüphanemizi oluşturalım. Dört ayrı proje oluşacaktır.



CriticalLibrary bizim provider sınıflarımızın bulunacağı yerdir. CriticalLibrary.Configuration.Design config dosyası içinde bizim provider sınıflarımıza ait node tanımlarının bulunduğu yerdir. Her iki projeye dair test projeleri de Unit Tests dizini içerisinde oluşmaktadır. Şimdi yeni bir Exception Handler ekleyelim.



Application Block Software Factory içerisinde her bir Provider için Typed/Untyped secenekleri görmektesiniz. Typed Provider kendisine ait veri yapısı olan Provider’lardır. Configuration dizini içinde Provider’a ait veri yapısı ile birlikte oluşturulurlar.



Evet CriticalExceptionHandler Povider sınıfımızı ekleyelim. Typed opsiyonunu seçtiğimiz için Configuration dizini içerisinde CriticalExceptionHandlerData sınıfı da oluşacaktır.



/// <summary>
/// TODO: Add CriticalExceptionHandler comment.
/// </summary>
[ConfigurationElementType(typeof(CriticalExceptionHandlerData))]
public class CriticalExceptionHandler :IExceptionHandler {
/// <summary>
/// <para>Initializes a new instance of the <see cref="CriticalExceptionHandler"/>.</para>
/// </summary>
/// <param name="configuration">The configuration object used to set the runtime values</param>
public CriticalExceptionHandler(CriticalExceptionHandlerData configuration) {
// TODO: Use the CriticalExceptionHandlerData object to set runtime values for the CriticalExceptionHandler.
}
#region IExceptionHandler Members
/// <summary>
/// Handles the exception.
/// </summary>
/// <param name="exception">The original exception.</param>
/// <param name="handlingInstanceId">The unique identifier attached to the handling chain for this handling instance.</param>
/// <returns>Modified exception to pass to the next exceptionHandlerData in the chain.</returns>
public Exception HandleException(Exception exception, Guid handlingInstanceId) {
throw new Exception("The method or operation is not implemented.");
}
#endregion


HandleException metodunu doldurarak Exception Handling Application Block Provider’ımızı tamamlayalım. Öncelikle hatalara ait bir veri standardı belirlememiz gerekmektedir. Bir xsd ile hatalara ait kayıt edilecek veri yapısını oluşturalım.



Gördüğünüz üzere oluşan hataları kayıt etmek için oldukça geniş detaylı bilgi sağlanmaktadır. Şimdi CriticalExceptionHandler için bu detaylı bilgiyi sağlayacak yeni bir untyped formater’a ihtiyacımız var. Benzer adımları tekrarlayacak CriticalExceptionFormatter sınıfını oluşturalım. CriticalExceptionFormatter Logger formatter sınıflarından farklı olarak ExceptionFormmatter sınıfından türetelim. ExceptionFormmatter özet sınıfı size yapmanız gereken işin gelen hata objesinden ve sistemden topladığınız bilgileri memoryStream üzerine kayıt etmek olduğunu söyler.



[ConfigurationElementType(typeof(CustomHandlerData))]
public class CriticalExceptionFormatter :ExceptionFormatter {
………………
public CriticalExceptionFormatter(MemoryStream memStream, Exception exception): base(exception) {
if (memStream == null)
throw new ArgumentNullException("memStream", " 'memStream' parametresi null olamaz.");
if (exception == null)
throw new ArgumentNullException("exception", "'exception' parametresi null olamaz.");
// hatanın saklanacağı stream
_memStream = memStream;
}
public override void Format() {
try {
_errorDS = new ErrorDS();
// kendi WriteException fonksiyonun ile log oluştur
WriteException(base.Exception, Guid.NewGuid());
// oluşan log bilgisini stream üzerine kayıt et
_errorDS.WriteXml(_memStream);
} catch(Exception ex) {
Debug.WriteLine("Hata Formatmala Hatası:" + ex.Message);
throw ex;
}
}
}


Evet, şimdide CriticalExceptionHandler sınıfını tamamlayalım.



/// <summary>
/// <summary>
/// Handles the exception.
/// </summary>
/// <param name="exception">The original exception.</param>
/// <param name="handlingInstanceId">The unique identifier attached to the handling chain for this handling instance.</param>
/// <returns>Modified exception to pass to the next exceptionHandlerData in the chain.</returns>
public Exception HandleException(Exception exception, Guid handlingInstanceId) {
Guid issueTag = handlingInstanceId;
string xmlExceptionData = "";
using(MemoryStream memStream = new MemoryStream()) {
// hata ile ilgili raporlamaları ErrorDS içinde topla
CriticalExceptionFormatter formatter =
new CriticalExceptionFormatter(memStream, exception);
formatter.Format();
xmlExceptionData = Encoding.UTF8.GetString(memStream.ToArray());
}
// ekstra bilgiler için
Dictionary<string, object> dictionary =
new Dictionary<string, object>();
// managed security context
ManagedSecurityContextInformationProvider mgdInfoProvider =
new ManagedSecurityContextInformationProvider();
mgdInfoProvider.PopulateDictionary(dictionary);
//unmanaged security context
UnmanagedSecurityContextInformationProvider unmgdInfoProvider =
new UnmanagedSecurityContextInformationProvider();
unmgdInfoProvider.PopulateDictionary(dictionary);
// com plus information
ComPlusInformationProvider complusInfoProvider =
new ComPlusInformationProvider();
complusInfoProvider.PopulateDictionary(dictionary);
// Logla
LogEntry logEntry = new LogEntry();
logEntry.EventId = GetNextEventId();
logEntry.Severity = TraceEventType.Critical;
logEntry.Priority = 2;
logEntry.Message = xmlExceptionData;
logEntry.Categories.Add(Categories.CriticalExceptionCategory);
logEntry.ExtendedProperties = dictionary;
logEntry.ExtendedProperties.Add("IssueTag", issueTag);
// MAC address al
NetworkInterface[] interfaces = NetworkInterface.GetAllNetworkInterfaces();
if(interfaces.Length > 0) {
string macAddress = interfaces[0].GetPhysicalAddress().ToString();
logEntry.ExtendedProperties.Add("SiteCode", macAddress);
}
// hatayı logla
Logger.Write(logEntry);
// hatayı geri döndür
return exception;
}


Artık ExceptionHandler sınıfımız hazır fakat işimiz henüz bitmedi. Loglama için Logging Applicataion Block üzerine Critical Logging Provider sınıfları eklemeliyiz. İlk olarak Hataları formatlamak için CriticalLogFormater sınıfını ABSF kullanarak diğer provider sınıflarına benzer şekilde (typed) oluşturalım. CriticalLogFormater kendisine formatlanmak üzere gelen LogEntry nesnesini ErrorDS.LogEntry kayıtına daha sonrada xml şeklinde çevirir. Eğer gelen LogEntry ErrorDS modeline ait bir xml ise aynı model üzerine eklenir.



/// <summary>
/// Formats a log entry as a serialized representation.
/// </summary>
/// <param name="log">The <see cref="Microsoft.Practices.EnterpriseLibrary.Logging.LogEntry"/> to format.</param>
/// <returns>A string version of the <see cref="Microsoft.Practices.EnterpriseLibrary.Logging.LogEntry"/> that can be deserialized back to a <see cref="Microsoft.Practices.EnterpriseLibrary.Logging.LogEntry"/> instance.</returns>
public override string Format(LogEntry logEntry) {
string xmlLogEntry = "";
try {
_errorDS = new ErrorDS();
bool isErrorMessage = TryLoadMessage(logEntry);
// yeni bir kayıt ac errorDS üzerinde
ErrorDS.LogEntryRow row = _errorDS.LogEntry.NewLogEntryRow();
row.id = Guid.NewGuid();
WriteSiteCode(row, logEntry);
WriteIssueTag(row, logEntry);
row.activityId = logEntry.ActivityId;
row.appDomainName = logEntry.AppDomainName;
row.eventId = logEntry.EventId;
row.loggedSeverity = logEntry.LoggedSeverity;
row.machineName = logEntry.MachineName;
row.managedThreadName = logEntry.ManagedThreadName;
row.priority = logEntry.Priority;
row.processId = Convert.ToInt32(logEntry.ProcessId);
row.processName = logEntry.ProcessName;
row.severity = logEntry.Severity.ToString();
row.timeStamp = logEntry.TimeStamp;
row.title = logEntry.Title;
row.win32ThreadId = Convert.ToInt32(logEntry.Win32ThreadId);
row.categoriesId = Guid.NewGuid();
WriteCategories(logEntry.CategoriesStrings, row.categoriesId);
row.errorMessages = logEntry.ErrorMessages;
if(isErrorMessage)
WriteExceptionId(row);
else
row.errorMessages = logEntry.Message;
row.extendedPropertiesId = Guid.NewGuid();
WriteExtendedProps(logEntry.ExtendedProperties, row.extendedPropertiesId);
_errorDS.LogEntry.Rows.Add(row);
using(MemoryStream memStream = new MemoryStream()) {
_errorDS.WriteXml(memStream);
xmlLogEntry = Encoding.UTF8.GetString(memStream.ToArray());
}
} catch(Exception e) {
Debug.WriteLine("LogEnty Formatlama Hatası: " + e);
throw e;
}
return xmlLogEntry;
}
private bool TryLoadMessage(LogEntry logEntry) {
try {
using(StringReader exceptionStream = new StringReader(logEntry.Message)) {
XmlReadMode mode = _errorDS.ReadXml(exceptionStream);
}
} catch(Exception) {
return false;
}
return true;
}


Şimdiye kadar olan adımlar ile oluşan hatayı detaylı olarak ErrorDS modeli ile xml formatlı olarak oluşturduk. Artık oluşan hataları yakalayacak ve web servise iletecek bir dinleyiciye ihtiyacımız var. En önemli sınıf gelen loglama isteklerini web servise iletecek olan CriticalLogTraceListener sınıfıdır.



CriticalLogTraceListener kendisine gelen LogEntry’leri önce formatlar daha sonra mesajını şifreler ve hataların veri tabanına kayıt edileceği web servisine gönderir. İlk defa burada bir yapılandırma ihtiyacımız oluştu. Konfigürasyon ile programcı loglamanın yapılacağı URL ve local loglamanın yapılacağı path’leri belirtmelidir.



public class CriticalLogTraceListener :FormattedTraceListenerBase {
…..
public CriticalLogTraceListener(CriticalLogTraceListenerData configuration) {
_localEntry = configuration.LocalEntryDirectory;
_url = configuration.ReportingURL;
}
public override void TraceData(TraceEventCache eventCache, string source, TraceEventType eventType, int id, object data) {
if(data is LogEntry && this.Formatter != null) {
if(this.Formatter is CriticalLogFormatter) {
FormatAndLog(data as LogEntry);
} else {
throw new
LoggingException("CriticalLogTraceListener formatter olarak CriticalLogFormatter kullanmalıdır.");
}
}
}
private void FormatAndLog(LogEntry logEntry) {
string entryMessage = this.Formatter.Format(logEntry);
// localede kayıt et
WriteLocalEntry(entryMessage);
// acık bir servis olduğu için datayı şifrele
byte[] entryBytes = Encoding.Unicode.GetBytes(entryMessage);
byte[] encryptedEntryBytes =
Cryptographer.EncryptSymmetric("RijndaelManaged", entryBytes);
string encodedEncryptedEntryMessage =
Convert.ToBase64String(encryptedEntryBytes);
byte[] encodedEncryptedEntryBytes =
Encoding.UTF8.GetBytes(encodedEncryptedEntryMessage);
try {
CriticalErrorServiceProxy.Service reportService =
new CriticalErrorServiceProxy.Service(_url);
reportService.Credentials = CredentialCache.DefaultCredentials;
reportService.ReportError(encodedEncryptedEntryBytes);
} catch(Exception ex) {
throw ex;
}
}


CriticalLogTraceListener önce veri şifreler daha sonra şifreli veriyi web servisine iletir. Hataların web servis üzerinde ki kayıt işlemi oldukça sadedir. Önce CriticalLogTraceListener tarafından şifrelenen bilgi aynı key dosyası ile çözümlenir. Daha sonra Data Access Application Block ile veri tabanı üzerine kayıt edilir.



[WebMethod]
public void ReportError(byte[] message) {
if(message.Length == 0)
return;
string base64EncryptedEntryMessage = Encoding.UTF8.GetString(message);
byte[] encryptedEntryBytes =
Convert.FromBase64String(base64EncryptedEntryMessage);
byte[] entryBytes =
Cryptographer.DecryptSymmetric("RijndaelManaged",
encryptedEntryBytes);
string entryMessage = Encoding.Unicode.GetString(entryBytes);
Debug.WriteLine(entryMessage);
Database db = DatabaseFactory.CreateDatabase("ErrorConnectionString");
ErrorDS errorDS = new ErrorDS();
errorDS.ProcessError(entryMessage, db);
}


Evet şu anda tüm gereksinimlerimizi tamamladık fakat hala kendi uygulama projemiz içerisinde yazdığımız yeni provider sınıfları kolayca kullanabileceğimiz bir yol oluşturmadık. Artık Desing projesini kullanabiliriz. Design projesi config dosyalar üzerinde ki node yapılarını sağlayan projedir. Öncelikle yeni bir Exception Provider Node oluşturalım.





Node Name: Node sınıfına vereceğiniz isindir. CriticalExceptionHandler provider sınıfı için ben CriticalExceptionHandlerNode ismini seçtim.

Run Time Configuration Type: Node ile temsil edilecek Provider sınıfının verisini taşıyan veri sınıfıdır. Bizim örneğimiz için veri sınıfmız CriticalExceptionHandlerData’dır.

Base Design Node: Oluşturduğumuz node sınıfının hangi sınıftan türetilmesi gerektiğini göstermektedir. CriticalExceptionHandlerNode için doğru temel sınıf ExceptionHandlerNode sınıfıdr.

Parent UI Node: Oluşturduğumuz yeni node konfigürasyon içerisnde hangi node altında olması gerektiğini göstermektedir. CriticalExceptionHandlerNode Exception Type Node altında yer almalıdır.

Cardinality: Node parent node altında bir defa mı birden cok defa mı yer alabileceğini göstermektedir. CriticalExceptionHandlerNode aynı Exception Type altında bir defa olmalıdır.



Böylelikle ExceptionHandler için yeni bir ExceptionHandlerNode oluşturduk. Oluşturduğumuz CriticalExceptionHandlerNode ExceptionNode sınıfından türetilmekte ve konfigürasyon içerisinden Exception Type Node altına sadece bir defa eklenebilemektedir. Önemli bir konuda design projesi içerisinde ki Resource bilgilerini doldurmanız gerekmektedir.

Aynı adımı CriticalLogNode ve CriticalLogTraceListener için tekrarlayalım. Şu anda projemiz içerisinde kullanmak için her şey hazır. Yazdığımız bu iki dll’i Enterprise Library kurum dizinine kopyalayalım. Artık istediğimiz proje içersinizde CriticalException provider ve diğer provider nesnelerimizi kullanabiliriz.



Tüm yapılandırmalardan sonra config dosyanızın görüntüsü muhtemelen şu şekilde oluşacaktır.



Böylelikle Enterprise Library için yeni provider’lar nasıl yazılırdığını ve kullanıldığını öğrendik. Enterprise Library bizim için gerekli genel uygulama blokları oluşturmuştur. Biz kendi iş ihtiyaçlarımıza göre nasıl yeni bloklar oluşturabiliriz? Bir sonra ki makale ile yeni uygulama blokları ABSF ile nasıl oluşturulduğunu inceliyeceğiz.

Şimdi tüm bu yaptıklarımızı denemek için örnek bir uygulama oluşturalım.



Çarşamba, Ocak 09, 2008

SCSF8 VS ile Kullanım

Bu seri ile birlikte sıfırdan başlayıp tüm CAB elemanlarını kullanmayı öğrendik. Şimdide Visual Studio içerisinde SCSF nasıl kullanılır olduğunu öğreneceğiz.

Burada ki tüm kurulum adımlarını izlediniz ve SCSF bilgisayarınıza yüklediniz. Artık yeni bir SCSF uygulaması yazmaya hazırsınız. Bu seri ile incelediğimiz Easy TaskBar uygulamasını birde SCSF kullanarak tekrar yazalım.



Demo



Öncelikle Yeni bir SCSF uygulaması başlatmalıyız. Uygulamayı ilk oluşturmak istediğinizde karşınıza Root Namespace ve dll kütüphanesinin yerini soran bir ekran gelecektir.



Sağ tarafta SCSF oluşturacağı dosyaları göstermektedir. Tüm uygulamalar için ortak olan kod bloğu Infrastructure içerisine alınmıştır. Daha önce ki makalelerden hatırladığınız Shell Liblary Layout ve Interface projeleri Infrastructure içerisindedir. Finish’i tıkladığınızda SCSF sizin için bu beş projeyi oluşturacak ve gerekli kodları ekleyecektir. Oluşan kodları çalıştırdığımızda aşağıda ki gibi bir ekran görüntüsü çıkacaktır.



Infrastructure.Library projesini inceleyelim. Burada uygulamamız için önceden tanımlanmış birçok hazır servisin olduğunu görmekteyiz.



Burada ki servisler tüm uygulama için geçerli genel servislerdir. Sizin SCSF işleyişine müdahale etmek istediğiniz zaman kullanacağınız servislerdir.
Infrastructure.Interface içerisinde Constants dizini içerisinde CommandNames, EventTopicNames, UIExtasionNames, WorkspaceNames sınıfları bulunmaktadır. Bu sınıflar adından da anlaşılacağı üzerinde uygulama genelinde kullanılacak isimlendirmelerdir.
Şimdi Easy TaskBar uygulamamızı geliştirmeye başlayalım. Önce ProcessService sınıfını Infrastructure.Library’e IProcessService interface’ini Infrastructure.Interface’e ekleyelim. Daha sonrada WinFormAuthenticationService’i ekleyelim. Servis interfaceleri Infrastructure.Interface projesinde Service dizini altına eklenir. Servis tanımları da Infrastructure.Library projesinde Service dizini altına eklenir. Tüm servisleri daha sonra RootWorkItem üzerine ekleyelim.



namespace SmartClientDevelopmentSolution1.Infrastructure.Library {
public abstract class SmartClientApplication<TWorkItem, TShell> : WPFFormShellApplication<TWorkItem, TShell>
where TWorkItem : WorkItem, new()
where TShell : Form {
protected override void AddServices() {
base.AddServices();
// scsf tarafında gelen servisler
RootWorkItem.Services.AddNew<ProfileCatalogModuleInfoStore, IModuleInfoStore>();
RootWorkItem.Services.AddNew<WorkspaceLocatorService, IWorkspaceLocatorService>();
RootWorkItem.Services.Remove<IModuleEnumerator>();
RootWorkItem.Services.Remove<IModuleLoaderService>();
RootWorkItem.Services.AddNew<XmlStreamDependentModuleEnumerator, IModuleEnumerator>();
RootWorkItem.Services.AddNew<DependentModuleLoaderService, IModuleLoaderService>();
RootWorkItem.Services.AddOnDemand<ActionCatalogService, IActionCatalogService>();
RootWorkItem.Services.AddOnDemand<EntityTranslatorService, IEntityTranslatorService>();

// uygulama genelinde çalışacak kendi servislerimiz
IActionCatalogService actionCatalog = RootWorkItem.Services.Get<IActionCatalogService>();
actionCatalog.RegisterGeneralCondition(new SimpleActionCondition());
RootWorkItem.Services.Remove<IAuthenticationService>();
RootWorkItem.Services.AddNew<WinFormAuthenticationService, IAuthenticationService>();
RootWorkItem.Services.AddOnDemand<ProcessService, IProcessService>();
}
}…….
}


Şimdi de modüllerimizi oluşturalım.Ben modülleri SCSF alt yapısından ayırmak için yeni bir ‘Module’ dizisinin altına ekliyorum. Module dizinin üzerine sağ tıkladığınız zaman açılan menüde Smart Client Factory menüsünü göreceksiniz. Burada Business Module ve Foundational Module ekleme opsiyonları görüyorsunuz.



Business Module iş kodlarımızı yazdığımız içerisinde Servisler View’lar barındıran modüldür. Foundational Module Business Module’den farklı olarak uygulama genelinde kullanılan Infrastructure.Layout içerisinde ki ShellLayoutView Layout’u yerine bu modül için kullanılacak özel bir layout ile birlikte gelir. Biz yolumuza yeni bir Business Module oluşturarak devam edeceğiz. Menüden Business Modüle seçtikten sonra aşağıda ki ekran ile karşılaşıyoruz.




Burada OK tıklayıp devam ettiğimizde aşağıda ki ekran ile karşılaşıyoruz.



Burada oluşturduğumuz modül için Interface projesi, test projesi ve projeleri oluşturduktan sonra yardımcı bir doküman isteyip istemediğimizi sormaktadır. Eğer modül içerisinde ki her hangi bir bileşen tanımına diğer servislerden ulaşılacaksa Interface projesi eklememiz gerekmektedir. Eğer test projesi eklersek modülümüze eklediğimiz her View için test kodları otomatik olarak eklenecektir. OK tıklayıp modülümüzü oluşturalım. Sıra geldi tüm process’lerin listeleneceği NavigatorView’ını yazmaya.



WPF form veya normal Windows formdan View oluşturabilirsiniz. Biz normal View oluşturmayı seçelim. Bir sonraki adımda aşağıda ki ekran ile karşılaşacağız.



Burada oluşturduğumuz View’a ismini verelim. Genelde her View’ın kendi dizininde olması daha iyidir. Finish’i tıklayalım ve SCSF bizim için MVP pattern ile oluşturulmuş View ve Presenter sınıflarımızı oluştursun. Arayüz işlemlerini tamamladıktan sonra NavigatorViewPresenter üzerine bir event ekleyelim.





Bu adım ile birlikte NeedChanfePrecess event’ını presenter içine eklemiş olduk. Şimdi oluşturduğumuz navigator view’ı ilk çalışmada LeftWorkspace üzerinde açalım.



namespace SmartClientDevelopmentSolution1.NavigatorModule {
public class ModuleController : WorkItemController {
public override void Run() {
AddServices();
ExtendMenu();
ExtendToolStrip();
AddViews();
}

private void AddServices() {}

private void ExtendMenu() {
ToolStripMenuItem miRefresh = new ToolStripMenuItem();
miRefresh.Name = "miRefresh";
miRefresh.Text = "Yenile";
this.WorkItem.Commands[CommandNames.Refresh].AddInvoker(miRefresh, "Click");
this.WorkItem.UIExtensionSites[UIExtensionSiteNames.MainMenu].Add(miRefresh);
}

private void ExtendToolStrip() {
ToolStripButton btRefresh = new ToolStripButton();
btRefresh.Name = "btRefresh";
btRefresh.Text = "Yenile";
this.WorkItem.UIExtensionSites[UIExtensionSiteNames.MainToolbar].Add(btRefresh);
this.WorkItem.Commands[CommandNames.Refresh].AddInvoker(btRefresh, "Click");
}

private void AddViews() {
NavigatorView navigatorView = ShowViewInWorkspace<NavigatorView>(WorkspaceNames.LeftWorkspace);
}
}
}


Yukarıda gördüğünüz gibi ModuleController üzerinde modlülün ilk çalışmasında uygulamaya eklenecek Service, Menü, ToolStrip ve View’lar için ayrı ayrı fonksiyonlar açılmıştır. View’ı eklerken Yenile menülerimizi de ekliyoruz. Daha önce ki bölümlerde Eventlar’ın ve Command’ları ne olduğu ve nasıl çalıştığını incelemiştik. Burada tanımlamalarına yaptıktan sonra gerekli kodları NavigatorViewPresenter içine yazıyoruz. Şimdi uyglamamızı çalıştıralım.



Aynı adımları izleyerek Context modülü ve Contextview ‘ı oluşturalım ve uygulamamızı tamamlayalım.



Burada biz SCSF Visual Studio içerisinde nasıl kullanır olduğunu gördük. SCSF ile gelen MVP, Event Broker, Command, Action gibi kavramları anlamak için önce ki makalelere bakmanız gerekmektedir.
Son bir not SCSF şu anda VS 2008 desteklememektedir. VS 2008 SCSF şubat 2008 içerisinde yayınlanacağı duyuruldu. Böylelikle 8 makalelik SCSF serisinin sonuna geldik bir sonra ki makale serisi Application Block Software Factory konusudur.

SCSF7 Action

Gelişmiş iş uygulamalarının en büyük ihtiyaçı kuşkusuz yetkilendirmedir. Fonksiyon bazlı yetkilendirme yapabilir misiniz?



Action’lar iş kodlarımızın yazıldığı yerlerdir.Action’lar ’Action’ attribute ile işaretlediğimiz ActionDelegate ile temsil edilebilen fonksiyonlardır. Bir workItem üzerinde ki Action’lar ActionCatalog tarafından saklanır ve yönetilirler. ActionCatalog üzerinde ActionCondition nesneleride mevcuttur. Çalıştırmak istediğiniz Action’ı ActionCatalogService söylersiniz. ActionCatalogService çalıştırılacak Action için geçerli Condition’ların hepsini denetler eğer tüm sınamalardan geçerse Action çalıştırılır.

Demo





namespace Interface.Service {
public delegate void ActionDelegate(object caller, object target);

public interface IActionCatalogService {
bool CanExecute(string action, WorkItem context, object caller, object target);
bool CanExecute(string action);
void Execute(string action, WorkItem context, object caller, object target);
void RegisterSpecificCondition(string action, IActionCondition actionCondition);
void RegisterGeneralCondition(IActionCondition actionCondition);
void RemoveSpecificCondition(string action, IActionCondition actionCondition);
void RemoveGeneralCondition(IActionCondition actionCondition);

void RemoveActionImplementation(string action);
void RegisterActionImplementation(string action, ActionDelegate actionDelegate);
}
}


Oluşan tüm Action’ları yakalamak için bir BuilderStrategy sınıfına olan ActionStrategy sınıfını yazılmalıdır. Injection Pattern’den hatırlayınız object builder’a ekleyeceğiniz Strategy nesneleri ile oluşturulan tüm nesnelere oluşturulma anında eriş ve oluşan nesneleri denetleyebilirsiniz. ActionStrategy oluşan tüm nesnelerde ActionDelegate ile temsil edilebilecek Action attribute’ne sahip fonksiyonları arar ve bulduğu fonksiyonları ActionCatalogServis üzerine ekler.



public class ActionStrategy : BuilderStrategy {
…………………
public override object BuildUp(IBuilderContext context, Type typeToBuild, object existing, string idToBuild) {
WorkItem workItem = GetWorkItem(context, existing);
if (workItem != null) {
IActionCatalogService actionCatalog = workItem.Services.Get<IActionCatalogService>();
if (actionCatalog != null) {
Type targetType = existing.GetType();
foreach (MethodInfo methodInfo in targetType.GetMethods())
RegisterActionImplementation(context, actionCatalog, existing, idToBuild, methodInfo);
}
}
return base.BuildUp(context, typeToBuild, existing, idToBuild);
}
…………
}


Böylelikle tüm Action’lar ActionCatalogService üzerine eklenmiş olamaktadır. ActionStrategy’i object builder’ın builder strategy’lerine ekleyelim.



public abstract class SmartClientApplication<TWorkItem, TShell> : FormShellApplication<WorkItem, TShell>
{
protected override void AddServices() {
base.AddServices();
…….
RootWorkItem.Services.AddOnDemand<ActionCatalogService, IActionCatalogService>();
IActionCatalogService actionCatalog = RootWorkItem.Services.Get<IActionCatalogService>();
actionCatalog.RegisterGeneralCondition(new SimpleActionCondition());
}
protected override void AddBuilderStrategies(Microsoft.Practices.ObjectBuilder.Builder builder) {
base.AddBuilderStrategies(builder);
builder.Strategies.AddNew<ActionStrategy>(
BuilderStage.Initialization);
}
}


Yukarıda AddService içinde önce ActionCatalogService oluşturuldu. Daha Sonra SimpleActionCondition genel Action Condition olarak register ettik. Daha sonra AddBuilderStrategies içinde object builder’a yeni ActionStrategy’i ekledik.

Şimdi SimpleActionCondion oluşturalım.



public class SimpleActionCondition : IActionCondition {
#region IActionCondition Members

public bool CanExecute(string action, Microsoft.Practices.CompositeUI.WorkItem context, object caller, object target) {
return Thread.CurrentPrincipal.IsInRole("ADMIN");
}
#endregion
}


Burada ‘ADMIN’ rolüne sahip ise Action çalıştırılacaktır. Tüm bu adımlar SCSF tarafında oluşturduğunuz projede olacaktır. Biz SCSF ile oluşturduğumuz projede sadece Action fonksiyonlarımızı oluşturacağız.

Authentication



Örnek uygulamamıza geri dönelim. Önce kullanıcıları Authenticate etmeliyiz. Daha sonra ADMIN rolune sahip kullanıcıların process detaylarını görmelerini sağlayacak kodları yazmalıyız.

Önce kullanıcıya yetki verebilmek için kullanıcının uygulamaya login olmasınısı sağlamalıyız. CAB varsayılan olarak bir AuthenticationService bulundurur. Bu servis kullanıcının bilgilerini Aktive Directory üzerinden alır ve ona göre yetkilendirme uygular. Fakat biz kendi yetkilendirme kurallarımızı uygulayacağımıza göre kendi Authentication servisimizi yazmalıyız.




namespace Microsoft.Practices.CompositeUI.Services {
public interface IAuthenticationService {
void Authenticate();
}
}

public class WinFormAuthenticationService : IAuthenticationService {
…..
[EventPublication("UserAuthorize", PublicationScope.Global)]
public event System.EventHandler<EventArgs> UserAuthorizeHandler;

#region IAuthenticationService Members
public void Authenticate() {
// login formu çıkart ve kullanıcı bilgilerini doğrula
UserData user = SelectUser();
if (user != null) {
GenericIdentity identity = new GenericIdentity(user.UserName);
GenericPrincipal principal = new GenericPrincipal(identity, new string[] { user.Rol });
Thread.CurrentPrincipal = principal;
UserAuthorize();
} else {
throw new AuthenticationException();
}
}

private UserData SelectUser() {……}

private void UserAuthorize() {
if (UserAuthorizeHandler != null)
UserAuthorizeHandler(this, new EventArgs());
}
#endregion
}
}


Evet yazdığımız Authentication servisi mevcut servisin yerine eklemeliyiz şimdide.



public abstract class SmartClientApplication<TWorkItem, TShell> : FormShellApplication<WorkItem, TShell> {
………
protected override void AddServices() {
base.AddServices();
RootWorkItem.Services.Remove<IAuthenticationService>();
RootWorkItem.Services.AddNew<WinFormAuthenticationService, IAuthenticationService>();

…..
}
}


Artık uygulama ilk açıldığında Login fomumuz açılacaktır.



Artık tüm alt yapımız hazır. Tüm Action’ların üzerine kayıt edildiği ActionCatalogServisimiz bu servisi register edilmiş ADMIN kullanıcılara çalıştırma izini veren SimpleActionCondition sınıfımız ve uygulamayı kullanan kullanıcının bilgilerini doğrulaya kendi WinFormAuthentication sınıfımız var. Artık ADMIN kullanıcılar için process detaylarını göstermeye izin veren Action fonksiyonumuzu yazabiliriz.



public class ContextViewPresenter {
…….

[EventSubscription("NeedChangeProcess", ThreadOption.UserInterface)]
public void OnNeedChangeProcess(object sender, EventArgs<List<ProcessDetailInfo>> eventArgs) {
_actionCatalogService.Execute("ChangeProcessAction", this.WorkItem, this, eventArgs.Data);
}

[Action("ChangeProcessAction")]
public void OnChangeProcess(object caller, object target) {
Guard.TypeIsAssignableFromType(target.GetType(), typeof(List<ProcessDetailInfo>), "ProcessDetailInfo");
List<ProcessDetailInfo> info = (List<ProcessDetailInfo>)target;
View.DataSource = info;
}
…….
}


OnNeedChangeProcess fonksiyonu içerisinde “ChangeProcessAction” fonksiyonun çalıştırılması gerektiğini ActionCatalogService bildirdik. ActionCatalogServis gerekli tüm condition’ları denetler ve eğer kullanıcının bu Action’ı kullanmaya yetkisi var ise fonksiyonu çağıaracaktır. OnChageProcess Action’ı içerisinde gelen bilgileri ekranda göstermekten başka bir şey yapmadık.


Böylelikle çok uzun bir serinin sondan bir önce ki adımını tamamladık. Actionlar ile birlikte Smart Client Software Factory tarafından kullanılan Composite Application Block elemanlarının hepsini incelemiş olduk. Bir sonra ki makalede Visial Studio içerisinden SCSF nasıl kullanılır inceliyeceğiz.





SCSF6 UIElement - Command Pattern

Son uygulamamızda birden çok view’ın bir biri ile nasıl haberleştiğini inceledik. Şimdi uygulamaların ortak menu toolbar gibi elemanları nasıl kullandığını inceleyelim.



ShellLayoutView üzerinde View’larımızı taşıyan WorkSpace’lerimiz ve tüm uygulamanın ortak kullanımında olan UIExtansionSite nesnelerimiz mevcuttur. IUExtansionSite nesneleri genelte MenuBar ToolBar, StatusBar gibi elemanlardır. CAB MenuBar, ToolBar ve StatusBar için hazır register sınıflarını sunmaktadır. Kendi kontrollerinizi CommandAdapter sınıfı yazarak CAB uygulamasına IUExtansionSite olarak ekleyebilirsiniz.

Demo



Örnek uygulamamız üzerinden devam edelim. Uygulamamıza bir menü elemanı ve birde toolbar elemanı ekleyelim. Her iki menü elemanıda Click olaylarında process listelesi güncellesin.

Öncelikle ortak kullanılacak olan MenuItem ToolbarItem ve StatusBar nesnelerini RootWorkItem üzerine register edelim.



public class ShellLayoutViewPresenter {
……………………
public void OnViewSet() {
// layout module ait work item nesnesine UI elemanlarını register et
_workItem.UIExtensionSites.RegisterSite("MainMenu", View.MainMenuStrip);
_workItem.UIExtensionSites.RegisterSite("MainStatus",View.MainStatusStrip);
_workItem.UIExtensionSites.RegisterSite("MainToolbar",View.MainToolbarStrip);
}
…………
}


Artık her hangi bir modül bu üç ortak arayüz elemanına erişip kullanabilir.



public class MaviModuleInit :ModuleInit {
private WorkItem _workItem;
[InjectionConstructor]
public MaviModuleInit([ServiceDependency] WorkItem workItem) {
_workItem = workItem;
}
public override void Load() {
base.Load();
NavigatorView kirmiziView = _workItem.SmartParts.AddNew<NavigatorView>();
_workItem.Workspaces["NavigatorWorkspace"].Show(kirmiziView);

InitMenu();
InitToolstrip();
}

private void InitToolstrip() {
ToolStripButton btRefresh = new ToolStripButton();
btRefresh.Name = "btRefresh";
btRefresh.Text = "Yenile";

_workItem.UIExtensionSites["MainToolbar"].Add(btRefresh);
_workItem.Commands["Refresh"].AddInvoker(btRefresh, "Click");
}

private void InitMenu() {
ToolStripMenuItem miRefresh = new ToolStripMenuItem();
miRefresh.Name = "miRefresh";
miRefresh.Text = "Yenile";
_workItem.Commands["Refresh"].AddInvoker(miRefresh, "Click");
_workItem.UIExtensionSites["MainMenu"].Add(miRefresh);
}
}




Command Pattern



Çalışma anında ekran görüldüğü gibi toolbar ve menü elemanımız eklenmiştir. Artık yenile menü elemanlarının ‘Click’ olaylarında ‘Refresh’ command’ı çalışacaktır. Event’lar kod ile programcının tetiklediği mekanizmalardır. Command’lar ile kullanıcının ara yüz elemanları ile tetiklediği mekanizmalardır. Command’lar bir invoker tarafındana tekillenirler ve inkover’ler her hangi bir event’a bağlanabilirler.

_workItem.Commands["Refresh"].AddInvoker(miRefresh, "Click");

Biz burada Refresh Command’ını miRefresh nesnesinin Click olayına bağlamış olduk. Command genel bir olay duyurusunu beklememekte Command özel bir olayı beklemektedir. Genelde bu özel olay kullanıcı arayüz işlemleri ile tetiklenen olaylardır.

Biz örnek uygulamamızda kullanıcı ‘Yenile’ menülerinden birine bastığı zaman process listesini güncellemeliyiz.


public class NavigatorViewPresenter {
…….
[CommandHandler("Refresh")]
public void OnRefresh(object sender, EventArgs e) {
List<ProcessInfo> data = _processService.EnumProcesses();
View.DataSource = data;
}
………………..
}


Evet hepsi bu kadar! Gelişmiş uygulamar için gerekli tüm aletlere sahibiz.
Dinamik olarak modülleri view’ları yükleyebiliyoruz, iş kodlarımızı view’lardan ayırabiliyoruz, oluşan olayları yayınlaya biliyor veya olaylara üye olabiliyoruz, ortak ara yüz elemanlarını yönetebiliyoruz, özel durumlara Command Pattern ile cevap verebiliyoruz. Bir sonra ki uygulamada Action’lar ile fonksiyon bazlı yetkilendirmeyi inceeyeceğiz.

SCSF5 Event Broker

Bir önce ki uygulamamızda View-Presenter arasında ki iletişimi incelemiştir. Şimdi viewlar arasında ki iletişimi inceleyeceğiz. View’lar birbiri ile nasıl haberleşir? Uygulama genelinde oluşan bir olaya nasıl tepki verilir?



Uygulama içerisinde oluşan her hangi bir aktiviteyi duyurmak için bir Event publish edersiniz. Oluşan aktiviteden haber olmak içinse event’a subscript olursunuz.
Demo




Yukarıda son şeklini gördüğümüz örnek uygulamamız üzerinden devam edelim. Kullanıcımız Process listesi üzerinden bir process detaylarını görmek için tıkladığında sağ tarafda ki pencere üzerinde process’e ait detay bilgilerini gösterelim.

Önce liste üzerinde kullanıcın tıklamasını yakalayalım.



[SmartPart]
public partial class NavigatorView : UserControl, INavigatorViewController
{
………………………
private void bindingSource1_CurrentChanged(object sender, EventArgs e) {
_presenter.ChangeProcess();
}
}
}


View içerisinde logic kod olmayacağı için View bu işi Presenter’a havale etmektedir. Presenter ProcessService’den seçili olan processe ait detayları alacak ve daha sonra bu olayı yayınlayacak.



public class NavigatorViewPresenter {
………
internal void ChangeProcess() {
List<ProcessDetailInfo> processDetail = _processService.EnumProcess(View.Current);
OnNeedChangeProcess(processDetail);
}

[EventPublication("NeedChangeProcess", PublicationScope.Global)]
public event System.EventHandler<EventArgs<List<ProcessDetailInfo>>> NeedChangeProcess;
protected virtual void OnNeedChangeProcess(List<ProcessDetailInfo> data) {
if (NeedChangeProcess != null) {
NeedChangeProcess(this, new EventArgs<List<ProcessDetailInfo>>(data));
}
}
}
}


Oluşan uygulama olayı böylelikle tüm uygulamaya duyuruldu. Eğer her hangi bir WorkItem içerisinde oluşturulan nesnelerden her hangi birinde bu olayı bekleyen bir sınıf var ise bu şekilde haberdar edilmiş olmaktadır.

NeedChangeProcess olayı mavi modül içerisinde ki NavigatorViewPresente sınıfı tarafında tüm uygulamaya duyuruldu. Kırmızı modül içerisinde ki ContextViewPresenter sınıfı bu olayı beklemektedir. Oluşan NeedChangeProcess olayı ile ContextView üzerinde process detayları gösterilmektedir.



namespace KirmiziModul.View.ContextView {
public class ContextViewPresenter {

…..
[EventSubscription("NeedChangeProcess", ThreadOption.UserInterface)]
public void OnNeedChangeProcess(object sender, EventArgs<List<ProcessDetailInfo>> eventArgs) {
View.DataSource = eventArgs.Data;
}
}
}

[SmartPart]
public partial class ContextView : UserControl, IContextViewController
{
public List<ProcessDetailInfo> DataSource {
set {
bindingSource1.DataSource = value;
}
}
}
}


Evet hepsi bu kadar! Artık uygulama genelinde oluşan olaylara uygulamanın nasıl tepki verdiğini görmüş olduk.



SCSF4 Model-View-Presenter

Uygulamalar dinamik olarak yüklenen SmartPart ara yüzlerinden ve bu SmartPart ara yüzlerine taşıyıcılık yapan Workspace nesnelerinden ibaret değildir. Uygulamanın iş kuralları ile ara yüz işlemleri açık kodlama ve anlaşılabilirlik için tamamen bir birinden ayrılmalıdır. Her bir SmartPart ara yüzü sadece ara yüz işlemlerini içermelidir.



Her bir View sadece ara yüz işlemleri gerçekleştirmektedir. Presenter uygulanın iş kodlarının bulunduğu yerdir. View ile Presenter ortak bir Interface aracılığı ile haberleşir. Model ise uygulamanın kabul ettiği veri yapısıdır. Model sınıflar uygulamanın ‘Business Entity’ sınıflarıdır.

Demo





[SmartPart]
public partial class NavigatorView : UserControl, INavigatorView
{
………
private NavigatorViewPresenter _presenter;
[CreateNew]
public NavigatorViewPresenter Presenter {
set {
_presenter = value;
_presenter.View = this;
_presenter.OnViewReady();
}
}
……
}
}


Örnek bir View ait kodları görmektesiniz. View oluşturulurken ObjectBuilder’a kendisine ait Presenter sınıfını oluşturmasını söylemektedir. Oluşturulan Presenter’e View olarak kendisini atamaktadır.



public class NavigatorViewPresenter {
private INavigatorView _view;
public INavigatorView View {
get {
return _view;
}
set {
_view = value;
OnViewSet();
}
}
public void OnViewSet() {
}

public void OnViewReady() {………………..
}
}
}

SOA


Şimdi örnek uygulamamıza taskbar uygulamasına benzetmeye çalışalım ve MVP pattern iş başında inceleyelim. Öncelikle bize bilgisayar üzerinde ki tüm processleri ve processlere ait detayları verecek bir iş servisine ihtiyacımız var. Daha sonra mavi modül içerisinde tüm processleri listeyen bir ara yüze ve kırmızı modül içerisinde process detaylarını gösteren bir başka ara yüze ihtiyacımız var.

Process bilgilerini sağlayan ProcessService’i yazalım:



using System;
using System.Collections.Generic;
namespace Interface.Service {
public interface IProcessService {
// seçilen processe ait detay bilgilerini getir
List<ProcessDetailInfo> EnumProcess(Interface.Service.ProcessInfo info);
// tüm process bilgilerini getir
List<Interface.Service.ProcessInfo> EnumProcesses();
}
}


Evet uygulamamızın Model kısmını oluşturmuş olduk. ProcessInfo ve ProcessDetailInfo sınıfları bizim uygulamamız için Model sınıflarıdır.

Projemize iki yeni dll daha eklemeliyiz. Birincisi tüm uygulama genelinde gerekli servislerin uygulandığı Library.dll ikincisi Library.dll içinde ki servislere ait arayüzlerin ve uygulamamızın ‘Model’ kısmını oluşturan sınıfların bulunduğu Interface.dll dir.

Kırmızı ve Mavi modül ProcessService’in sadece tanımına yani Interface bilgilerine ihtiyaç duymaktadır.



Uygulama başlarken ProcessService RootWorkItem üzerine ekleyelim ki ProcessService tüm WorkItem nesneleri tarafından kullanılabilir olsun.



namespace Library {
public abstract class SmartClientApplication<TWorkItem, TShell> : FormShellApplication<WorkItem, TShell>
where TWorkItem : WorkItem, new()
where TShell : Form{

protected override void AddServices() {
base.AddServices();
// Uygulama açılırken ProcessService ilk çağrıldığı yerde
// create olacak şekilde register et
RootWorkItem.Services.AddOnDemand<ProcessService, IProcessService>();
}
}
}


Artık istediğimiz yerde ProcessService’e injection ile erişim sağlayabiliriz.



public class NavigatorViewPresenter {
IProcessService _processService;

[ServiceDependency(Type = typeof(IProcessService))]
public IProcessService ProcessService {
set {
_processService = value;
}
}

private INavigatorView _view;
public INavigatorView View {
get {
return _view;
}
set {
_view = value;
OnViewSet();
}
}

public void OnViewSet() { }

public void OnViewReady() {
List<ProcessInfo> data = _processService.EnumProcesses();
View.DataSource = data;
}
}

public interface INavigatorViewController {
List<ProcessInfo> DataSource {
set;
}
}


Process listesini gösterecek olan Mavi modüle ait navigator view’dır. Bu view’a ait olan presenter içerisinde ProcessService’i yukarıda ki gibi aldık. Bilgisayarımıza ait process listesini bu servis ile oluşturduk. Daha sonra oluşan process listesini view’a ekranda göstermek üzere gönderdik. View üzerinde ki kodda oldukça basit gelen listeyi bağlamaktan başka bir görevi yok.



[SmartPart]
public partial class NavigatorView : UserControl, INavigatorViewController
{
public NavigatorView()
{
InitializeComponent();
}
private NavigatorViewPresenter _presenter;
[CreateNew]
public NavigatorViewPresenter Presenter {
set {
_presenter = value;
_presenter.View = this;
_presenter.OnViewReady();
}
}

public List<Interface.Service.ProcessInfo> DataSource {
set {
bindingSource1.DataSource = value;
}
}
}
}


Böylelikle bir demonun daha sonuna geldik. Uygulamamız artık bilgisayar üzerinde ki tüm precessleri listelemektedir. Bu uygulama ile her servis güdümlü yazılımın CAB içerisinde nasıl yapıldığını hem MVP pattern CAB içerisinde nasıl kodlandığı gördük.



SCSF3 SmartPart - Workspace

CAB uygulamalarının görsel her bir parçası [SmartPart]’dır. SmartPart bir UserControl nesnesidir. Her hangi bir temel sınıftan türetilmeleri gerekmemektedir. Sadece [SmartPart] attribute ile işaretlenmeleri yeterlidir. Bu parçalar uygulama içerisinde dinamik olarak yerleşmektedir. Dizayn anında uygulama içerisinde [SmartPart] nesnelerini yerleşeceği alanlar Workspace olarak adlandırılır. Workspace nesneleri IWorkspace ara yüzünü uygulayan ve içerinde [SmartPart] özelliğine sahip nesneleri saklayan Container nesnelerdir.




Demo


Böylelikle uygulamanız için en esnek ara yüz tasarımlarını gerçekleştirebilirsiniz. Ayrıca IWorkspace arayüzünü uygulayarak kendi SmartPart Container kontrolü olan Workspace sınıflarını yazabilirsiniz.
RootWorkItem Injection Pattern kullanarak oluşan tüm nesneleri takip edebilmektedir. Dolayısı ile sizin tasarım anında ara yüze yerleştirdiğiniz Workspace nesneleri oluşturulurken RootWorkItem.WorkSpaces koleksiyonu üzerine eklenecektir.
Şimdi uygulama üzerinde işler nasıl dönüyor inceleyelim. İlk demo uygulamada kırmızı ve mavi adlı iki ayrı uygulamayı aynı proje ile açmıştık. Şimdi bu iki ayrı uygulamayı aynı ara yüz içerisinde gibi bir araya getirelim.

Öncelikle Shell form üzerine bir tane DeckWorkspace kontrolü ekleyelim. Uygulama genelinde gerekli genel ara yüz işlemleri için bir Layout modülü oluşturalım. Daha sonra bu workspace içerinde uygulamanın asıl görünümünü oluşturacak bir ShellLayoutView ekleyelim.


Şekil 1 Shell.exe içinde ShellForm


Şekil 2 Layout.dll için ShellLayoutView

ShellLayoutView tüm uygulama için ortak olan ara yüz elemanlarını barındırmaktadır. Daha sonra uygulama içerisinden ortak ara yüz elemanlarına nasıl erişim sağlandığını inceleyeceğiz.

ShellForm üzerinde ki LayoutWorkspace RootWorkItem.Workspaces içerisine kendiliğinden eklenecektir. Layout modül içerisinde ki ShellLayoutView ara yüzünü LayoutWorkspace içerisine ekleyelim.

namespace Layout {
public class Module : ModuleInit {
private WorkItem rootWorkItem;
[InjectionConstructor]
public Module([ServiceDependency] WorkItem rootWorkItem) {
this.rootWorkItem = rootWorkItem;
}

public override void Load() {
base.Load();

// layout view shellform üzerine ekle
ShellLayoutView shellLayout = rootWorkItem.SmartParts.AddNew<ShellLayoutView>();
rootWorkItem.Workspaces["LayoutWorkspace"].Show(shellLayout);
}
}
}

Şimdi her iki iş modülümüz içinde ki ara yüzleri ayrı pencereler içine yerleştirelim.
namespace KirmiziModul {
public class KirmiziModuleInit : ModuleInit{
private WorkItem _workItem;
[InjectionConstructor]
public KirmiziModuleInit([ServiceDependency] WorkItem workItem) {
_workItem = workItem;
}
public override void Load() {
base.Load();
// Form1 view shellformview üzerinde adı
// 'ContextWorkspace' olan alan üzerine ekle
Form1 kirmiziView = _workItem.SmartParts.AddNew<Form1>();
_workItem.Workspaces["ContextWorkspace"].Show(kirmiziView);
}
}
}


namespace MaviModul {
public class MaviModuleInit :ModuleInit {

private WorkItem _workItem;
[InjectionConstructor]
public MaviModuleInit([ServiceDependency] WorkItem workItem) {
_workItem = workItem;
}
public override void Load() {
base.Load();
// MaviModule.Form1 view shellformview üzerinde adı
// 'NavigatorWorkspace' olan alan üzerine ekle
Form1 kirmiziView = _workItem.SmartParts.AddNew<Form1>();
_workItem.Workspaces["NavigatorWorkspace"].Show(kirmiziView);
}
}
}





Gördüğünüz ara yüzü her bir parçası farklı bir dll dosyası içerisindedir. Tamamen dinamik olarak oluşturulduk.

Salı, Ekim 16, 2007

20/10/2007 15:00 Beykoz

Cumartesi, Eylül 29, 2007

SCSF2 Shell - Module

Shell - Module


Cab 2 – Shell Module

Bu makaleye başlamadan önce arka alanda bizim için çalışan Injecton Pattern ve ObjectBuilder sihirbazını incelemenizi öneririm. Anlamamız gereken iki kavram vardır ‘Module’ ve ‘Shell’. Modüller bir birinden bağımsız çalışan içinde kob bloklarımız, kullanıcı ara yüzlerimiz, iş servislerimiz, ara yüz parçalarımız ve çeşitli bileşenlerimiz olan kendi başına ayrı uygulamalardır. Modül kendi başına çalışan ayrı bir .NET projesi gibi görülebilinir. Modüller kendi başlarına atomik bir işi yerine getirirler. İkinci kavramımız ‘Shell’ kavramıdır. Shell basitçe ‘ortak pencere’dir. Shell bizim modüllerimizi ve ara yüz elemanlarımızı taşır. Shell ve müdüllerin hiç biri bir diğerini referans etmez. Tanım olarak;

Module: Karmaşık arayüzler kullanan tek başına bir projedir

Shell: Karmaşık ara yüzler için bir sunucu(host) uygulamasıdır.

Demo

Basit bir uygulama ile bu iki kavramın nasıl çalıştığına bakalım. Sadece kırmızı bir form üreten projemiz olsun. Bu projeden tamamen bağımsız çalıştığında mavi bir form üreten ikinci bir projemiz olsun. Bunlar bizim modüllerimiz. Üçüncü bir projede ise bu iki modülü taşıyacak olan Shell uygulamamız olsun. Shell sadece bu iki formu görüntüleyecektir.

Burada zor olan her üç uygulamanın aynı Solution üzerinde çalışıyor olmasıdır. Biz her üç formunda açılışta gösterilmesini istiyoruz. Ortada hiçbir referans olmadığı için reflection ile tabii ki her üç formu gösterebiliriz. Fakat biz bu işi CAB bırakıyoruz.

Önce üç adet .NET projesi oluşturalım. Shell ve modüllerimiz Mavi, Kırmızı. Her üç uygulamaya da ObjectBuilder, CAB ve CABWinforms dll’lerini referanslarını ekleyelim. Tüm uygulamalarda ‘Build Directory ‘ alanını aynı konum ayarlayalım ki bir arada çalışabilsinler. Her bir uygulamayı çalıştırdığımızda kendi özel formlarını görürüz.

Şimdi Shell uygulamasına diğer uygulamalara ‘Container’ olma yeteneğini verelim. Bunun için Shell uygulamasını CAB uygulamasında türetmemiz ve Main fonksiyonu içerisinde üst sınıf olan CAB uygulamasını başlatmamız yeterlidir.

using System;
using System.Collections.Generic;
using System.Windows.Forms;
using Microsoft.Practices.CompositeUI.WinForms;
using Microsoft.Practices.CompositeUI;
 
namespace Shell {
    public class Program :FormShellApplication<WorkItem, Form1> {
        [STAThread]
        static void Main() {
            new Program().Run();
        }
    }
}

CAB uygulama sınıfı FormShellApplication dır. İki generic parametresi vardır. Giriş bölümde tartıştığımız Use-Case diyagramlarımızı yerine getiren WorkItem sınıfı, diğeri bizim uygulamamızda ara yüz işlemlerini yerine getiren ve ortak pencere olma özelliğini taşıyan Form1 formumuzdur. CAB Shell uygulaması bir xml dosya içerisinde kendisine çalıştırılacak modüllerin sunulmasını bekler. Modül listesini taşıyan dosya uygulamanın çalışma dizininde yer alan ‘ProfileCatolog.xml’ dosyasıdır. Bu dosya çok basit bir şekilde çalıştırılacak mödül listesini gösterir.

<?xml version="1.0" encoding="utf-8" ?>
<SolutionProfile xmlns="http://schemas.microsoft.com/pag/cab-profile">
  <Modules>
    <ModuleInfo AssemblyFile="KirmiziModul.exe" />
    <ModuleInfo AssemblyFile="MaviModul.exe" />
  </Modules>
</SolutionProfile>


Son bir işimiz daha kaldı. Uygulama programını CAB uygulama sınıfından türettiğimiz gibi modül programlarını da CAB ile uyumlu hale getirmeliyiz.

CAB uygulaması Run() fonksiyonu ile çalıştırıldığında CAB ProfileCatolog.xml dosyasını acar. Burada yer alan modüllerde CAB ModuleInit sınıfından türetilmiş bir sınıfı arar. Eğer ModülInit sınıfını bulursa ModuleInit.Load() fonksiyonunu çağırarak modülleri yükler.

using Microsoft.Practices.CompositeUI;
 
namespace KirmiziModul {
    public class KirmiziModuleInit : ModuleInit{
        public override void Load() {
            base.Load();
            Form1 form = new Form1();
            form.Show();
        }
    }
}

ModuleInit içerisinde biz bu örnekte formlarımız gösteriyoruz. Shell uygulamamızı çalıştırdığımızda her üç formunda açıldığını görüyoruz.

Hepsi bu kadar böylelikle ilk CAB uygulamızı yazmış olduk.

Perşembe, Eylül 27, 2007

Injection Pattern

Injection Pattern


CAB 1- Object Builder – Injection Pattern

Bir önce ki makalede bir stok uygulaması için muhtemel bir senaryo üzerine konuştuk. Use Case diyagramlarını Sub-Use Case ve Root-Use Case diyagramları halinde kodlamak gerektiği sonucuna vardık. Use Case diyagramlarını gerçekleştirme görevini (gerekli tüm parçaları içinde barındıran) ‘WorkItem’ adı verdiğimiz bir nesnesinin yerine getirdiğini konuştuk. Kaldığımız nokta tüm WorkItem nesnelerini yöneten bizim Use Case uygulamamızı yerine getirecek olan RootWorkItem WorkItem nesnelerine ihtiyaç duydukları nesneleri nasıl sağlamalı konusu idi.

Aynı uygulama üzerinden devam edelim. Aşağıda örnek bir Stok projesinin parçaları ve bu parçaların bir birleri ile olan ilişkisi mevcuttur. (Demo)

Şimdi klasik bir yöntemle bu projeyi kodlayalım. Muhtemel kodlamamız şu şekilde olacaktır.

public class App {
    public App() {
        quotes = new StockQuotes();
        authenticator = new Authenticator();
        database = new Database();
        logger = new Logger();
        errorHandler = new ErrorHandler();
    }
}


Uygulama sınıfımız için alt parçalara ulaşmak gayet acık ya alt parçalar birbirleri ile nasıl haberleşir. Yani StockQuotes nasıl Logger elemanını bulur? Veya Database kullanıcı yetkilendirmesini nasıl yapar?

Muhtemel kodlamanızda her bir parça kendi içinde ihtiyaç duyduğu diğer sınıfları yeniden oluşturacaktır.


public class StockQuotes {
    public StockQuotes() {
        logger = new Logger();
        errorHandler = new ErrorHandler();
    }
    internal Logger logger;
    internal ErrorHandler errorHandler;
}


Burada bir sorun görüyor musunuz? Aynı sınıfa ait nesneler mükerrer defa oluşturulmakta. Bu durumu iyileştirmek için tüm nesneleri içinde saklayacak yeni bir sınıf yazalım. Tüm gerekli nesneleri bu sınıf içinde bir defa oluşturalım ve daha sonra aynı sınıf üzerinden erişelim.


public interface ILocator {
    TObject Get<TObject>();
    object Get(Type objectType);
}
 
public class MyLocator :ILocator {
    protected Dictionary<Type, object> dict;
 
    public MyLocator() {
        dict = new Dictionary<Type, object>();
 
        dict.Add(typeof(ILogger), new Logger());
        dict.Add(typeof(IErrorHandler), new ErrorHandler(this));
        dict.Add(typeof(IStockQuotes), new StockQuotes(this));
        dict.Add(typeof(IDatabase), new Database(this));
        dict.Add(typeof(IAuthenticator), new Authenticator(this));
        dict.Add(typeof(App), new App(this));
    }
}


Tüm sınıfları saklayan anahtar olarakta sakladığı sınıfın arayüzünü kullanan bir üst sınıf ile mükerrer defa nesne oluşturma problemini aşabiliriz. Uygulama kodlarımız şu şekilde olacaktır:


public App(ILocator locator) {
    quotes = locator.Get<IStockQuotes>();
    authenticator = locator.Get<IAuthenticator>();
    database = locator.Get<IDatabase>();
    logger = locator.Get<ILogger>();
    errorHandler = locator.Get<IErrorHandler>();
}
 
public class StockQuotes :IStockQuotes{
    public StockQuotes(ILocator locator) {
        logger = locator.Get<ILogger>();
        errorHandler = locator.Get<IErrorHandler>();
    }…

Bu çözümün güzel tarafı sınıflar ihtiyaç duydukları sınıfları kolaylıkla alabiliyorlar.

Ama bizim için tam bir çözüm değil. Her obje ILocator ile ilişkili, ayrıca nesne oluşturma sırası hala problemli. Uygulama sırasında anlık olarak kullanılmayan nesneler hala saklanmaktadır. O halde birde LifeTime Continer nesnesine ihtiyacımız var. Locator nesneleri bu LifeTimeContainer üzerinde saklamalı LifeTimeContainer referansı kalmayan nesneleri kendisi temizlemelidir.

LifeTime Continer nesnesini kullanabilmemiz için tüm oluşan objelerden haberdar olmamız gerekmektedir. Madem her nesne oluşturulurken çalışacak bir sınıf yazacağız bu sınıfı Stragy Pattern destekler şekilde yazalım daha sonradan eklenecek yeni Stragy sınıfları ile entegre edebilelim. Tabi çok fazla sayıda Stragy ve Policy sınıfı olunca bunların bir sorumluluk zinciri şeklinde çalışmaları gerekmektedir. P&P grubunun hazırladığı Builder sınıfı bizim için bu işlemi yapmaktadır.



Nesnelerin Creation anını kontrol edebildiğimize göre artık nesnelere ihtiyaç duydukları tüm nesneleri ‘enjekte’ edebiliriz. Önce Builder sınıfını uygulayacak bizim örnek uygulamamızda tüm nesneleri yönetecek örnek bir DependencyContainer sınıfı yazalım ve daha sonra nesnelere enjekte olayının nasıl uygulandığına bakalım.


public DependencyContainer() {
    _builder = CreateBuilder(null);
    _locator = new Locator();
    _container = new LifetimeContainer();
    _locator.Add(typeof(ILifetimeContainer), _container);
}
 
private IBuilder<BuilderStage> CreateBuilder(IBuilderConfigurator <BuilderStage> configurator) 
{
IBuilder<BuilderStage> result = new BuilderBase<BuilderStage>();
result.Strategies.AddNew<TypeMappingStrategy>(BuilderStage.PreCreation);
result.Strategies.AddNew<SingletonStrategy>(BuilderStage.PreCreation);
result.Strategies.AddNew<ConstructorReflectionStrategy>(BuilderStage.PreCreation);
result.Strategies.AddNew<PropertyReflectionStrategy>(BuilderStage.PreCreation);
result.Strategies.AddNew<MethodReflectionStrategy>(BuilderStage.PreCreation);
result.Strategies.AddNew<CreationStrategy>(BuilderStage.Creation);
result.Strategies.AddNew<PropertySetterStrategy>(BuilderStage.Initialization);
result.Strategies.AddNew<MethodExecutionStrategy>(BuilderStage.Initialization);
result.Strategies.AddNew<BuilderAwareStrategy>(BuilderStage.PostInitialization);
result.Policies.SetDefault<ICreationPolicy>(new DefaultCreationPolicy());
if(configurator != null)
    configurator.ApplyConfiguration(result);
return result;
}

Kendi özel Strategy sınıflarımızı yazıp burada DependencyContainer üzerine ekleyebiliriz. Bu tüm nesne Creation işlemlerinde çalışacak bir kod bloğu manasına gelir. Uygulamaya sonradan kazandırılacak yetenekler için harika bir durumdur.

DependencyContainer sınıfımıza diğer nesnelere hizmet verecek şekilde biraz daha genişletelim.


public TBuild Get<TBuild>() {
    return _builder.BuildUp<TBuild>(_locator, null, null);
}
 
public void RegisterSingleton<TBuild>() {
    _builder.Policies.Set<ISingletonPolicy>(new SingletonPolicy(true), typeof(TBuild), null);
}
 
public void RegisterTypeMapping<TRequested, TToBuild>() {
    _builder.Policies.Set<ITypeMappingPolicy>(new TypeMappingPolicy(typeof(TToBuild), null), typeof(TRequested), null);
}


Artık nesneler DependencyContainer içinde register olan her hangibir sınıf örneğini kolayca Get<T>() ile alabilirler.Nesneler oluşurken kendi ihtiyaçlarını Builder sınıfı sağlayacaktır.


DependencyContainer container = new DependencyContainer();
IStockQuotes stock = container.Get<IStockQuotes>();
 
public class StockQuotes :IStockQuotes {
    public StockQuotes(IErrorHandler handler, Logger logger) {
        _errorHandler = handler;
        _logger = logger;
    }
 
    Logger _logger;        
    public Logger Logger {
        get {
            return _logger;
        }
    }
 
    IErrorHandler _errorHandler;
    public IErrorHandler ErrorHandler {
        get {
            return _errorHandler;
        }
    }
}


Sadece constructor içinde değil property veya fonksiyonlara da enjekte yapabiliriz.


public class Database :IDatabase {
    public Database() {            
    }
    Logger _logger;
    [Dependency]
    public Logger logger {
        set {
            _logger = value;
        }
        get {
            _logger;
        }
    }
    [InjectionMethod]
    public void Init(IErrorHandler handler) {
        
    } 
}


DependencyContainer veya CAB içinde ki adı ile Builder sınıfı konfigürasyon ile nesne oluşturabilir. Bunun için hazırlanmış IBuilderConfigurator ara yüzü vardır. Kullanımı insan sihirli kod bloğu hissini verir. Nesneleriniz sizin config dosyası üzerinde yazdığınız verilere göre oluşmaktadır. Tüm property metot parametreleri her şeyi config üzerinden halledebilirsiniz.


<ContainerConfig xmlns='container-config'>
    <Mappings>
        <Mapping FromType='Object1' ToType='Object1'/>
    </Mappings>
    <BuildRules>
        <BuildRule Type='Object1' Mode='Instance'>
            <Constructor>
                <Value Type='System.String'>ABC</Value>
            </Constructor>
            <Method Name='SetLength'>
                <Value Type='System.Int32'>123</Value>
            </Method>
        </BuildRule>
    </BuildRules>
</ContainerConfig>
 
public class Object1 {
    public Object1(string color) {
        _color = color;
    }
    public void SetLength(int length) {
        _length = length;
    }


Şimdi burada durup birde büyük resme bakalım. ObjectBuilder kütüphanesi tüm P&P grubu ürünlerinde başrol oynamaktadır. Biz farkında olmasak bile mesela bir xml üzerinden config verilerini okurken bile bu kütüphane iş başındadır. CAB içerisinde her WorkItem nesnesi kendi Builder nesnesine sahiptir. Her WorkItem RootWorkItem ile entegre çalıştığı için asıl LifeTime Container RootWorkItem üzerindedir.

Böylelikle en zor konu olan Injection Pattern sonuna geldik. Eğer bu konuyu anladı iseniz P&P grubuna ait tüm ürünleri rahatlıkla kullanabilirsiniz. P&P grubu bu pattern ile nasıl harika işler çıkartmış incelemeye devam edeceğiz. Hafta gerçek programlar yazmaya SCSF ile uygulama geliştirmeye başlıyoruz.


Emre Coşkun 27/09/2007 http://www.emrecoskun.net/

Pazartesi, Eylül 24, 2007

SCSF1 Giriş

SCSF-CAB Giriş


Microsoft Smart Client Software Factory (SCSF) microsoft pattern&practice(P&P) gurubunun hazırlamış olduğu tüm aplication block'ları entegre bir biçimde kullanan gene aynı gurup tarafından geliştirilen harika bir yazılımdır. Birbirine çok benzeyen WorkItem, Module, WorkSpace, Shell gibi kavramlardan dolayı ilk bakışta yazılımcılara karmaşık gelen bir yapısı vardır. Gerçektende dependency injection, command patterns, event brokers, model-view-presenter patterns gibi üst seviye kavramların anlaşılması son derece güçtür. Biz bu yazı dizisinde SCSF-CAB ile kullanılan kavramların hepsini ele alıp inceleyeceğiz. Aynı zamanda bu kavramların anlaşılması için Microsoft tarafından yayınlanan dokümanlar oldukça fazladır.

SCSF projesinin başlangıç noktası Composite Application Block (CAB)'dır. CAB aslında bizim masaüstü uygulamamızın temelli oluşturmaktadır. Diğer uygulama blokları CAB çevresinden CAB’ı destekleyen yapılardır.


SCF-CAB Temel Mimarisi.


CAB Nedir?

Composite UI Aplication Block Microsoft P&P grup tarafından karmaşık ve modüler Smart Client uygulamalar geliştirmek üzere kullanıma hazır olarak sunulan bir uygulama bloğudur. İki önemli yeteneği vardır:

  1. Birbirinden bağımsız olarak geliştirilen ve kullanılan bileşenler ile oluşturulan karmaşık ara yüzler dizayn eder. (Module)

  2. Yapılan ara yüz uygulamalarını alır ve bunları ortak bir pencere üzerinde entegre bir biçimde çalıştırır. (Shell)


CAB UseCase Driven Developing yaklaşımını temel alır. CAB mimarisinin öncelikli amacı bizim UseCase diyagramlarımızı gerçekleştirmektir.

Use case diyagramlarınızla 1:1 eşleşen WorkItem düzenleyebilirsiniz. WorkItem eklenen ara yüz işlemleri ve gerekli tüm parçalarla birlikte çalışan bir Use Case kontrolcüsüdür. Kodlama açısından baktığımızda ise WorkItem nesneleri Use Case işlemlerini gerçekleştirecek tüm alt parçalara sahiplik yapan bir üst nesnedir.

WorkItem


Bir senaryo üzerine düşünelim. Bir stok projesi için Back Office uygulamasını yazacaksınız. Use Case diyagramlarını çıkarttınız. Back Office – front Office çalışanı, stok yöneticisi, müşteri gibi çeşitli aktörleriniz olacaktır. Her bir aktörün yetki seviyelerine göre yerine getirebileceği stok isteği, müşteri tanımlama, kredi onaylama gibi birçok hareketleri olacaktır. Her bir aktör bir diğer aktöre ait hareketlerin bir kısmını da kullanacaktır. Mesela stok yöneticisi Back Office çalışana ait müşteri borçlandırma hareketini de işleyebilecektir.

Ortak kullanılan hareket gruplarını bir Sub-Use Case içine toplayalım. Geriye kalan her bir atomik hareket gruplarını da ayrı Sub-Use Case diyagramlarının içine toplayalım. Tüm bu Sub-Use Case diyagramlarını içeren bir Root-Use Case diyagramımız ve bu Root-Use Case içinde bir kısmı ortak bir kısmı sadece kendi aktörü ile çalışan Sub-Use Case diyagramlarımız olacaktır.

İşte size CAB. Bir önce ki kısımda her bir Use-Case ile 1:1 eşleşen ve görevi Use-Case diyagramını gerçekleştirmek olan WorkItem nesnelerini gördük. Seneryomuzda Sub-Use Case ve Root-Use Case diyagramları olması gerektiğini gördük. CAB içerisinde Root-Use Case diyagramını gerçekleştiren bir RootWorkItem vardır. Tüm Sub-Use Case işlerini gerçekleştiren WorkItem nesneleri bu RootWorkItem altında oluşturulurlar.

Şimdi böyle bir yazılımın ihtiyaçlarını düşünelim.

  1. RootWorkItem nesnesi WorkItem nesnelerine ihtiyaç duydukları parçaları sağlamadır. (Injection Pattern – Object Builder)

  2. RootWorkItem nesnesini içeren ortak bir uygulama sağlanmalıdır. Üstünde her biri sadece bir tek WorkItem içeren modüllerden oluşan esnek ve genişleyebilen uygulama yapısı sağlanmalıdır.(Shell – Module)

  3. Arayüz ile iş kodu birbirinden tamamen ayrılmalıdır.(Model-Viewer-Presenter Pattern)

  4. WorkItem nesneleri program kullanıcısıyla doğrudan etkileşime girebilmelidir. (Command Pattern)

  5. WorkItem nesneleri kullanıcı yetkisine göre iş kurallarını işletmelidir. (Action Pattern)

  6. WorkItem nesneleri gerekli durumlarda diğer WorkItem nesneleri ile konuşabilmelidir. (Event Broker)

  7. WorkItem nesneleri ortak kullanılan ToolBar-StatusBar gibi ara yüz elemanlarını erişebilmelidir. (Register-Unregister UI Element)

  8. Ara yüz geliştirirken sadece Visual Studio ile gelen componentlere bağlı kanılamayacağı için genişleyebilir bir mekanizma sağlanmalıdır.(CAB Extansions- Adapter- Factory Pattern)


Tabii bu ihtiyaçlar bizim Use Case güdümlü yazılım mimarimiz için özel ihtiyaçlardır. Birde tüm uygulamalar için ortak ihtiyaçlar vardır. (Application Blocks)

  1. Hata yönetimi

  2. Log

  3. Veri erişimi

  4. Güvenlik

  5. Şifreleme

  6. Veri doğrulama

Tüm bu uygulama bloklarının da kendi uygulamamız içerisinde kullanmamız gerekmektedir. Belki bu uygulama bloklarına kendi iş ihtiyaçlarımıza göre bir takım yeni özellikler eklemek veya yeni bir uygulama bloğu oluşturmak gerekebilir. (Application Block Software Factory)


Bu makalede SCSF-CAB konusuna giriş yaptık. CAB Use-Case güdümlü programlamayı nasıl yerine getirdiğine dair konuştuk. CAB uygulamasının ihtiyaçlarını gördük. Yukarıda ki sıraya göre bu yazılım ihtiyaçlarını CAB nasıl yerine getirmiş P&P serisinde anlatmaya çalışacağım. Bu seri makaleler içinde öğrenme yöntemimiz tümden gelim olacaktır. Perşembe günü biraz uzun bir makale ile benim mühendislik harikası olarak değerlendirdiğim Injection Pattern ile görüşmek üzere.


24.08.2007

Emre COŞKUN

Http://www.emrecoskun.net/