30 Ocak 2011 Pazar

Entity Framework Code First

EF 4 ile birlikte birçok yeni özellik gelmiştir.

  • POCO: Entity sınıflarının bir üst sınıftan türetilmek zorunda değiller
  • Lazy Load: Entity örneği alt Entity özelliklerini ilk erişim anında yükleyebilirler
  • Self-Tracking: Entity örnekleri değişim durumlarını kendileri takip ediyorlar. Böylece durum bağımsız (stateless) web çağrılarında kullanılabilirler.
  • Test: EF 4 nesneleri interface’ler aracılığı ile daha kolay test edilebilir durumdalar
  • Linq: Artık EF 4 tüm linq metotlarını destekliyor

Visual Studio içinde EF 4 için birçok model tasarımcısı ve araç eklenmiştir. EF tasarımcıları “Model First” olarak adlandırılan yöntem için kullanılırlar. Model First önce veri tabanında veya EF 4 model tasarımcısında veri modelin çıkartılması daha sonra çıkartılan modele ait kodların kod şablonları (T4) ile oluşturulması yöntemidir.

Code First

Entity Framework 4 ile tasarımcı temelli Model First veya Database First yaklaşımına ek olarak “Code First” olarak adlandırılan kod merkezli yeni bir yaklaşım ortaya konmuştur. Code First ile her hangi bir tasarımcıya, xml eşleştirme dosyasına veya bir Entity Base Class’a ihtiyacınız yoktur. Model ile Entity sınıflarınız arasında ki eşleştirmeyi isimlendirme kuralları ile yapabilirsiniz ve bu eşleştirmeyi tamamen kodlama ile özelleştirebilirsiniz.

Uygulama Geliştirme

Öncelikle bir uygulama oluşturalım. Daha sonra Nuget ile projemize EFCodeFirst ekleyelim.

Bir veri tabanı ile başlamamıza gerek yok. Bunun yerine veri yapımızı (Domain Model ) taşıyan standart NET sınıfları yazarak işe başlayacağız.

Model Sınıflarını Oluşturma

Küçük bir adres defteri uygulaması yazıyoruz ve bizim veri yapımız gerçekten küçük bir yapıdır. Veri modelimizde sadece Kişiler ve Kişi Adresleri var. Bu basit iki sınıfı oluşturalım.

[cs]

public  class  Person 
 {
     public  int  PersonId  { get ; set ; }
     public  string  FullName  { get ; set ; }
     public  string  Company  { get ; set ; }
     public  DateTime  BirthDay  { get ; set ; }
     public  virtual  List  Adresses  { get ; set ; }
 }
 public  class  Adress 
 {
     public  int  AdressId  { get ; set ; }
     public  string  AdressType  { get ; set ; }
     public  string  FullAdress  { get ; set ; }
     public  int  PersonId  { get ; set ; }
     public  virtual  Person  Person  { get ; set ; }
 }

Yukarıda ki Person ve Adress model sınıfları POCO sınıflardır. Her hangi bir üst sınıftan veya interface’ den türetmek zorunda değiliz. Her hangi bir veri tabanı bağlantısı da belirtmek zorunda değiliz. Veri modeline uyan herhangi bir veri tabanına bağlanabilirler.

Context Sınıfını Oluşturma

POCO sınıflarımızı oluşturduk. Veri tabanımız ile POCO sınıflarımızı eşleştirme işlemini yapacak Context sınıfımızı yazabiliriz.

[cs]

public  class  AdressBook  : DbContext 
 {
     public  DbSet  Persons  { get ; set ; }
     public  DbSet  Adresses  { get ; set ; }
 }

AdressBook eşleştirme sınıfının DbContext sınıfından türetilmelidir. AddressBook sınıfı iki public özelliği Persons ve Adresses özellikleride DbSet sınıfının uygulamalarıdır. DbContext ve DbSet sınıfları EF Code First kütüphanesinde yer almaktadır.

İhtiyacımız olan tüm kodlamayı yaptık. Veri tabanı ile eşleştirme için başka bir işleme ihtiyacımız yok. Her hangi bir veri tabanı şema eşleştirme dosyası veya eşleştirme aracı veya her hangi bir tasarımcı kullanmayacağız. Veri tabanı üzerinde gerekli tüm işlemleri bu üç sınıf yapacak.

Kural Tabanlı Eşleştirme

Her hangi bir eşleştirme verisi olmadan bu üç sınıf nasıl veri tabanında ki nesneler ile eşleşecekler?

EF Code First “yapılandırma yerine kurallar” yaklaşımı ile eşleştirme yapmaktadır. Tabi ki bu varsayılan kuralları değiştirebilirsiniz.

Nedir EF Code First kuralları?

EF Code First “sadece çalış” mantığı ile mümkün olduğu adar az veri ile çalışabilecek şekilde tasarlanmıştır. Mesela Adres defteri örneğimizde Persons ve Adress olarak iki tablo oluşturacaktır. Bu tabloların kolonları da sınıfların property’lerinden oluşacaktır. Primary key olarak da tablo adı ile aynı sonu Id ile biten property kullanılacaktır. Foreing Key olarakda sınıf içinde ki diğer nesne referansları kullanılacaktır.

Veri Tabanı

Kodlarımız yazdık. Peki ya veri tabanı? Kodları yazarken hiçbir veri tabanına ihtiyacımız yok. Fakat çalışma anında veri tabanı gerekmektedir. İki şekilde veri tabanı oluşturabiliriz. İlki kendimiz gider uygun veri tabanını oluştururuz. İkincisi EF Code First bizim yerimize otomatik olarak tüm veri tabanını oluşturur. Adres defteri uygulamamız için ikinci yolu tercih ediyorum. EF Code First varsayılan olarak DbContext sınıfı ile aynı isimde bir bağlantı adı arayacaktır. Bizim DbConext sınıfımızın adı AdressBook olduğu için aynı isimde bir bağlantı adı kurmalıyız.

   <connectionStrings>
     <add name= "AdressBook "
          connectionString= "Data Source=|DataDirectory|AdressBookDb.sdf "
          providerName= "System.Data.SqlServerCe.4.0 " />
   </connectionStrings>

Bu örnekte ben veri tabanı olarak SQL CE veri tabanında faydalandım.

Model Nasıl Kullanılır?

Tüm ihtiyacımızı karşılayan üç sınıfı yazdık. Linq ile istediğimiz tüm sorguları çalıştırabiliriz.

Tüm kişi listesini getiren bir linq expression yazalım:

[cs]

var  adressBook = new  AdressBook ();
 var  list = from  p in  adressBook.Persons 
                 orderby  p.FullName 
                 select  p;

İki tablo arasında ki ilişkiden de faydalanabiliriz:

[cs]

var  adressBook = new  AdressBook ();
 var  list = from  p in  adressBook.Persons 
             where  p.Adresses .Count  > 0
             orderby  p.FullName 
             select  p;

Yukarıda ki kodda sadece adres verisi olan kişiler listelenmiştir. Primary Key özelliğinden de faydalanabiliriz:

[cs]

var  adressBook = new  AdressBook ();
 var  person = adressBook.Persons .Single (c => c.PersonId  == id);

CRUD işlemleri olmadan olmaz. Yeni bir kişi eklemek için yeni bir Person nesnesi oluşturup gerekli property’leri doldurmalıyız. Daha sonra DbContext üzerinde ki ilgili DbSet’e yeni kişi nesnemizi eklemeliyiz. Son olarak da yaptığımız değişikliği veri tabanına yansıtmamız gerekmektedir.

[cs]

var  person = new  Person 
 {
     FullName  = "Ali Veli" , 
     Company  = "ana okulu" , 
     BirthDay  = DateTime .Today 
 };
 var  adressBook = new  AdressBook ();
 adressBook.Persons .Add (person);
 adressBook.SaveChanges ();

Kayıt güncellemek için önce güncelleyeceğimiz kaydı buluyoruz. Sonra gerekli değişikliği yapıyoruz. En son yapılan değişikliği veri tabanına yansıtıyoruz.

[cs]

var  adressBook = new  AdressBook ();
 var  person = adressBook.Persons .Find (id);
 person.Company  = "FOG" ;
 person.BirthDay  = DateTime .Today .AddYears (20);
 person.FullName  = "Deli Veli" ;
 adressBook.SaveChanges ();

Sonuç

ORM araçları iş uygulaması geliştirmenin en önemli parçasıdır. EF 4 Code First veri ile kod temelli çalışılabilecek bir yol sunmaktadır. EF Code First çok güçlü bir veri işleme yöntemidir. Bu yazıda EF Code First varsayılan kuralları ile ele alınmıştır. Burada değinilmemiş birçok özellik var. System.ComponentModel.DataAnnotations isim uzayında var olan nitelikler ile entity nesnelerinize doğrulama özellikleri ekleyebiliriz. IValidatableObject arayüzü ile sınıf seviyesinde doğrulama metotları ekleyebiliriz. DbContext sınıfının OnModelCreating metodu ile entity ve veri nesnelerini eşleştirmelerini özelleştirebilirsiniz. (Böylece mevcut veri tabanları ile EF Code First yaklaşımını kullanabilirsiniz. ) DatabaseIntialize sınıfları ile veri tabanı oluşunca eklenecek varsayılan kayıtları girebilirsiniz vs.

EF Code First tümleşik bir veri işleme yöntemi sunmaktadır. EF Code First ile kod geliştiriciler daha aktif kullanılabilen bir veri işleme ortamına sahip oldular.


28 Ocak 2011 Cuma

SQL CE

SQL CE kolay veritabanı depolaması sağlayan ücretsiz, gömülü, veritabanı motorudur. SQL CE kullanmak için her hangi bir veri tabanı kurulumu gerektirmez. Sadece gerekli dll’ler bin dizinine kopyalanır ve uygulamanız veri tabanını motorunu kullanır. SQL CE çalışma döngüsü uygulamanın veri tabanına ilk erişim ile başlar ve uygulama kapatılınca uygulama ile birlikte kapatılır. Uygulama ile birlikte kapanan SQL CE veri tabanı otomatik olarak bellekten temizlenir.

SQL CE veri tabanı verileri dosya içinde saklamaktadır. SQL CE veri tabanını sadece dosyayı kopyala-yapıştır yaparak taşıyabilirsiniz. Dosyada verileri saklaması rağmen ASP.NET gibi çok kanallı kullanıma da uygundur. Her hangi bir çökme veya kilitlenme riski yoktur. Tüm uygulamalarda rahatlıkla kullanılabilinir.

Nasıl Kullanılır?

SQL CE kullanmak için geliştiriciler ekstra hiçbir bilgiye ihtiyaçları yoktur. Mevcut tüm .NET veri erişim metotları ile SQL CE erişimi sağlanabilmektedir. Kod geliştiriciler için en uygun veri tabanıdır. SQL CE veri tabanını kullanmak için önce Visual Studio SP 1 (beta)yı yüklemeniz gerekmektedir. Daha sonra SQL CE Tools For Visual Studio yüklemeniz gerekmektedir.

Örnek Uygulama

Örnek bir Asp.Net adres defteri uygulaması yapalım. Öncelikle boş bir Asp.Net uygulaması açalım.

Uygulamaya SQL CE Veri Tabanı Ekleme

Açtığımız boş uygulamanın App_Data dizinine SQL CE veri tabanını aşağıda ki gibi ekleyelim.

Veri tabanını ekledikten sonra projenin görüntüsü aşağıdaki gibi olacaktır.

Proje referanslarına System.Data.SqlServerCe kütüphanesi ve App_Data dizinine Sql Ce veri dosyasının eklendiğini görüyoruz. Veri tabanı ile çalışmak için gereken kütüphaneyi ve verilerin saklanacağı veri dosyasını projemize eklemiş olduk.

Eklediğimiz Sql Ce dosyasına çift tık ile içini açalım. Şimdi yeni bir tablo ekleyebiliriz.

Eklediğimiz tablolarımızın veri modelini oluşturalım ve daha sonra bu tablolara veri girelim. Örnek adres defteri uygulamamız için People tablosu oluşturdum ve tabloya deneme kayıtlarını girdim.

SQL CE Veri Yapısını Alma

Tüm SQL CE işlemlerimizi tamamladık. Artık uygulama tarafında kullanabiliriz. SQL CE veri tabanına Entity Framework ile bağlanmak için Entity Data Model ekleyelim.

Açılan ekranda “Generate from Database” seçeneğini seçip ilerleyelim. Sonra ki ekranda eklediğimiz AdressBook.sdf dosyasının seçili geldiğini göreceksiniz.

Sırada ki ekranda ekran da SQL CE veri tabanımızda ki tabloların geldiğini görüyoruz. Burada iki tabloyu da seçip sihirbazı tamamlayalım. Oluşan Entity Modeli aşağıda ki gibi olacaktır.

SQL CE ile Çalışmak

Veri tabanında oluşturduğumuz modeli uygulamamıza taşımış olduk. Şimdi veriyi ekranda göstermek gerekiyor. Uygulamanın ana sayfasına bir tane grid kontrolü ekleyelim. Grid kontrolünün DataSourceId özelliğinden “<New Data Source>” seçeneğini seçelim. Karşınıza aşağıda ki veri kaynağı ayarlama ekranı gelecektir.

Ben örnek olarak Entity Framework alt yapısını kullanmak istediğim için Entity seçeneğini işaretledim. Sonra ki sayfada projede ki veri kaynaklarının listesi gelmektedir. Oluşturduğumuz entity data modeli listeden seçelim.

Sonra ki sayfada gride hangi tabloyu bağlamak istediğimizi sormaktadır. People tablosunu seçelim.

Artık uygulamayı çalıştırıp önyüzden kayıtları değiştirebiliriz.

Sonuç

SQL CE kolayca veritabanı depolamasını etkinleştirmek için kullanabileceğiniz ücretsiz, gömülü, veritabanı motoru sağlar.

SQL CE ile yapılan çalışmaları isterseniz kolayca uygulamanın herhangi bir kodu değiştirmek zorunda kalmadan SQL Server veya SQL Azure üzerine taşıyabilirsiniz. Yapmanız gereken sadece uygulama yapılandırma dosyasında <connectionString> değerini değiştirmektir. Böylece uygulama geliştirmek için gerekli küçük gömülü veritabanı çözümü esnekliği sağlanmış olmaktadır.

22 Ocak 2011 Cumartesi

Genişletilebilir Uygulamada Hata Yakalama ve İzleme

Problem

Genişletilebilir (Composite) uygulamalar bir birine bağlı parçalardan oluşmaktadır. Eğer bir parça oluşturulurken parçanın ihtiyaç duyduğu eksik bir bağımlılık var ise uygulama hata alacaktır.

Yukarıda A örneğinin oluşturulması sırasında önce A'nın bağımlılıkları oluşturuluyor. C parçası [Import] edilmiş fakat uygulama içinde C parçasını [Export] eden her hangi bir kontrat bulunmamaktadır. Çalışma anında A parçasını üretmeye çalıştığımızda C'nin [Export] kontratı olmadığından dolayı hata üretilecektir.

Hatalar çalışma anında ortaya çıkacağı için oluşan hataları raporlamalı ve temel sorunu bulmalısınız.

Taşıyıcıyı Yazdırma

Örneğimize geri dönersek A örneği B ile bağımlı ve B ise C ile bağımlı ama uygulamada C örneğini [Export] eden bir kontrat bulunmamaktadır. Yani aşağıd ki şekilde bir kodlama olduğunu varsayalım.

[cs]

[Export ]
 public  class  ClassA 
 {
     [Import ] private  ClassB  _classB ;
 }
 [Export ]
 public  class  ClassB 
 {
     [Import ]
     private  ClassC  _classC ;
 }
 public  class  ClassC 
 {
 }

Taşıcının içeriğini bir stream örneğine yazdırmak için mef.codeplex.com adresinde ki Microsoft.ComponentModel.Composition.Diagnostics.dll assembly dosyasına ihtiyacınız var. Burada yer alan CompositionInfo ve CompositionInfoTextFormatter sınıfları ile taşıyıcının tüm içeriğini bir stream örneğine aktarabilirsiniz.

[cs]

var  compositionInfo = new  CompositionInfo (_aggregateCatalog , _compositionContainer );
 CompositionInfoTextFormatter .Write (compositionInfo, Console .Out );

Yukarıda bir katalog üzerinde ki tüm tanımlamaları çalışma anında Console yazan kod blogu bulunmaktadır. Örneğimie ait çıktı ise aşağıdaki gibi oluşacaktır.

Çıktıyı incelersek katalog üzerinde iki tane [Part] bulunduğunu görüyoruz. A için [Export] tanımının ve [Import] B tanımının olduğunu fakat [Import] B tanımının geçersiz olduğunu görüyoruz. Part B için [Primary Rejection] ifadesini görüyoruz bu ifade bize temel sorunun B parçasında olduğunu gösteriyor. B parçasında da [Import] C tanımını görüyoruz. Hatanın detayını okuduğumuzda [Import] C için geçerli bir [Export] kontratının bulunmadığını görmekteyiz. Böylelikle sorunu tesbilt etmiş olduk. C örneğine ait bir [Export] kontratına ihtiyacımız var.

Sonuç

Böylelikle genişletilebilir (Composite) uygulama yazma konulu serinin sonuna gelmiş olduk. Katalog ve taşıyıcı kavramlarını gördük. [Export] kontratlarımıza nasıl Metadata etiketleri verileceğini ve bu etiketleri kullanarak ihtiyacımız olan parçaları nasıl bulabileceğimizi gördük. Parçaların hayat döngüsünü yönetmeyi inceledik. Son olarakda genişletilebilir uygulamada hata yakalama özelliklerini inceledik.

Genişletilebilir uygulama alt yapısı (System.ComponentModel.Composition) bilinen IoC alt yapılarında (Unity, Structure Map,vb ) farklıdır. IoC alt yapıları derleme anında bağımlılıkları bilmektedir. .NET Composition ile bağımlılıklar çalışma anında keşif edilmektedir. Composition bilinmeyen bağımlılıkları yönetirken IoC ile bilinen bağımlılıklar yönetilmektedir.

Genişletilebilir uygulamalar uzun geliştirme süreci olan projelerde daha çok tercih edilmektedir. Bir sonraki yazı dizisinde genişletileblir MVC 3 uygulaması ve genişletilebilir WCF uygulaması üzerinde durucağız.

21 Ocak 2011 Cuma

Derinlemesine Genişletilebilir Uygulama Yazma

Genişletilebilir uygulamaların asıl gücü tak-çalıştır desteğidir. Bir önce ki yazıda ele aldığımız katalog ve taşıyıcı kavramlarının çalışma anında tak-çalıştır modüllerini nasıl takip ettiğini ve kullandığını inceliyoruz.

Çalışma Zamanı Desteği

Çalışma anında uygulamazın tak-çalıştır modülleri nasıl yönettiğini görelim. Öncelikle katalog ve taşıyıcıyı oluşturalım.

[cs]

var  assemblyCatalog = new  AssemblyCatalog (Assembly .GetExecutingAssembly ());
 var  directoryCatalog = new  DirectoryCatalog (Path .Combine ("../.." , "Plugins" ));
 var  aggregateCatalog = new  AggregateCatalog (assemblyCatalog, directoryCatalog);
 _compositionContainer  = new  CompositionContainer (aggregateCatalog);

DirectoryCatalog örneğinin Plugins dizinini dinlediğini görüyorsunuz. DirectoryCatalog siz yenilemedikçe dizin içinde ki modülleri tekrar yüklemeyecektir. Plugins dizinini dinlemeli ve oluşan değişikliklerde DirectoryCatalog örneğinizi yenilemelisiniz.

[cs]

// Plugins dizinini takip et 
 var  watcher = new  FileSystemWatcher (directoryCatalog.FullPath )
 {
     Filter  = "*.dll" ,
     EnableRaisingEvents  = true 
 };
 // dizindeğişince katalogu yenile
 watcher.Changed  +=  (s, e) => directoryCatalog.Refresh ();

Yukarıda ki kodlama ile Plugins dizininde ki dosyalar değiştiğinde katalogumuzu yeniliyoruz. Katalog ilgili dizinde ki tüm assembly dosyalarına bakarak [Export] tanımlarını almaktadır. Basit bir uygulama ile çalışma anında katalogun nasıl değiştiğini görelim. Elimizde karakter dizisi dönen bir ISeriesFactory interface var.

[cs]

public  interface  ISeriesFactory 
 {
     ///  
     ///   seri oluştur 
     ///  
     /// dizide olması gereken eleman sayısı       /// oluşturdupu seriyi dönderir       List  Create (int  numberOfElement);  }

Bu interface ait uygulama yazmak istiyoruz. Fakat interface uygulama sınıfları çalışma anında eklenecekler. Aşağıdakine benzer bir önyüz hazırlıyoruz. Sol tarafta katalogda yer alan ihraç [Export] tanımları yer alacak. Sağ tarafta ise katalogda yer alan ISeries interface’sini uygulayan sınıflar listelenmiş. Seçilen sınıfı çağıracak olan birde buton eklenmiş durumdadır.

Uygulamayı çalıştırıp uygulamanın dinlediği Plugins dizinine ISeries interface’sini uygulayan assembly dosyalarını kopyalıyoruz. Katalog listesinin doğrudan güncellendiğini görürüz. Bir tane ISeries uygulaması seçip serinin eleman sayısını girelim ve çalıştır diyelim.

ContractName ve MetaData

Uygulama katalogu içinde birden çok ISeriesFactory uygulaması var. Bu durumda elimizde ki parçalar arasında sorgular çalıştırarak istediğimiz parçayı bulmamız gerekmektedir. [ExportMetadata] niteliği ile katalog üzerinde arama yapabilmek için[ Export] tanımlarımıza etiket değerleri verebilmekteyiz. Parçalara ContractName değeri ekleyerek doğrudan istediğimiz parçayı içe aktarabilir veya kendimiz özel sorgular yazarak aradığınız parçayı taşıyıcı üzerinde bulabiliriz.

[cs]

[Export ("Fibanacci" ,typeof  (ISeriesFactory ))]
 public  class  Fibanacci  : ISeriesFactory
 private LazyGetFibanacciSeries(CompositionContainer compositionContainer)
 {
     Lazy  fibanacci =
         compositionContainer.GetExport ("Fibanacci" );
     return  fibanacci;
 }

Veya taşıyıcının sizin yerinize ContractName metadata değeri ile [Import] işlemi yapmasını sağlayabilirsiniz.

[cs]

[Import ("Fibanacci" )]private  ISeriesFactory  _fibanacci ;

Yukarıda ki tanımlama katalog üzerinde aşağıdaki gibi saklanmaktadır.

Görüldüğü üzere yukarıda ki [Export] tanımına ait bir Meradata dizisi bulunmaktadır. [Export] tanımın ilk parametresinde verdiğimiz isim ContractName olarak saklanmaktadır. Metadata dizisinde ExportTypeIdentity etiketinin değeri ise [Export] yapılan tipin tam adıdır. Eğer bir [Export] kontratı için bir isim belirtmez iseniz ExportTypeIdentity değeri ContractName alanına atanacaktır. ContractName ve ExportTypeIdentity metadata değeri ile katalog üzerinde sorgulama yapabilirsiniz.

Buraya kadar her şey güzel ama matematik ve karakter serilerinin aynı listede görülmesini istemiyorsunuz. O zaman kendinize ait [ExportMetadata] etiketleri tanımlama zamanı gelmiştir.

[cs]

[Export ("Fibanacci" ,typeof  (ISeriesFactory ))]
 [ExportMetadata ("Series" ,"Math" )]
 public  class  Fibanacci  : ISeriesFactory
 [Export (typeof  (ISeriesFactory ))]
 [ExportMetadata ("Series" , "String" )]
 public  class  UpperLetters  : ISeriesFactory

Yukarda Prime serisinin bir matematik seri olduğunu benzer şekilde UpperLatter serinin bir karakter serisi olduğunu belirtiyoruz. Şimdi formumuza yeni bir combobox ekleyerek kullanıcıya seri türlerini gösterelim. Her seçilen seri türünde de seri uygulamaları listemizi güncelleyelim.

[cs]

private  void  cbTypes_SelectedIndexChanged (object  sender, EventArgs  e)
 {
     var  selectedType = cbTypes .SelectedItem .ToString ();
     var  seriesList = new  StringList ();
     _compositionContainer 
         .Catalog .Parts 
         .SelectMany (part => part.ExportDefinitions )
         .Where (c=>c.Metadata .ContainsKey ("Series" ) 
                 && c.Metadata ["Series" ].ToString () == selectedType)
         .ToList ()
         .ForEach (c=>seriesList.Add (c.ContractName ));
     sourceSeries .DataSource  = seriesList;
 }

Yukarıda görüldüğü üzere seçilen seri türüne göre seri uygulama sınıflarını listeletebiliyoruz. Fakat bu yöntemde benim eklenti sınıflarında verilen etiket değerlerini bilmem gerekiyor. Metadata nın adı “Series” olmalı değeri “Math” veya “String” olmalı şeklinde kurallarım var. Eklenti kodlanırken bu kurallarımı zorlayan bir şey yok. Benim ihtiyacım ISeriesFactory interface ile birlikte sınıflara Metadata bilgisini de dağıtmalı ve böylece beklenmedik metadata değerleri ile karşılaşmamalıyım. O halde yeni bir Metadata sınıfı yazalım ve bu Metadata ya sahip ISeriesFactory sınıfları ile birlikte çalışalım.

[cs]

public  enum  SeriesType  {Math, String}
 public  interface  ISeriesFactoryMetadata 
 {
     SeriesType  SeriesType  { get ;  }
     string  Name  { get ;  }
 }
 [MetadataAttribute ]
 [AttributeUsage (AttributeTargets.Class,AllowMultiple= false )]
 public  class  SeriesFactoryMetadataAttribute  
     : ExportAttribute , ISeriesFactoryMetadata 
 {
     public  SeriesFactoryMetadataAttribute () 
         : base (typeof (ISeriesFactory )) { }
     public  SeriesType  SeriesType  { get ; set ; }
     public  string  Name  { get ; set ; }
 }

Yukarıda [Export] kontratını türettiğimizi ve sınıfmıza [Metadata] özelliği eklediğimizi görüyorsunuz. Böylelikle [Export] kontratı tanımlarken [Export] ve metadatayı ayrı ayrı tanımlamak zorunda değiliz. Ayrıca Metadata string olarak girilen Metadata etiketlerimizin yerini ISeriesFactoryMetadata interface property'leri aldı. Şimdide uygulamamızda ISeriesFactoryMetadata'yı kullanalım.

[cs]

//[Export("Fibanacci",typeof (ISeriesFactory))] 
 //[ExportMetadata("Series", "Math")] 
 [SeriesFactoryMetadata (Name ="Fibannacci Series" , SeriesType =SeriesType.Math)]
 public  class  Fibanacci  : ISeriesFactory

Görüldüğü üzere kod çok daha anlaşılır oldu. Uygulamayıda ISeriesFactoryMetadata'yı kullanacak şekilde değiştirelim.

[cs]

private  void  btnCreate_Click (object  sender, EventArgs  e)
 {
     var  selectedType = (SeriesType ) cbTypes .SelectedItem ;
     var  selectedSeries = cbSeries .SelectedItem .ToString (); 
     var  series =  _series .First (c => 
         c.Metadata .SeriesType  == selectedType 
         && c.Metadata .Name  == selectedSeries).Value ;
     var  list = new  StringList ();
     list.AddRange (series.Create (int .Parse (txtNumber .Text )));
     sourceResult .DataSource  = list;
 }
 [ImportMany ] private  Lazy [] _series ;
 private  void  cbTypes_SelectedIndexChanged (object  sender, EventArgs  e)
 {
     var  selectedType = (SeriesType ) cbTypes .SelectedItem ;
     var  seriesList = new  StringList ();
     _series 
         .Where (c=>c.Metadata .SeriesType  == selectedType)
         .ToList ()
         .ForEach (c => seriesList.Add (c.Metadata .Name ));
     sourceSeries .DataSource  = seriesList;
 }

Uygulama geliştirici olarak eklentilernden geleçek olan Metadata bilgilerimi güçlü bir tipe çekmiş oldum. Eklenti geliştiricilerede uygulama içinde gerekli olan Metadata bilgilerine doğrudan ulaşmış oldular.

Neden SeriesFactoryMetadataAttribute sınıfnıda bir interface uygulamak zorundadır ? Compositon geri tarafta metadata sınıfı için hala bir anahtar-değer sözlüğü üretmektedir. Doğrudan SeriesFactoryMetadataAttribute sınıfnı [Import] ile almak için SeriesFactoryMetadataAttribute sınıfının üretilen sözlüğü constractor parametresi olarak alması gerekmektedir. Daha kolay yöntem ise SeriesFactoryMetadataAttribute sınıfın bir interface uygulaması ve interface üzerinde ki property değerlerinin composition tarafından okunmasıdır.

Parçaların Hayat Döngüsünü Belirlemek

Ben asal sayıları hesaplayan dizinin her çağrılmada tekrar hesaplama yapmasını istemiyorum. Çünkü bu hesapla uzun süremektedir. Bu sınıfın uygulamada sadece bir defa oluşturulmasını istiyorum. Prime sınıfı ürettiği asay sayıları saklasın ve mümkün oldupunca az hesapla yapsın istiyorum. Taşıyıcıya bu sınıfı her çağrıda oluşturma sadece bir defa oluştur ve kullan demem gerekiyor.

[cs]

[SeriesFactoryMetadata (Name  = "Prime Series" , SeriesType  = SeriesType.Math)]
 [PartCreationPolicy (CreationPolicy.Shared)]
 public  class  Prime  : ISeriesFactory

Böylece Prime sınıfını paylaşımlı/Shared işaretleyerek kullandığım taşıyıcı üzerinde aynı anda sadece bir tane örneği bulunacağını belirtiyoruz. Eğere bir nesnenin her erişimde yeniden oluşturulmasını istiyor iseniz NonShared olarak işaretlemeniz gerekmektedir. Fibanacci serisinde nasıl kullanılması gerektiğini [Import] anında karar verilmesini istiyorum. Yani Fibanacci serisinin [Import] eden parça isterse taşıyıcı üzerinde mevcut olan örneği isterse yeni bir örneği kullanabilir.

[cs]

[SeriesFactoryMetadata (Name  = "Fibannacci Series" , SeriesType=SeriesType.Math)]
 [PartCreationPolicy (CreationPolicy.Any)]
 public  class  Fibanacci  : ISeriesFactory
 [Import("Fibanacci", RequiredCreationPolicy = CreationPolicy.NonShared )]
 private  ISeriesFactory  _fibanacci ;

Composition aksi belirtilmedikce [Export] kontratı olarak Any, [Import] kontratı olrakta Shared kullanmaktadır. Yani aksi belirtilmedikce taşıyıcı üzerinde aynı [Export] kontratı için bir tane parça var olacaktır.

Katalogu Çalışma Zamanında Değiştirme

Fibanacci sınıfın yeni versiyonunu yazdık. Şimdi uygulamayı dinamik olarak güncellememiz gerekiyor. Taşıyıcıya [Import] ettiğiniz parçalarda bir değişiklik olursa tekrar [Import] işlemini yapmasını söyleyebiliyoruz.

[cs]

[ImportMany (AllowRecomposition  = true )] 
private  Lazy [] _series ;

Ayrıca her hangi bir [Import] parçası taşıyan bir sınıfta IPartImportsSatisifiedNotification interface'i uyguladı ise tüm [Import] işlemleri bittikten sonra ImportCompleted olayı taşıyıcı tarafından çağırılmaktadır.

Sonuç

Böylelikle [Export] kontratlarımıza Metadata özelliklerini eklemeyi ve eklediğimiz Metadata özelliklerini bir sınıf yapısına getirmeyi inceledik. Taşıyıcının Metadata ve ContractName ile nasıl çalıştığını ve taşıyıcı üzerinde sorgulama yapmayı gördük. Daha sonra taşıyıcı üzerinde ki parçaların hayat döngüsünü yönetmeyi inceledik. Son olarakta çalışma anında değişen katalogdan haberdar olmayı gördük. Bu iki yazı ile her hangi bir .NET platformunda genişletilebilir uygulama yazacak duruma geldik. Bir sonra ki makalede Genişletilebilir Uygulamalarda Hata Ayıklama ve Takip etme özelliklerini inceliyor olacağız.

19 Ocak 2011 Çarşamba

NuGet:Microsoft Ve Open Source

NuGet (http://nuget.org/)  açık kaynaklı projeleri kolaylıkla bulup kendi projenize eklemeyi sağlayan MS tarafından (açık kaynak kodlu olarak) geliştirilen Visual Studio eklentisidir. NuGet tüm geliştiricilere bir online galeri sağlar. Geliştiriciler bu online galeriden istedikleri açık kaynak kodulu projeyi kendi projelerine referans olarak dahil edebilmektedir. Ayrıca NuGet tüm VS proje tipleri ile birlikte çalışabilmektedir.

Açık kaynak kodlu projeler kullanırken en büyük sıkıntı projeler arasında ki bağımlılıkları yönetmektir. NuGet proje bağımlılıklarını kendisi yönetmektedir. Aynı zamanda NuGet kullanıdığınız açık kaynak kodlu proje için gerekli web.config düzenlemesinide yapmaktadır. Kullandığınız projenin yeni versiyonu çıktığında yükseltme işinide kolaylıkla yapabilmektesiniz. Eğer kendi geliştirdiğiniz bir açık kaynak kodlu projeniz var ise galeriye ekleyip diğer geliştiricilerin kullanımınada sunabilirsiniz.

Kısacası NuGet ile amaç açık kaynak kodlu projeleri mümkün olduğu kadar kolay şekilde .NET projelerine dahil etmektir.

Nasıl Kullanılır ?

NuGet eklentisini kurdunuz ve çalışmaya hazırsınız. Her hangi bir proje üzerinde sağ tıkladığınızda paket ekleme menüsünün geldiğini görürsünüz.

Açılan formdan projenize dahil ettiğiniz paketleri ve online katalog üzerinde yer alan tüm açık kaynak kodlu projleri görebilirsiniz. Ayrıca  projenizde bulunan güncellenebilir durumda ki tüm paketleri de açılan formdan görebilmektesiniz. Ben bu örnekte online katalog üzerinde Moq projesini aradım. Moq projesini kendi projeme dahil ettim.

Moq projesini dahil ettikten sonra proje referanslarına Moq.dll geldiğini ve package.config dosyasınında projeye eklendiğini göreceksiniz.

NuGet ile eklediğiniz açık kaynak kodlu projeyi kaldırmak istediğinizde tekrar paket ekleme formuna gidip referansı projenizden kaldırabilirsiniz. Aynı zamanda NuGet Package Manager Console (View -> Other Windows -> Package Manager Console ) ile de paketleri yönetebilirsiniz.

Sonuç olarak NuGet ve Visual Studio ile açık kaynak kodlu projelerin kullanılması ve dağıtılması son derece kolay hale geldi.