C# ile Windows Server AppFabric Önbellek Etkileşimi

6. June 2010

Daha önceki makalelerimde sizlerle Windows Server AppFabric’in nasıl kurulacağını, geliştirme ortamının nasıl hazırlanacağını paylaşmıştım. Bu makalemde ise C# ile Windows Server AppFabric önbelleğiyle nasıl etkileşimde bulunacağınızı paylaşacağım.

Önbelleği kullanmaya başlamadan önce Sistem yöneticisi ile görüşerek Windows Server AppFabric Önbellek hizmetinin kurulu olduğu sunucuları ve bu sunuculardaki önbellek portunu öğrenmelisiniz. Makalemin geri kalanında Windows Server AppFabric önbellek kümesinin yerel makinenizde 22233 portu üzerinden hizmet veren tek bir sunucu ile hizmet verdiği varsayımıyla örnekleme yapacağım.

C# ile bir önbellek kümesine bağlanmak için öncelikle bir yapılandırma sınıfı (DataCacheFactoryConfiguration) oluşturmalı ve bu sınıf içerisinde en az bir önbellek kümesi üyesi önbellek sunucusu belirtmelisiniz.

var yapilandirma = new DataCacheFactoryConfiguration();
yapilandirma.Servers = new[] { new DataCacheServerEndpoint("localhost", 22233) };
yapilandirma.LocalCacheProperties = new DataCacheLocalCacheProperties();

Yukarıda da belirttiğim gibi burada en az bir önbellek sunucu bilgisi verilmesi yeterlidir. Windows Server AppFabric önbellek kütüphanesi arka planda bu bilgiyi kullanarak önbellek kümesine bağlanmakta ve kümedeki tüm önbellek sunucularının bilgilerini almaktadır. Yüksek erişilebilirlik gerektiren bir uygulama geliştiriyorsanız size tavsiyemse burada kümenizde bulunan mümkün olduğunca fazla sunucunun bilgisini vermeniz; çünkü tek sunucu bilgisinin verildiği senaryolarda ilk bağlantı sırasında bu sunucunun erişilemiyor olması halinde kümedeki diğer sunucular ayakta olsa bile uygulamanız onlardan haberdar olmadığı için kullanılamayacaktır.

Yukarıdaki kod satırları sonrasında geriye kümeye bağlanmak için yapmanız gereken tek satır kod kalıyor; önbellek fabrikasının oluşturulması.

var onbellekFabrikasi = new DataCacheFactory(yapilandirma);

Bu kod ile birlikte uygulamanı kümeye bağlanacak ve bu önbellek fabrikası üzerinden isteğiniz belleğe erişebileceksiniz. Kime geliştirme/üretim ortamlarında yukarıdaki kodlar işletilirken aşağıdaki hata ile karşılaşabilirsiniz;

ErrorCode<ERRCA0017>:SubStatus<ES0006>:Geçici bir hata var. Lütfen daha sonra yeniden deneyin. (Belirtilen Önbellek sunucularından biri veya birden fazlası, ağın ya da sunucuların meşgul olması nedeniyle kullanılamıyor. Kümede bu istemci hesabına güvenlik izni verildiğinden ve tüm önbellek konaklarında güvenlik duvarı aracılığıyla AppFabric Önbelleğe Alma Hizmeti'ne izin verildiğinden emin olun. Daha sonra yeniden deneyin.)

Genelde yazılımcının unutkanlığından kaynaklanan bu hatayı aldığınızda öncelikle önbellek hizmetinin çalıştığından emin olun. Hizmetin çalışmıyor olması durumunda sistem yöneticisi olarak başlatılan PowerShell oturumunda (Başlat menüsü –> Windows Server AppFabric –> Önbelleğe Alma Yönetimi Windows PowerShell) aşağıdaki komut ile önbellek kümesini başlatmalısınız;

start-cachecluster

Önbellek kümesi çalışıyor olmasına rağment bu hatayı alıyorsanız önbellek kümesine erişim yetkiniz olmayabilir. Bu durumda uygulamayı çalıştıran kullanıcının/makinenin önbelleğe erişim yetkisi olup olmadığı aşağıdaki Windows Server AppFabric PowerShell komutu ile kontrol edilmeli;

Get-CacheAllowedClientAccounts

Eğer bu komutun çalıştırılması sonucu gelen liste içerisinde kullanıcı/makine yer almıyorsa aşağıdaki komut vasıtasıyla önbellek kümesine erişim yetkisi verilmelidir;

Grant-CacheAllowedClientAccount [KULLANICI ADI]

Önbellek kümesi erişim yetkisi ile ilgili sıkıntıları çözdükten sonra aşağıdaki komut ile varsayılan önbellek alınarak üzerinde işlem yapılabilir.

var onbellek = onbellekFabrikasi.GetDefaultCache();

Tabi ki işlemlerinizi varsayılan önbellek dışında bir başka önbellek üzerinde de gerçekleştirebilirsiniz. Bu durumda kodumuz aşağıdaki gibi olmalıdır;

var onbellek = onbellekFabrikasi.GetCache("[ÖNBELLEK ADI]");

Yukarıdaki her iki kod parçacığına da performans açısında bakacak olursak; önbelleği bir önbellek fabrikasından almak maliyetli bir iştir. Bu sebeple de her ihtiyaç duyulduğunda önbellek fabrikasından bir örneğini almak yerine bir defa alarak bunu bir değişken içerisinde saklamak ve gerektiğinde bu değişken üzerinden işlem yapmak daha doğru olacaktır.

Önbellek’e erişmek için onbellek nesnemizi oluşturduktan sonra isterseniz şimdi de nasıl içerisine veri koyabileceğimizi görelim. Windows Server AppFabric önbelleğe konulacak olan nesneler için tek zorunluluk bu nesnelerin serilize edilebilir olarak işaretlenmiş olmasıdır. Bunun için sınıf tanımlamasının hemen üzerinde Serializable özniteliği kullanılmalıdır. Aşağıda tanımlamasını bulacağınız OnbellegeAtilabilirNesne sınıfı önbelleğe alınabilecek sınıfların tanımlarına bir örnek olup yazımın devamında bu sınıf üzerinden örnekleme yaparak konunun pekişmesine çalışacağım.

[Serializable]
public class OnbellegeAtilabilirNesne {
    public string BirStringIfade { get; set; }
    public int BirIntIfade { get; set; }
}

Görüldüğü gibi basit bir tanımlamaya sahip olan OnbellegeAtilabilirNesne sınıfı bir string ve bir int özellikten oluşmaktadır.

Windows Server AppFabric önbelleğe yeni bir nesne koymak için öncelikle ilgili sınıfın bir örneğini almalı ve bu örnek için tekil bir anahtar belirlemelisiniz. Ardından önbellek nesnesi üzerindeki Add metodu yardımıyla nesnenizi önbelleğe ekleyebilirsiniz;

var ornekVeri = new OnbellegeAtilabilirNesne { 
    BirStringIfade = "Test verisi", 
    BirIntIfade = 1 
};

var ornekVeriSurumu = onbellek.Add("testAnahtari", ornekVeri);

Add metodu ile ilgili dikkat etmeniz gereken bir nokta, bu metodu kullanarak bir önbelleğe aynı anahtarla ikinci bir değer eklenemeyecek olmasıdır. Eğer yukarıdaki kodu arka arkaya iki kez çalıştıracak olursanız aşağıdaki hatayı aldığınızı göreceksiniz;

ErrorCode<ERRCA0008>:SubStatus<ES0001>:Önbellekte zaten varolan bir Anahtar ile nesne oluşturulmaya çalışılıyor. Önbellek, nesneler için yalnızca benzersiz Anahtar değerlerini kabul eder.

Ancak; eklediğiniz ilk değer zamanının dolmasıyla birlikte hafızadan silinirse ikinci kez eklediğinizde bu hata mesajını almazsanız.

Önbelleğe değer ekleme sırasında kullanılan Add metoduna alternatif olarak Put metodu da kullanılabilir. Üstelik Add metodundan farklı olarak Put metodunda aynı anahtar ile ikinci bir değer eklenmesi durumunda hata almazsınız. Put metodu aynı değerle ikinci bir anahtar eklenmeye çalışıldığında önceki değerin üzerine yeni değeri yazar.

var ornekVeri = new OnbellegeAtilabilirNesne { 
    BirStringIfade = "Test verisi", 
    BirIntIfade = 1 
};

var ornekVeriSurumu = onbellek.Put("testAnahtari", ornekVeri);

Bu iki metodtan hangisi kullanacağınız iş mantığınıza göre değişecektir.

Önbelleğe eklenmiş verilere Get metodu kullanılarak erişilebilir. Aşağıdaki kod parçacığı daha önceden önbelleğe testAnahtari anahtarı ile eklediğimiz veriye nasıl erişebileceğimizi göstermektedir;

OnbellegeAtilabilirNesne ornekVeri;

if ((ornekVeri = (OnbellegeAtilabilirNesne)onbellek.Get("testAnahtari")) != null) {
    MessageBox.Show(string.Format(@"'testAnahtari' ahtarıyla saklanmış veri önbellekten okundu :
        BirStringIfade : {0} 
        BirIntIfade : {1}", 
        ornekVeri.BirStringIfade, 
        ornekVeri.BirIntIfade));
}
else {
    MessageBox.Show("önbellekte 'testAnahtari' anahtarı ile bir veri bulunamadı");
}

Add, Put ve Get metodları aksi belirtilmediği sürece küme tarafında yonetilen sitem regionlarında işlem yapmaktadır. İstenirse bu metodların işlem yapılmak istenen region’ı belirtebileceğiniz overload’ları da kullanılabilir. Aşağıdaki kod parçacığı ile Add ya da Put ile eklediğiniz verilerin hangi sistem alanında yer aldığını bulabilirsiniz;

var testAnahtariBolgeAdi = onbellek.GetSystemRegionName("testAnahtari");

Önbelleğe eklediğiniz bir veriyi silmek isterseniz de onbellek nesnesi içerisinde yer alan Remove metodunu kullanabilirsiniz;

onbellek.Remove("testAnahtari");

Görüldüğü gibi Windows Server AppFabric Önbellek işlemleri daha önceki makalelerimde sizlerle paylaştığım mimariyi mümkün olduğunda saklayarak geliştiricilere mümkün olan en basit şekilde bir arayüz sağlamıştır. Pek çok işlem bir kaç basit metod çağrısı ile yapılabilir. Buna bir başka örneği aşağıdaki region oluşturarak bu region içerisine veri eklemeyi göstern kod parçacığında bulabilirsiniz;

onbellek.CreateRegion("testBolgesi");
onbellek.Add("testAnahtari1", new OnbellegeAtilabilirNesne {
    BirStringIfade = "Test verisi 1",
    BirIntIfade = 1
}, "testBolgesi");

onbellek.Add("testAnahtari2", new OnbellegeAtilabilirNesne {
    BirStringIfade = "Test verisi 2",
    BirIntIfade = 1
}, "testBolgesi");

onbellek.Add("testAnahtari3", new OnbellegeAtilabilirNesne {
    BirStringIfade = "Test verisi 3",
    BirIntIfade = 1
}, "testBolgesi");

Şimdiye kadar anlattıklarım Windows Server AppFabric önbelleğini kullanan uygulamalar geliştirmenizde hızlı bir başlangıç olacaktır. Temel işlemler olan veri ekleme, değiştirme ve çıkarmanın yanı sıra bir bölge oluşturup aynı işlemleri bu bölge üzerinde gerçekleştirebilirsiniz.

Windows Server AppFabric önbelleği yukarıda anlattığım istemci kütüphanesi sayesinde önbellek kümesi ile haberleşebilmektedir. Bu haberleşme de arka planda WCF altyapısını kullanmaktadır. Yapılan bu işlemler ağ üzerinde gidip geldiği için bir maliyeti vardır. Bir önbelleği ilk kez ayağa kaldırırken programsal olarak yaptıklarımızı ele alalım; önbellekten veri çekerken kullandığımız her Get metod çağrısında bir WCF isteği oluşmakta, bu durumda büyük ölçekli verileri getirirken oluşacak yükü sanırım anlatmama gerek yok. Windows Server AppFabric önbelleği tasarlanırken bu gibi senaryolar da göz önüne alınarak toplu veri çekilmesi için bir metod eklemiştir; BulkGet. Belirli bir bölge için vereceğiniz anahtarlar çekilerek uygun veriler size toplu olarak iletilmektedir. Aşağıda bir önceki örnekte oluşturduğum ve içerisine veri eklediğim testBölgesi içerisindeki verilerin BulkGet ile sorgulanmasına dair örnek bir kod parçacığı bulabilirsiniz;

var ornekVeriler = onbellek.BulkGet(new[] { 
    "testAnahtari1",
    "testAnahtari2", 
    "testAnahtari2" }, "testBolgesi");

Dağıtık bir ortamda önbellek hizmeti sunan Windows Server AppFabric’in sunduğu bir diğer özellikte güvenli güncellemedir. Güncellenmesi istenen nesne, istenirse küme içerisinden alınırken kilitlenmekte, bu sayede de aynı anda tek bir istemci nesneye erişerek güncelleyebilmektedir. Güvenlik güncelleme amacıyla Windows Server AppFabric önbellek bize  Unlock, GetAndLock, PutAndUnlock gibi metodlar sunmaktadır.

Aşağıdaki örnek kod parçacığında, daha önceden testAnahtari anahtari ile önbelleğe eklemiş olduğumuz nesne GetAndLock metodu kullanılarak kilitlenerek değeri alınmış ve PutAndUnlock metodu ile güvenli bir şekilde güncellenerek kilit kaldırılmıştır.

DataCacheLockHandle handle;
var onbellegeAtilabilirNesne = onbellek.GetAndLock("testAnahtari", 
    TimeSpan.FromSeconds(1), 
    out handle) as OnbellegeAtilabilirNesne;

onbellegeAtilabilirNesne.BirStringIfade = "Değiştirilmiş Veri";

onbellek.PutAndUnlock("testAnahtari", onbellegeAtilabilirNesne, handle);

Dikkat edecek olursanız bu örnekte nesne GetAndUnlock ile değeri alınıp önbellekte kilitlenirken parametre olarak TimeSpan kabul etmekte. TimeSpan kullanarak belirttiğimiz bu süre nesnenin önbellek tarafından ne kadar süreyle kilitli tutulacağını belirtir. Bu süre içerisinde nesne kilidi kaldırılmazsa önbellek otomatik olarak kaldıracaktır. Bu yöntem sayesinden olası istemci bağlantı problemleri sonucunda nesnenin sonsuza kadar kilitli kalmasının önüne geçilmiş olur.

Kilitlenmiş bir nesne üzerinde yapılan işlemler sonrasında herhangi bir güncelleme yapılmadan kilidin kaldırılması istenirse PutAndUnlock yerine Unlock metodu kullanılabilir. Unlock metodu veri güncellemesi yapmayıp sadece mevcut kilidi kaldıracaktır.

Not : GetAndLock ile bir anahtara ait değeri almaya ve kilitleme çalıştığınızda kaydı olmayan bir anahtar kullanırsanız aşağıdaki hata mesajını alırsınız;

ErrorCode<ERRCA0006>:SubStatus<ES0001>:Başvurulan Anahtar yok. Hatayı gidermek için bir Anahtarı temel alan nesneler oluşturun.

Digg It!DZone It!StumbleUponTechnoratiRedditDel.icio.usNewsVineFurlBlinkList

Türkçe, C#, AppFabric Cache ,

C# ile AppFabric Cache Yönetimi - 2

9. May 2010

Önceki makalemde sizlerle C# üzerinden AppFabric Cache PowerShell komutlarını nasıl kullanabilceğinizi paylaşmıştım. Örnek kod üzerinden nasıl önbellek kümesine bağlanabileceğinizi ve yeni bir önbellek oluşturabileceğinizi anlatmıştım;

var initialSessionState = InitialSessionState.CreateDefault();
initialSessionState.ImportPSModule(new[] { "DistributedCacheAdministration" });
initialSessionState.ThrowOnRunspaceOpenError = true;

var cacheRunspace = RunspaceFactory.CreateRunspace(initialSessionState);

cacheRunspace.Open();

var pipe = cacheRunspace.CreatePipeline();

pipe.Commands.Add("Use-CacheCluster");

var newCacheKomutu = new Command("New-Cache");
newCacheKomutu.Parameters.Add("CacheName", "test");
pipe.Commands.Add(newCacheKomutu);

var sonuc = pipe.Invoke();

PowerShell komutlarımızın çalışacağı bir çalışma alanı içerisinde bir pipeline oluşturmuş ve son satırda bu pipeline’ı çalıştırmıştık. Bu örnekte dikkat edecek olursanız sonuc değişkeni içerisinde System.Management.Automation.PSObject barındıran 0 boyutlu bir koleksiyon bulunmamakta; çünkü pipeline içerisinde çalıştırdığımız AppFabric Cache komutlarının bir geri dönüş değeri bulunmamakta.

Bu makalemde AppFabric Cache komutlarından gelen bilgileri C# içerisinde nasıl kullanabileceğinizi anlatmaya çalışacağım.

Önceki makaledeki kodu ilk çalıştırmanız sonrasında alınan aşağıdaki ekran görüntüsünde PowerShell oturumunda, DistributedCacheAdministration modülünün yüklendiğini ve bilgisayarın bağlı olduğu önbellek kümesinin kullanıldığını, ardından da bu önbellek kümesinde bulunan önbelleklerin listesinin alındığını göreceksiniz.

Get-Cache

Görüldüğü gibi test adıyla bir önbellek oluşturulmuş durumda.

Aynı kodu ikinci defa çalıştıracak olursanız, verdiğiniz isimde bir önbelleğin küme içerisinde daha önceden oluşturulmuş olduğuna dair “ErrorCode<ERRCAdmin011>:SubStatus<ES0001>:Specified Cache is already present in cluster.” şeklinde bir hata mesajı alırsınız. Bu durumdan kaçınmak için öncelikle küme içerisindeki önbellekleri listeyip içlerinde oluşturmak istediğimiz önbelleğin olup olmadığına bakmalıyız. Eğer yoksa yeni bir önbellek oluşturmalıyız.

pipe.Commands.Add("Get-Cache");

var sonuc = pipe.Invoke();

ilk kodumuzdaki diğer komutları çıkartaran sadece “Get-Cache” kullandığımız yukarıdaki örnek kod bize içerisinde sadece tek bir elemanı olan bir PSObject listesi verecektir. Quick Watch ile sonuc değişkenine bakacak olursak 0. indeksteki PSObject içerisinde CacheInfo türünden bir nesne barındırdığını görürüz. Bu nesneye PSObject içerisindeki BaseObject özelliğinden ulaşabiliriz.

Sonuc QuickWatch

Aşağıdaki kodla, gelen sonuçlar arasında test önbelleği olup olmadığını kontrol ederek bu bilgiyi daha sonra kullanmak üzere bir boolean içerisinde tutuyoruz.

var testOnbellegiBulundu = false;
foreach (var cacheInfoPSObject in sonuc)
{
	var cacheInfo = ((CacheInfo)cacheInfoPSObject.BaseObject);

	if (cacheInfo.CacheName == "test")
	{
		testOnbellegiBulundu = true;
		break;
	}
}

devamında ise eğer test önbelleği yoksa ilk örneğimizdeki oluşturma kodunu çalıştırıyoruz;

if (!testOnbellegiBulundu)
{
	pipe = cacheRunspace.CreatePipeline();

	var newCacheKomutu = new Command("New-Cache");
	newCacheKomutu.Parameters.Add("CacheName", "test");
	pipe.Commands.Add(newCacheKomutu);

	pipe.Invoke();
}

AppFabric Cache PowerShell komutları ve bu komutları çalıştırdığımızda dönecek nesneleri get-help komutu yardımıyla öğrenebiliriz. Örneğin; Get-Cache komutu hakkında aşağıdaki şekilde bilgi alabilirsiniz;

Get-Help Get-Cache -full

Get-Help Get-Cache -full

Gelen yardım bilgilerinin sonunda komutun çıktısı hakkındaki bilgiler “OUTPUTS” başlığı altında verilmektedir.

Şimdiye kadar ki anlattıklarımı toparlayacak olursak, kodumuz aşağıdaki hale gelmiştir;

var initialSessionState = InitialSessionState.CreateDefault();
initialSessionState.ImportPSModule(new[] { "DistributedCacheAdministration" });
initialSessionState.ThrowOnRunspaceOpenError = true;

var cacheRunspace = RunspaceFactory.CreateRunspace(initialSessionState);

cacheRunspace.Open();

var pipe = cacheRunspace.CreatePipeline();

pipe.Commands.Add("Use-CacheCluster");

pipe.Commands.Add("Get-Cache");

var sonuc = pipe.Invoke();

var testOnbellegiBulundu = false;
foreach (var cacheInfoPSObject in sonuc)
{
    var cacheInfo = ((CacheInfo)cacheInfoPSObject.BaseObject);

    if (cacheInfo.CacheName == "test")
    {
        testOnbellegiBulundu = true;
        break;
    }
}


if (!testOnbellegiBulundu)
{
    pipe = cacheRunspace.CreatePipeline();

    var newCacheKomutu = new Command("New-Cache");
    newCacheKomutu.Parameters.Add("CacheName", "test");
    pipe.Commands.Add(newCacheKomutu);

    pipe.Invoke();
}

Şimdiye kadar anlatılanlarla oluşturmak istediğimiz önbelleğin varlığı kontrol ettikten sonra yoksa oluşturmayı, çalıştırılan PowerShell komutlarının sonuçlarını cast ederek kullanmayı öğrendik; fakat bazı AppFabric Cache komutlarında malesef ki hayat biz yazılım geliştiriciler için o kadar kolay olmuyor.Bazı komutların sonuç sınıfları barındıkları assembly içerisinde internal olarak tanımlanmaları nedeniyle dışarıdan kullanılamayabilir. Get-CacheStatistics komutunu ele alalım;

Get-Help Get-CacheStatistics -full

Get-Help Get-CacheStatistics -full

Bu komut bize iki tür sonuç döndürebilir;

  1. Microsoft.Data.Caching.NamedCacheStats (gerçekte Microsoft.ApplicationServer.Caching.NamedCacheStats)
  2. Microsoft.Data.Caching.HostCacheStats (gerçekte Microsoft.ApplicationServer.Caching.HostCacheStats)

 

Fakat bunlar assembly dışından erişilemeyen sınıflar (internal class) olduğu için kodumuz içerisinde doğrudan kullanamayız. Peki bu durumda ne yapmalıyız?

Bu durumda PSObject.BaseObject NamedCacheStats ya da HostCacheStats sınıflarına dönüştürülemeyeceği için imdadımıza PSObject’in Properties özelliği yetişiyor. Properties yardımıyla PSObject’in barındırdığı sonuç nesnesinin özelliklerine erişilebilir.

var psObject = sonuc[0];

var ItemCount = (long)psObject.Properties["ItemCount"].Value;
var MissCount = (long)psObject.Properties["MissCount"].Value;
var RegionCount = (long)psObject.Properties["RegionCount"].Value;
var RequestCount = (long)psObject.Properties["RequestCount"].Value;
var Size = (long)psObject.Properties["Size"].Value;
Digg It!DZone It!StumbleUponTechnoratiRedditDel.icio.usNewsVineFurlBlinkList

Türkçe, C#, AppFabric Cache ,

C# ile AppFabric Cache Yönetimi

8. May 2010

Daha önceki makalelerimde sizlere PowerShell üzerinden AppFabric Cache’i nasıl yönetebileceğinizi anlatmış, örnek bir AppFabric Cache PowerShell script’i paylaşmıştım. Bu makalemde sizlere C# üzerinden nasıl PowerShell’e ulaşabileceğinizi ve nasıl AppFabric Cache yönetim komutlarını kullanabileceğinizi anlatacağım.

C# üzerinden PowerShell komutları çalıştırabilmek için öncelikle System.Management.Automation kütüphanesine referans vermeniz gerekli. Bu kütüphaneyi aşağıdaki klasör altında bulabilirsiniz;

C:\Program Files\Reference Assemblies\Microsoft\WindowsPowerShell\v1.0 (x32)
C:\Program Files (x86)\Reference Assemblies\Microsoft\WindowsPowerShell\v1.0 (x64)

Eğer bilgisayarınızda bu klasörü bulamıyorsanız PowerShell 2.0 SDK’sını kurmalısınız.

Referansını ekledikten sonra AppFabric Cache komutlarını çalıştırabileceğimiz bir PowerShell çalışma alanı oluşturmalıyız. Aşağıda bir PowerShell çalışma alanı oluşturma kodunu bulabilirsiniz;

var initialSessionState = InitialSessionState.CreateDefault();
initialSessionState.ThrowOnRunspaceOpenError = true;

var cacheRunspace = RunspaceFactory.CreateRunspace(initialSessionState);

cacheRunspace.Open();

Not: Yukarıdaki kodun derlenebilmesi için System.Management.Automation.Runspaces isim uzayı için using eklemelisiniz.

Bu kod ile oluşturduğunuz çalışma alını üzerinden bir pipeline oluşturarak bu pipeline içerisinde PowerShell komutlarını çalıştırabilirsiniz;

var pipe = cacheRunspace.CreatePipeline();
pipe.Commands.Add("[bir PowerShell komutu]");

var sonuc = pipe.Invoke();

PipeLine içerisine vereceğiniz PowerShell komutu yeni bir cache oluşturma olabileceği gibi (New-Cache), Önbellek kümesini yeniden başlatmakta olabilir (Restart-CacheCluster).

Konu hakkında daha fazla ilerlemeden önce, aynı PowerShell komut satırından yaptığımız gibi ihtiyacımız olan AppFabric Cache PowerShell modüllerini yüklememiz gerektiğini de belirtmeliyim. Aksi halde, yazacağımız komutlar PowerShell tarafından tanınmayacağı için hata mesajı alacağız. PowerShell modüllerini AppFabric çalışma alanına eklemenin en kolay yolu bu çalışma alanı eklenmeden hemen önce hazırladığımız SessionState içerisine ImportPSModule fonksiyonunu kullanarak yüklemek;

initialSessionState.ImportPSModule(new[] { "DistributedCacheAdministration", "DistributedCacheConfiguration" });

Her ne kadar yukarıdaki örnekte her iki AppFabric Cache modülü yüklenmiş de olsa; iş mantığınız gereği kullanacağınız AppFabric Cache komutları bunlarda sadece birine ihtiyaç duyabilir. Bu durumda sadece gerekli olan modülü yüklemeniz ihtiyacınızı karşılayacaktır.

Eğer AppFabric Cache Yönetimi ile ilgili bir/birkaç komut çalıştırmayı planlıyorsanız aynı PowerShell komut satırında olduğu gibi ilk çalıştırmanız gereken komut “Use-CacheCluster” olmalı. Eğer kodun çalıştığı bilgisayar bir önbellek kümesine dahil değilse ya da farklı bir önbellek kümesi için komut çalıştırmak istiyorsanız bu durumda “Use-CacheCluster” komutuyla birlikte bu önbellek kümesinin bağlantı bilgilerini de vermeniz gereklidir.

Oluşturduğumuz pipeline içerine birden fazla komut eklememiz durumda bu komutlar PowerShell içerisinde eklenme sırasına göre işletilecektir.

C# içerisinde AppFabric Cache PowerShell komutlarını çalıştırmaya en güzel örnek bir cache’in istemcilerce kullanılmaya başlanması öncesinde hazırlanması olacaktır. Bu durumda yeni cache-oluşturulması sonrası cache’in doldurulması gereklidir;

var newCacheKomutu = new Command("New-Cache");
newCacheKomutu.Parameters.Add("CacheName", "test");
pipe.Commands.Add(newCacheKomutu);

Yukarıdaki örnekte New-Cache komutu sadece zorunlu olan CacheName parametresi verilerek çağırılmıştır. Komutun işletilmesi sonucunda küme içerisinde test adıyla yeni bir önbellek oluşturulacaktır. Bu basit örnekten yola çıkarak sizde C# üzerinden kendi önbelleklerinizi oluşturabilirsiniz. New-Cache komutuna verebileceğiniz parametreleri ise PowerShell oturumda “get-help New-Cache” komutuyla göreceğiniz yardım ekranında bulabilirsiniz. Bu komut hakkında daha detaylı yardım almak için “get-help New-Cache –full” komutunu da kullanabilirsiniz.

Şimdiye kadar anlattıklarımı bir araya topladığımızda karşımıza aşağıdaki kod parçacığı çıkacaktır;

var initialSessionState = InitialSessionState.CreateDefault();
initialSessionState.ImportPSModule(new[] { "DistributedCacheAdministration" });
initialSessionState.ThrowOnRunspaceOpenError = true;

var cacheRunspace = RunspaceFactory.CreateRunspace(initialSessionState);

cacheRunspace.Open();

var pipe = cacheRunspace.CreatePipeline();

pipe.Commands.Add("Use-CacheCluster");

var newCacheKomutu = new Command("New-Cache");
newCacheKomutu.Parameters.Add("CacheName", "test");
pipe.Commands.Add(newCacheKomutu);

var sonuc = pipe.Invoke();

PowerShell içerisinden kullanabileceğiniz komutları aşağıdaki komut vasıtasıyla listeleyebilirsiniz;

Get-Command -module DistributedCacheAdministration

CommandList-DistributedCacheAdministration

Güncelleme [9 Mayıs 2010] : Yukarıdaki kodun çalışması sırasında DistributedCacheAdministration modülünün bulunamadığına dair hata alıyorsanız bu makaleden yardım alabilirsiniz.
Digg It!DZone It!StumbleUponTechnoratiRedditDel.icio.usNewsVineFurlBlinkList

Türkçe, C#, AppFabric Cache ,

Dinamik Oluşturulan Kodlara Debug Bilgisi Eklemek

20. April 2010

Daha önce yazdığım makalemde sizlere çalışma-zamanı dinamik kod oluşturmayı anlatmıştım. Yazım ardından aldığım bazı maillerde bu yöntemi kullanmaya başladıktan sonra ikinci bir ihtiyacın oluştuğunu gördüm; oluşturulan dinamik kodun Debug edilebilmesi. Okuyacağınız yazımda bu konuyu ele alarak dinamik kod oluşturma sürecinde yapacağınız ek kodlama ile nasıl debug edilebilir bir kod oluşturabileceğinizi göstermeye çalışacağım.

Öncelikle bir önceki makalemde ele aldığım ve dinamik oluşturulan nihayi kodu hatırlayalım;

public class DinamikSinif {
    public static void Main(string[] parametreler) {
        Console.WriteLine("Merhaba Dinamik Dünya");
    }
}

Basit bir sınıf tanımlaması, içerisinde yer alan Main metodunda sadece “Merhaba Dinamik Dünya” yazdırdığımız bu konsol uygulamasını aşağıdaki kodu kullanarak üretebilmiştik;

using System;
using System.Reflection;
using System.Reflection.Emit;
using System.Threading;
using System.Diagnostics;

namespace ReflectionEmitOrnegi {
    class Program {
        static void Main(string[] args)  {
            var assemblyAdi = new AssemblyName("MerhabaDinamikDunya");
            var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyAdi, AssemblyBuilderAccess.RunAndSave);
            var modul = assemblyBuilder.DefineDynamicModule("MerhabaDinamikDunya.exe");

            var typeBuilder = modul.DefineType("DinamikSinif", TypeAttributes.Public | TypeAttributes.Class);

            var metodbuilder = typeBuilder.DefineMethod("Main",
                MethodAttributes.HideBySig | MethodAttributes.Static | MethodAttributes.Public,
                typeof(void),
                new Type[] { typeof(string[]) });

            var ilOlusturucu = metodbuilder.GetILGenerator();

            //ilOlusturucu.Emit(OpCodes.Ldstr, "Merhaba Dinamik Dünya");
            //ilOlusturucu.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }));
            ilOlusturucu.EmitWriteLine("Merhaba Dinamik Dünya");

            ilOlusturucu.Emit(OpCodes.Ret);

            typeBuilder.CreateType();

            assemblyBuilder.SetEntryPoint(metodbuilder, PEFileKinds.ConsoleApplication);
            assemblyBuilder.Save("MerhabaDinamikDunya.exe");
        }
    }
}

İçinde bulunduğumuz domain içerisinde (AppDomain.CurrentDomain) yeni bir dinamik assembly tanımlayarak içinde oluşturduğumuz modüle sınıfımızı ve metodumuzu eklemiş, dosya sistemine kaydetmiştik.

Dinamik oluşturduğumuz bu kodu isterseniz aşağıdaki şekilde saklamadan önce/sonra programınız içerisinde kullanabilirsiniz;

var dinamikSinifType = typeBuilder.CreateType();
dinamikSinifType.GetMethod("Main").Invoke(null, new string[] { null });

 

Bu noktada, kimi zaman oluşan kodla ilgili problem yaşayabilirsiniz; acaba üretilen kod istediğiniz şekilde mi üretildi? Üretilen kod içerisinde bir noktada hatalı bir kullanım mı var? Ürettiğim kod zaman zaman hata fırlatmakta, neden?

Bu problemleri aşabilmek için kimi zaman saatlerce/günlerce uğraşmanız gerekebilir, problemin kaynağını bulamadığınız zamanlar dahi olabilir. Keşke bu kodu Visual Studio içerisinde yazdığınız diğer kodlar gibi debug edebilsediniz, programda ileri-geri gidip değişkenlerin değerlerini görüp değiştirebilseydiniz. Eğer bunları düşünüyor ve istiyorsanız, size iyi bir haberim var; System.Reflection.Emit içerisinde bunu da yapabilirsiniz.

Öncelikle oluşturmak istediğiniz kodu bir dosyada saklayın. Sakladığınız bu dosya herhangi bir uzantıya sahip olabilir .cs, .txt, .abc ya da istediğiniz başka bir uzantı. Bu makalede dosya uzantısının önemli olmadığını göstermek adına .txt uzantısını kullanacağım, MerhabaDinamikDunya.txt.

MerhabaDinamikDunya.txt

Projeme eklediğim bu kodu makalemin devamında kolayca debug edebilmek için çıktı klasörüne kopyalamak istiyorum. Bunu için Solution Explorer’da dosyaya ters tıklayıp properties seçeneği üzerinden özelliklerinin listelendiği ekrana gelip “Copy to Output Directory” özelliğini “Copy always” yapıyorum.

MerhabaDinamikDunya.txt Properties

Bu adımı sizinde takip etmeniz zorunlu değil. Benim yapmış olmamın tek nedeni kaynak kodunun bulunduğu dosyanın uzun uzadıya absolute path’ını vermek istememem. Dilerseniz sizde benimle aynı şekilde her derlemede kaynak kodu çıktı klasörüne kopyalatabilir ya da kaynak kodun absolute path’ini kullanabilirsiniz.

Dinamik bir kodun debug edilebilmesi için öncelikle tanımlandığı assembly’nin debug edilebilir olarak işaretlenmesi lazım. Bunu yapabilmek için DebuggableAttribute’ün kullanmanız gerekecektir. DebuggableAttribute’ünü kullanarak .net runtime’ın bir modüle ne şekilde davranacağını kontrol edebilirsiniz. Runtime üretilen kod içerisinde ekstra bilgiler taşıyabilir, hatta bu attribute’ün taşıdığı değerler doğrultusunda varsayılan olarak yapılan bazı optimizasyonları iptal edebilirsiniz. Burada dikkat edilmesi gereken nokta, bir kodun debug edilebilmesi için DebuggableAttribute’ün DefineDynamicModule metodu çağrılmadan önce tanımlanarak AssemblyBuilder içerisine attribute olarak eklenmiş olması gerektiğidir.

Aşağıdaki kod parçacığı yardımıyla AssemblyBuilder içerisinde kullanacağımız DebuggableAttribute’ü bizim için oluşturacak CustomAttributeBuilder’ı oluşturabilirsiniz.

var debuggableAttributeCtor = typeof(DebuggableAttribute).GetConstructor(new[] { typeof(DebuggableAttribute.DebuggingModes) });
var debuggableAttributeBuilder = new CustomAttributeBuilder(debuggableAttributeCtor, 
    new object[] { 
        DebuggableAttribute.DebuggingModes.DisableOptimizations | 
        DebuggableAttribute.DebuggingModes.Default 
});

Oluşturduğumuz bu CustomAttributeBuilder ile DebuggableAttribute’ü AssemblyBuilder içerisine eklemek ise aşağıdaki kodda görüldüğü kadar kolay;

assemblyBuilder.SetCustomAttribute(debuggableAttributeBuilder);

Dinamik bir assembly’nin debug edilebilmesi için ufak bir ayrıntı daha var; DefineDynamicModule ile modül tanımlamasını yaparken sembol bilgilerinin de üretilmesi gerektiğini belirtmeliyiz. Önceki makalemde sadece üretilecek olan dinamik modülün adını verdiğim DefineDynamicModule metodunu aşağıdaki şekilde yeniden düzenlemeliyiz;

var modul = assemblyBuilder.DefineDynamicModule("MerhabaDinamikDunya.exe", true);

Yukarıda sıraladığım adımlar sonrasında artık dinamik assembly’mizi debug edilebilir şekilde ayarlamış oluyoruz; ama işimiz henüz bitmedi. Öncelikle debug sembollerinin saklanacağı dokümanı (.pdb dosyası) hazırlamalı, ardından da bu dokümana debug bilgilerini eklemeliyiz.

Aşağıdaki satır debug sembollerinin saklanacağı dokümanı oluşturmamızı sağlayacaktır;

var document = modul.DefineDocument("MerhabaDinamikDunya.txt", SymLanguageType.CSharp, SymLanguageVendor.Microsoft, SymDocumentType.Text);

ModuleBuilder üzerinden ulaşılabilen DefineDocument metodu ilk parametre olarak bizden kaynak kodunun bulunduğu dosyayı isteyecektir. Makalemin başında oluşturduğu MerhabaDinamikDunya.txt dosya adını bu parametrede kullanıyorum. Burada oluşan assembly ile kaynak kodunun aynı klasörde bulunacağını varsaydığım için sadece kaynak kodu dosyasının adını vermekle yetindim; fakat daha öncede belirttiğim gibi istenirse burada absolute path’te kullanılabilir. Ardından vereceğiniz parametrelerle kullanılan programlama dilinin, dilin dağıtıcısının ve doküman türünün guid değerlerini vermemiz gerekmekte. Bu değerleri biliyorsanız guid’lerini oluşturup parametre olarak geçebilir ya da benim yaptığım gibi SymLanguageType, SymLanguageVendor ve SymDocumentType sınıfları içerisinde tanımlanmış static değerleri kullanabilirsiniz.

Bu adımları takip etmemiz ardında artık kodumuzu üretirken adım adım bu üretilen kodun kaynak kodumuz içerisinde hangi satırları kapsadığını belirtmeliyiz. Bu şekilde bir debugger debug edilen kod parçacığının kaynak kod içerisinde tam olarak nerede olduğu bilebilir. Bu bilgi aşağıdaki örnekte olduğu gibi MarkSequencePoint metodu yardımıyla verilebilir.

ilOlusturucu.MarkSequencePoint(document, 3, 9, 3, 52);

Burada verdiğimiz değerler bilginin hangi dokümana yazılacağı ve ilgili kodun kaynak kod içerisinde hangi satır/sütunda başlayıp hangi satır/sütunda bittiğini belirtmelidir. Yukarıdaki örnek üzerinden gidersek konsola “Merhaba Dinamik Dünya” yazdığım kodun bulunduğu 3.satır 9. kolon ve bitişi olan 3. kolon 52. satırı belirtmekteyiz.

Şimdiye kadar anlattıklarımı toparladığımızda ilk makalemdeki programa debug bilgileri aşağıdaki şekilde eklenebilmekte:

using System;
using System.Reflection;
using System.Reflection.Emit;
using System.Threading;
using System.Diagnostics;
using System.Diagnostics.SymbolStore;

namespace ReflectionEmitOrnegi
{
    class Program
    {
        static void Main(string[] args)
        {
            var assemblyAdi = new AssemblyName("MerhabaDinamikDunya");
            var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyAdi, AssemblyBuilderAccess.RunAndSave);

            var debuggableAttributeCtor = typeof(DebuggableAttribute).GetConstructor(new[] { typeof(DebuggableAttribute.DebuggingModes) });
            var debuggableAttributeBuilder = new CustomAttributeBuilder(debuggableAttributeCtor, 
                new object[] { 
                    DebuggableAttribute.DebuggingModes.DisableOptimizations | 
                    DebuggableAttribute.DebuggingModes.Default 
                });

            assemblyBuilder.SetCustomAttribute(debuggableAttributeBuilder);

            var modul = assemblyBuilder.DefineDynamicModule("MerhabaDinamikDunya.exe", true);

            var document = modul.DefineDocument("MerhabaDinamikDunya.txt", SymLanguageType.CSharp, SymLanguageVendor.Microsoft, SymDocumentType.Text);
        
            var typeBuilder = modul.DefineType("DinamikSinif", TypeAttributes.Public | TypeAttributes.Class);

            var metodbuilder = typeBuilder.DefineMethod("Main",
                MethodAttributes.HideBySig | MethodAttributes.Static | MethodAttributes.Public,
                typeof(void),
                new Type[] { typeof(string[]) });

            metodbuilder.DefineParameter(1, ParameterAttributes.None, "parametreler");

            var ilOlusturucu = metodbuilder.GetILGenerator();
            
            ilOlusturucu.MarkSequencePoint(document, 3, 9, 3, 52);
            //ilOlusturucu.Emit(OpCodes.Ldstr, "Merhaba Dinamik Dünya");
            //ilOlusturucu.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }));
            ilOlusturucu.EmitWriteLine("Merhaba Dinamik Dünya");

            ilOlusturucu.MarkSequencePoint(document, 4, 5, 4, 6);
            ilOlusturucu.Emit(OpCodes.Ret);

            var dinamikSinifType = typeBuilder.CreateType();

            assemblyBuilder.SetEntryPoint(metodbuilder, PEFileKinds.ConsoleApplication);
            assemblyBuilder.Save("MerhabaDinamikDunya.exe");

            dinamikSinifType.GetMethod("Main").Invoke(null, new string[] { null });

        }
    }
}

Yapmamız gereken sadece bu kadar. Bu adımları doğru şekilde takip ederseniz artık kodu çalıştırdığınızda çıktı klasöründe MerhabaDinamikDunya.exe dosyasının yanında debug sembollerinin bulunduğu MerhabaDinamikDunya.pdb dosyasının da oluştuğunıu görebilirsiniz.

Kodun debug edilip edilemediğini test etmek için makalemin başında belirttiğim kod içerisinde dinamik türümüzü oluşturarak çağırdığımız satıra bir break point ekleyebiliriz.

Breakpoint

Uygulamamızı Visual Studio içerisinden F5 ile debug edecek olursak bu satırdaki BreakPoint’e takıldığında F11 ile kod içerisine girmek istediğimizde Debugger’ımızın MerhabaDinamikDunya.txt içerisinde 3. satıra konumlandığını rahatlıkla görebiliriz.

MerhabaDinamikDunya.txt BreakPoint

Yukarıdaki ekran görüntüsünde Locals penceresine dikkat ederseniz, burada yerel değişkenlerimizin de bulunduğunu görebiliriz.

Artık bu kod içerisinde Cursor’u sürükleyerek debug edilen kod içerisinde debuggerın ileri-geri gitmesini sağlayabilir, değişken değerlerini değiştirebilir, hatta “İmmediate Window” içerisinde kod parçacıkları dahi çalıştırabilirsiniz.

Digg It!DZone It!StumbleUponTechnoratiRedditDel.icio.usNewsVineFurlBlinkList

Türkçe, C# ,

Çalışma-Zamanı Dinamik Kod Oluşturma

14. April 2010

Yazılım geliştiriciler olarak her zaman kullanıcıların ihtiyaçlarını karşılamak için derleme-zamanı yazmış olduğumuz kodları kullanırız; ama zaman zaman keşke kendi kendini yazan, çalıştıran programlarımız olsun dediğiniz olmuştur. İşte tam bu düşüncede olan tüm yazılım geliştiricilerin en seveceği C# namespace’i sanırım System.Reflection.Emit olacaktır. Bu makalemde sizlere bu namespace altında yer alan sınıflar yardımıyla çalışma-zamanında nasıl dinamik olarak assembly üretip çalıştırabileceğinizi anlatmaya çalışacağım.

Yazılım dünyasının en popüler örneği olan “Merhaba Dünya” örneği üzerinden System.Reflection.Emit namespace’ini kullanmayı örneklemeye çalışacağım. Aşağıda nam-î değer “Merhaba Dünya” örneğinin C# versiyonunu bulabilirsiniz. Aslında örneğimiz içerisinde kompleks bir kod yok, bir konsol uygulaması, ve sadece ekrana “Merhaba Dinamik Dünya” yazmakta.

public class DinamikSinif {
    public static void Main(string[] parametreler) {
        Console.WriteLine("Merhaba Dinamik Dünya");
    }
}

Yukarıdaki kod parçacığını incelediğimizde üç ana bölüm görmekteyiz; DinamikSinif adıyla bir sınıf tanımlaması, bu sınıf içerisinde yer alan ve string parametre listesini kabul eden Main adıyla statik bir metod (aynı zamanda uygulamamızın giriş noktası), konsola “Merhaba Dinamik Dünya” yazdıran metod çağrısı.

İşe öncelikle yukarıdaki programın tanımlanacağı assembly’e bir isim vererek başlamalıyız;

var assemblyAdi = new AssemblyName("MerhabaDinamikDunya");
var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyAdi, AssemblyBuilderAccess.RunAndSave);

Bu satırlarda MerhabaDinamikDunya adıyla bir dinamik assembly tanımlaması yapılmakta. DefineDynamicAssembly metoduna geçilen ikinci parametrede oluşturulacak olan bu dinamik assembly’nin çalıştırılabileceği, aynı zamanda da dosya sisteminde saklanabileceği belirtilmekte.  Burada kullanılmış olan AssemblyBuilderAccess enum’u aşağıdaki değerleri alabilir;

Run Dinamik assembly çalıştırılabilir; fakat saklanamaz
Save Dinamik assembly saklanabilr; fakat çalıştırılamaz
RunAndSave Dinamik assembly hem çalıştırılabilir, hem de saklanabilir
ReflectionOnly Dinamik assembly sadece reflection yapılabilir bir context’e yüklenir ve çalıştırılamaz
RunAndCollect Dinamik assembly çalıştırılması sonrası kaldırılarak hafızadan temizlenebilir.

Yukarıdaki kod ile dinamik assembly oluşturabilmek için ilk adımları attık. Devamında, assembly içerisindeki işlemlerimizi gerçekleştireceğimiz bir modül tanımı yapmalıyız;

var modul = assemblyBuilder.DefineDynamicModule("MerhabaDinamikDunya.exe");

Artık elimizde asıl işimizi yapabileceğimiz bir modülümüz bulunmakta. Bu noktadan itibaren tüm dikkatimizi oluşturmak istediğimiz örnek uygulamamızın üç ana bölümüne verebiliriz; sınıf tanımlaması, ana metodumuz ve konsola “Merhaba Dinamik Dünya” yazmak.

  1. Sınıf Tanımlaması;
    var typeBuilder = modul.DefineType("DinamikSinif", TypeAttributes.Public | TypeAttributes.Class);

    Bu satırda modülümüz içerisinde DinamikSinif adıyla yeni bir tür oluşturmaktayız. Verdiğimiz  TypeAttribute’ler (System.Reflection altında yer almakta) vasıtasıyla bu oluşturduğumuz türün herkesçe erişilebilir bir sınıf olduğunu belirtmekteyiz. TypeAttribute’u aynı zamanda FlagsAttribute’ü ile işaretlenmiş olması sebebiyle, yukarıda olduğu gibi, bitwise combinasyonlarla bu metoda birden fazla sayıda parametre olarak geçilebilir.

  2. Ana Metodumuz ;
    var metodbuilder = typeBuilder.DefineMethod("Main",
        MethodAttributes.HideBySig | MethodAttributes.Static | MethodAttributes.Public, 
        typeof(void), 
        new Type[] { typeof(string[]) });

    Bu satırda, oluşturduğumuz tür içerisinde yeni bir metod tanımlamaktayız. İlk parametrede verdiğimiz “Main” adıyla oluşturulacak bu metod ikinci parametrede verdiğimiz MethodAttributes’ler sayesinde static public olarak tanımlanacaktır. TypeAttributes’da olduğu gibi MethodAttributes’da FlagsAttribute’ü ile işaretlenmiştir ve farklı kombinasyonlarda bitwise olarak kullanılabilir. üçüncü parametrede void türünü geçerek metodumuzun bir dönüş türü olmadığını belirtiyoruz. Son parametre ile de metodumuzun alacağı parametrelerin türlerini belirtmiş oluyoruz.

    Şimdiye kadar takip etmiş olduğumuz adımlarla birlikte aslında assembly’mizin önemli bir bölümünü tamamlamış durumdayız. Eğer bu noktada oluşan kodu görebilmiş olsaydık aşağıdakine benzer olduğunu görürdük:

    public class DinamikSinif {
        public static void Main(string[] parametreler) {

    Dikkatli gözler bu görüntüde açılmış olan parantezlerin henüz kapatılmadığı rahatlıkla görecektir. Bunu bilerek bu şekilde gösteriyorum; çünkü oluşturduğumuz metod içerisine henüz return kodunu eklemedik. Yazımın devamında bunu nasıl yapacağınızı da göstereceğim.

  3. Konsola “Merhaba Dinamik Dünya” yazmak
    var ilOlusturucu = metodbuilder.GetILGenerator();
    ilOlusturucu.EmitWriteLine("Merhaba Dinamik Dünya");

    Bir önceki adımla birlikte oluşturduğumuz metodumuzun içerisinde işlemlerimizi ancak bir DotNet IL (Intermediate Language) oluşturucu ile gerçekleştirebiliriz. ILGenerator sınıfı yardımıyla vereceğimiz komutlar Intermidiate Language’e dönüştürülür. ILGenerator’ün pek çok metodu yanında konsola yazmak için EmitWriteLine metodu yer almaktadır. ILGenerator her ne kadar bize doğrudan tüm DotNet kütüphanelerindeki metodları çağırmak için yardımcı olmasa da konsola bir şeyler yazabilmek için bu özel metoda sahiptir.

    Peki ILGenerator içerisinde bu yardımcı metod olmasaydı konsola birşey yazamaz mıydık? ya da başka metod çağrıları yapmak istediğimizde ne yapmalıyız? Bu iki soruyu aşağıdaki örnekle açıklamak isterim.

    ilOlusturucu.Emit(OpCodes.Ldstr, "Merhaba Dinamik Dünya");
    ilOlusturucu.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }));

    Bu örnekte ikinci satırda gördüğümüz Call IL işlem koduyla (OpCodes) Console türü içerisinde yer alan WriteLine metodunuzun (verilen parametreleri kabul eden) bir önceki satırda verilen string ile çağırılması sağlanmakta. Bu iki örnekte oluşturulan kodlara reflector uygulaması yardımıyla bakacak olursak aynı çıktıya sahip olduklarını göreceğiz.

Neredeyse işimizi bitirmek üzereyiz. Kodumuza aşağıdaki son dokunuşları da ekleyecek olursak en baştaki hedefimize ulaşmış olacağız.

typeBuilder.CreateType();

assemblyBuilder.SetEntryPoint(metodbuilder, PEFileKinds.ConsoleApplication);
assemblyBuilder.Save("MerhabaDinamikDunya.exe");

Bu satırlarla öncelikle tanımlamasını yaptığımız türün oluşturulmasını sağlamaktayız. Ardından Assembly’mizin bir konsol uygulaması olduğunu ve giriş noktasının oluşturmuz olduğumuz “Main” metodu olduğunu belirtiyoruz. Son olarak ise oluşan assembly’nin “MerhabaDinamikDunya.exe” adıyla uygulamamızı çalıştığı dizine saklanmasını sağlıyoruz.

Tüm bu adımlar ardından bir adım geriye çekilerek büyük resme bakacak olursak elimizde aşağıdaki şekilde bir kod oluştuğunuz göreceğiz:

using System;
using System.Reflection;
using System.Reflection.Emit;
using System.Threading;
using System.Diagnostics;

namespace ReflectionEmitOrnegi {
    class Program {
        static void Main(string[] args)  {
            var assemblyAdi = new AssemblyName("MerhabaDinamikDunya");
            var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyAdi, AssemblyBuilderAccess.RunAndSave);
            var modul = assemblyBuilder.DefineDynamicModule("MerhabaDinamikDunya.exe");

            var typeBuilder = modul.DefineType("DinamikSinif", TypeAttributes.Public | TypeAttributes.Class);

            var metodbuilder = typeBuilder.DefineMethod("Main",
                MethodAttributes.HideBySig | MethodAttributes.Static | MethodAttributes.Public,
                typeof(void),
                new Type[] { typeof(string[]) });

            var ilOlusturucu = metodbuilder.GetILGenerator();

            //ilOlusturucu.Emit(OpCodes.Ldstr, "Merhaba Dinamik Dünya");
            //ilOlusturucu.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }));
            ilOlusturucu.EmitWriteLine("Merhaba Dinamik Dünya");

            ilOlusturucu.Emit(OpCodes.Ret);

            typeBuilder.CreateType();

            assemblyBuilder.SetEntryPoint(metodbuilder, PEFileKinds.ConsoleApplication);
            assemblyBuilder.Save("MerhabaDinamikDunya.exe");
        }
    }
}

Bu kodu çalıştırdığımızda, başta verdiğimiz işlemi yapan kodu ürettiğiniz ve “MerhabaDinamikDunya.exe” adıyla uygulamamızın çalıştığı yere saklandığını göreceğiz.

Dilerseniz oluşturduğunuz bu sınıfı saklamadan önce uygulamanız içerisinde de kullanabilirsiniz. TypeBuilder içerisinde yer alan CreateType metodu bize uygulamamız içerisinde rahatlıkla kullanabileceğimiz bir tür sunacaktır. Biraz reflection kullanarak bu tür içerisindeki metodu çalıştırabiliriz;

var merhabaDunyaTuru = typeBuilder.CreateType();
merhabaDunyaTuru.GetMethod("Main").Invoke(null, new string[] { null });

 

Bir “Merhaba Dünya” örneğinden yola çıkarak anlatmaya çalıştığım dinamik kod oluşturma örneği gerçek dünyada çok faydalı yerlerde kullanılabilir. Bu kullanımlar için aklıma gelen ilk örnek verilen bir arayüz üzerinde bir web servise bağlanılmasını sağlayan bir vekil (proxy) sınıf. Oluşturduğunuz kütüphaneyi kullanacak kişilere aşağıdaki gibi çok basit şekilde servislere bağlanmalarını sağlamak sizce de hoş olmaz mı?

IWebServisim servisVekili = WebServisVekilFabrikasi.VekilOlustur<IWebServisim>("service adresi");

Bunun için çalışma-zamanında oluşturacağımız ve IWebServisim arayüzünü implement’e eden bir dinamik tür yetecektir. Bu dinamik tür implement’e ettiği metodlar vasıtasıyla kendi içerisinde ilgili web servis metodunu çağırabilir.

Çok sıklıkla tekrar eden ve reflection kullanarak yaptığınız işlerinizi System.Reflection.Emit namespace’i altındaki sınıfları kullanarak yapmanız durumunda oldukça önemli bir performans kazanımı sağlayabilirsiniz.

Kompleks kodlarda bu yöntemin uygulanması

Kompleks olan kodların bu yöntemle üretilmesi zor olabilir. Bu gibi durumlarda aşağıda anlattığım yolla hızlıca ilerleyebilirsiniz.

Öncelikle RedGate web sitesine giderek Reflector ürününün ücretsiz sürümünü indirmelisiniz. Kuruluma ihtiyaç duymayan bu program sayesinde herhangi bir algoritma ile içeriği okunamaması için karıştırılmamış olan tüm DotNet assembly’lerini inceleyebilirsiniz.

Reflector

Yapmanız gereken, içeriğini incelemek istediğiniz sınıfa gelip ters tıklamak ve gelen menüden Disassemble seçeneğini seçmek. Bunu yaptığınızda sol tarafta kaynak kodunu istediğiniz dilde inceleyebilirsiniz.

Reflector Disassemble

Üst tarafta yer alan combobox yardımıyla kodu istediğiniz dilde görüntüleyebilirsiniz. Eklenti desteği olan bu ürün’e CodePlex içerisinde yer alan ReflectionEmitLanguage eklentisini kurarak isterseniz bu assembly’nin oluşması için kullanılabilecek Reflection.Emit kodlarını kolaylıkla görebilirsiniz. Yapmanız gereken sadece eklentiyi kurduktan sonra dil olarak yukarıdaki diller combobox’ı içerisinde Reflection.Emit’i seçmek.

Reflector Reflection.Emit

Bu işlem sonrası istediğiniz kütüphane için Reflection.Emit kodunu görebilirsiniz; ki bu özellik karmaşık kodların üretilmesi noktasında sizin için can simidi olacaktır.

Digg It!DZone It!StumbleUponTechnoratiRedditDel.icio.usNewsVineFurlBlinkList

Türkçe, C# ,

C# Programsal Olarak AppFabric Cache Host Bilgisine Erişmek

4. April 2010

AppFabric Cache ile ilgilendiğinizde gördüğünüz ilk şeylerden birisi kurulumunu ve yapılandırmasını PowerShell üzerinden yapabilmenizdir. AppFabric Cache yönetimsel araçlarını kurarak erişebileceğiniz bu yapılandırma cmdlet’leri sayesinde gerek yerel gerekse de uzak sunucularınızda ihtiyacınız olan her türlü önbellek işlemini yapabilirsiniz.

Zamanla, yaptığınız bu yönetimsel işleri programsal olarak yapmak isteyebilirsiniz ya da iş mantığınız gereği önbellekte bulunacak verileri en baştan atmak isteyebilirsiniz. Her ne kadar bu gibi şeyleri yazacağınız PowerShell cmdlet’leri ile yapabiliyor olsanız da (burada ve burada örneklerini bulabileceğiniz gibi) bu işi programsal olarak yapmak en az benim kadar sizin de hoşunuza gidecektir.

İşe öncelikle PowerShell komutlarını çalıştırabileceğimiz ortamı hazırlamakla başlamalıyız. Bunun için, şimdiye kadar yapmadıysanız eğer, PowerShell v2 SDK’sını indirerek bilgisayarınıza kurmalısınız.

var initialSessionState = InitialSessionState.CreateDefault();
initialSessionState.ThrowOnRunspaceOpenError = true;

runspace = RunspaceFactory.CreateRunspace(initialSessionState);

runspace.Open();

Yukarıdaki kod bize PowerShell komutlarımızı çalıştırabileceğimiz bir ortam sağlayacaktır. Ardından komutlarımızı vereceğimiz bir PipeLine oluşturmalıyız;

var pipeline = runspace.CreatePipeline();

Nerdeyse programımız bitmek üzere, aynı yeni bir PowerShell komut satırında olduğu gibi önbellekte çalıştırılacak komutlarınızı pipeline’a geçmemiz gerekli;

pipeline.Commands.Add(new Command("Use-CacheCluster"));
pipeline.Commands.Add(new Command("Get-CacheHost"));

Get-CacheHost komutu bize önbellek sunucumuz hakkında bilgiler verecektir. Yukarıda ardıl olarak sıralamış olduğumuz komutlar pipeline üzerindeki invoke metodu ile çalıştırabiliriz;

var result = pipeline.Invoke();

Bu noktada result nesnesi, içerisinde sunucu hakkındaki adı, port numarası, servis adı, sürümü  ve durumu gibi bilgileri barındıran HostInfo (Microsoft.ApplicationServer.Caching.AdminApi.HostInfo) türündendir.

Son bir dokunuşla kKodumuza önbellek komutlarını çalıştırmak için gerekli olan DistributedCacheAdministration modülünün yüklenmesinide ekleyecek olursak işimizi tamamlayabiliriz;

var initialSessionState = InitialSessionState.CreateDefault();
initialSessionState.ImportPSModule(new[] { "DistributedCacheAdministration" });
initialSessionState.ThrowOnRunspaceOpenError = true;

runspace = RunspaceFactory.CreateRunspace(initialSessionState);

runspace.Open();

var pipeline = runspace.CreatePipeline();
pipeline.Commands.Add(new Command("Use-CacheCluster"));
pipeline.Commands.Add(new Command("Get-CacheHost"));
var result = pipeline.Invoke();

var hostInfo = (HostInfo)result[0].BaseObject;

Console.Out.WriteLine("Sunucu Adı        : " + hostInfo.HostName);
Console.Out.WriteLine("Sunucu Portu      : " + hostInfo.PortNo);
Console.Out.WriteLine("Sunucu Servis Adı : " + hostInfo.ServiceName);
Console.Out.WriteLine("Sunucu Durumu     : " + hostInfo.Status);
Console.Out.WriteLine("Sunucu Sürümü     : " + hostInfo.VersionInfo); 

Yukarıdaki kodun çalışabilmesi için aşağıdaki assembly’lerin referanslarınızda olması gerekli;

  • Microsoft.ApplicationServer.Caching.Client
  • Microsoft.ApplicationServer.Caching.Core
  • Microsoft.ApplicationServer.Caching.Management
  • System.Management.Automation

 

Not 1 : 64-bit bir işletim sistemi üzerinde çalışıyorsanız. AppFabric Cache referanslarını eklenmesi ile ilgili makalemi okumanızı tavsiye ederim.

Not 2 : Yukarıdaki kodu Visual Studio 2010 ve .Net framework 4.0 ile derlemeye çalışıyorsanız hedef framework’ünüzün “.Net Framework 4 Client Profile” değil “.Net Framework 4” olduğunu kontrol edin.

Digg It!DZone It!StumbleUponTechnoratiRedditDel.icio.usNewsVineFurlBlinkList

C#, PowerShell, Türkçe, AppFabric , ,

AppFabric Caching

4. March 2010

AppFabric Cache (ya da kod adıyla Velocity) ölçeklenebilir, erişilebilir ve yüksek performanslı uygulamalar geliştirmek için sunulan harici bir dağıtık uygulama önbellek platformudur. Temelde WCF uçnoktası ile erişilebilen bir windows hizmeti olan AppFabric Cache, sizin yerinize verilerinizin nerede ve ne şekilde tutulacağını yönetmekle kalmayıp bu işlemi senkronize şekilde birden fazla bilgisayar üzerinde rahatlıkla yapabilmekte, üstelik kodunuzda bir değişikliğe ihtiyaç duymadan. Uygulamalarınızda kullandığınız her türlü veriyi (CLR nesneleri, XML, binary v.b.) harici ve dağıtık bir yapıda önbellekte tutabilmenize imkan veren AppFabric Caching sayesinde uygulamanızı ihtiyaçlarınız doğrultusunda yatayda dilediğiniz kadar genişletebilme gücüne sahip olabileceksiniz. Bu özelliğin size getireceği bir diğer önemli güç ise uygulamanızın yüksek erişilebilirliğe sahip ve hatalara karşı töleranslı olmasıdır. AppFabric Cache'in bir başka önemli özelliği asp.net oturum sağlayacı (session provider) desteği sunması. Bu özellikle birlikte mevcut asp.net uygulama kodunuzda değişikliğe ihtiyaç duymaksızın yapılandırma dosyasında oturum sağlayıcı olarak AppFabric Cache'i verebilir ve size sağladığı tüm avantajları anında kullanmaya başlayabilirsiniz.

Örnek bir senaryo üzerinden AppFabric Cache'i hangi gereksinimler nedeniyle ve ne şekilde kullanabilirsiniz görmeniz konuyu daha iyi anlamanız açısından faydalı olacaktır; web tabanlı bir uygulama yaptığınızı düşünelim. Arka planda verilerinizi bir veritabanında saklamaktasınız. Aşağıdaki grafikte görülen mimaride, sunucu ve istemci tarafları birbirinden ayrılmış.

Sistemin ilk mimarisi

Uygulamanız ister IIS üzerinde host edilen ve Asp.Net ile yazılmış bir web sitesi ya da bir WCF servisi, isterse de bir windows servisi olsun. Başta sisteminiz oldukça performanslı çalışacak, kullanıcı isteklerine hızlıca yanıt verecektir.

Zamanla sisteminizin popüleritesi artacak ve yukarıdaki mimari ile istekleri karşılayamayacak duruma geleceksiniz.

Gelen istekler sonrası yeni mimari

Bu yeni durum karşısında sisteminize yatırım yaptığınızı düşünelim, iki yeni sunucu eklenmesiyle birlikte bir load-balancer arkasında toplamda üç sunucu ve bir veritabanı ile hizmet veriyorsunuz.

Bu yeni mimari ile birlikte daha önceden hesaplamadığınız sıkıntılar yaşamaya başladınız. Uygulamanız örneğin bir asp.net uygulaması ise oturum bilgilerini sunucularınız arasında paylaştıramadığınız için gelen bir istemci oturumu boyunca aynı sunucu tarafından hizmet görmek zorunda. Bu durumda, sisteminizde bulunan load-balancer gerçek anlamda yükü dağıtamaz duruma gelmiştir. Üstelik gelen yoğun istekler sonrası büyüyen sisteminizde veritabanı da sizin için başta düşündüğünüzden çok daha önemli bir kaynak haline geldi ve bu kaynağa yaptığınız her ekstra erişimle birlikte uygulamanız yavaşlar oldu. Tüm bunların yanında peki ya sunuculardan birisi aniden bozulursa? ya da bir sunucudaki asp.net uygulamanız oluşan bir hatadan dolayı yeniden başlar ve oturum bilgileri kaybolursa? Bu durumda en iyi ihtimalle bile kullanıcı memnuniyetini kaybedeceğiniz için bir kayıp söz konusu olacaktır; kaldı ki kötü ihtimallerde önemli verilerin kaybı dahi söz konusu olabilir.

Yeni mimarideki belki de en önemli problem, yukarıda da belirttiğim gibi, gelen isteklerin yoğunluğunda her gelen istekte çok önemli bir kaynak olan veri tabanına gidilmesi sistemde yavaşlamalara neden olacaktır. Veriniz büyüdükçe yapılan sorguların maliyeti giderek daha fazla artacak ve sonuç kullanıcılarınıza sistem yavaşlıkları olarak yansıyacaktır. Bu problemin çözümü ise hem mimari, hem maliyet açısından oldukça külfetlidir. Bazı durumlarda bu problemin çözümü o kadar maliyetli olabilir ki, yeni bir müşteri kabul etmemek bile size mantık gelmeye başlar.

Bu durumda öncelikli ihtiyacınız tüm sunucularınızla payşalıcak bir yapıda ve veri kaybına neden olmayacak şekilde oturum bilgilerinin tutulması. Aynı şekilde sık erişim yapılan verilerin (örneğin ürün kataloğunun) hafızada tutulmasıyla sistemin daha hızlı yanıt vermesinin sağlanması gereklidir.

Böylesi bir ihtiyaca yönelik kod geliştirmek hem zamanınızı alacaktır hem de asıl hedefinizden sapmanıza ve ek maliyetler oluşmasına neden olacaktır. İşte tam da bu noktada AppFabric Caching sahneye çıkmakta.

AppFabric Cache ile yeni mimari

Mimarinizin en başından AppFabric Caching ile tasarlanması ile birlikte sık erişimi olan bilgilerinizi ve oturum bilgilerinizi önbellek kümesine alabilir, bu sayede hem yatayda genişletilebilirlik kazanır hem de sistem performansınızı arttırmış olursunuz. Örneğin, bir alış-veriş sitesinde her gelen istekte ürün/fiyat kataloğunun yeniden oluşturulması yerine ilk istekte hesaplayarak sonraki istekler için önbellekte tuturak buradan sunulmasının performansı arttıracağı aşikardır. Üstelik dilerseniz AppFabric Cache'e attığınız verileri birbirinden yalıtabilirsiniz; ki bu sayede sistem güvenirliği de arttırılmış olacaktır.

AppFabric Cache yüksek erişilebilirlik sunmak amacıyla verilerinizi önbellek içerisinde bulunan sunucular arasına dağıtmaktadır. Yüksek erişilebilirlik için verilerinizin aynı anda birden fazla sunucuda kopyası bulunabilir; fakat bu sunuculardan sadece birisini birincil olarak veriden sorumludur. Küme içerisindeki tüm sunucular birbirlerinden haberdar ve birbirlerini takip etmektedir. Sunuculardan birisinin herhangi bir sebeple erişelemez duruma gelmesi halinde küme içerisindeki diğer bir sunucu erişilemeyen sunucudaki veriler için birincil sorumlu haline gelerek veri kayıpsız şekilde sistemin çalışmasını sağlayacaktır.

Verileriniz önbellek kümesinde anahtar-değer ikilisi şeklinde tutulmaktadır. Programsal olarak herhangi bir anahtar için değer değişimleri takip edilebileceği gibi bir önbellek içindeki tüm değişiklikler de takip edilebilir.

AppFabric Cache kümesinin yönetimi ise bizlere sunulan PowerShell komutları üzerinden yapılabilmektedir. PowerShell komutları ile önbellek içerisinde nesnelerin yaşam ömürleri, yedeklerinin bulunması gibi bilgilere erişilebilir/düzenleyebilinir. Bunun yanında önbellek kümesi yetkileri düzenlenebilir, kümeye yeni önbellek sunucusunun erişimi sağlanabilir.

AppFabric Cache'in Beta 2 sürümü ile gelen yeni bir özelliği ise performans monitörü üzerinden önbellek durumunun takip edilebilmesi. Bu özellik sayesinde anlık yapılan okuma yazma istekleri, önbellekte tutulan nesne boyutları v.b. sisteminizi takip etmenizi kolaylaştıran bilgilere anlık ve hızlı şekilde ulaşabileceksiniz.

Yukarıda detaylı olarak anlattığım AppFabric Cache’in sunduğu avantajlar özet olarak şu şekilde sıralanabilir;

  • Aynı ve/veya farklı uygulamalarınız arasında veri paylaşımı yapabilirsiniz
  • Uygulamalarınız verileri önbelleğe alabildikleri için daha performanslı çalışacaktır.
  • Performans ihtiyaçlarınız doğrultusunda sisteminizi istediğiniz şekilde yatayda genişletebilirsiniz.
  • Sisteminiz 7/24 hizmet verebilir hale gelecektir. Herhangi bir sebeple sunucularınızda birisi hizmet veremeyecek durumda dahi olsa bir bilgi kaybı olmayacak, önbellekteki veriler küme içerisideki diğer sunucularca size iletilebilecektir.
  • PowerShell desteği sayesinde merkezi bir noktadan yönetilebilir.
  • Önbellekler Perfmon aracı ile izlenebilir (Beta1 sürümünden itibaren)
  • Loglama ile durum takibi yapılabilir.
  • Tüm bu getiriler oldukça düşük maliyetlerle birlikte gelmekte.

AppFabric Cache aslında yeni çıkmış bir teknoloji değil. İlk olarak haziran 2008’de Velocity kod adıyla CTP1 sürümü yayınlandı. Bunu CTP2 (PDC08) ve CTP3 (Mart 2009) sürümleri takip etti. Ürünün Beta1 (Kasım 2009) olarak yayınlanmasıyla birlikte AppFabric Cache olarak isimlendirildi.

Bu makalenin yazıldığı zamanda Beta 2 (Mix10) sürümü yayınlanmış olan AppFabric Cache’in 2010 yılı içerisinde RTM sürümünün yayınlanması planlanmakta. İlerleyen zamanlarda ise Windows Azure ile entegre çalışacak olan Azure CTP sürümünün yayınlanması söz konusu.

Digg It!DZone It!StumbleUponTechnoratiRedditDel.icio.usNewsVineFurlBlinkList

C#, AppFabric

dynamic : statik ve dinamik diller arası köprü

14. February 2010

Bir önceki makaleme gelen güzel yorumlar sonrası dynamic hakkında ikinci bir makale ile konuyu detaylandırmanın faydalı olacağını düşündüm.

C# 4.0’la birlikte gelen özellikleri inceleyenlerin dynamic hakkında düştüğü ortak bir yanılğı, dynamic’in CLR seviyesinde desteklendiğidir. Her ne kadar dynamic hayatımızda önemli bir değişikliğe neden olsa da, bu yanılgıya düşenler için basit kullanımlarda beklediklerinin aksine uygulamalarında performans kayıplarına neden olacaktır.

dynamic, CLR açısından bakıldığında, herhangi bir C# kütüphanesinden farklı değildir. Tabi ki microsoft bu kütüphanenin bizler tarafından daha kolay kullanılabilmesi amacıyla C# diline ekleme yapmış, derleyici desteği sağlamıştır.

Konuyu daha netleştirebilmek için bir önceki makalemde verdiğim aşağıdaki dynamic kod örneğini hatırlayalım:

class Program {
   static void Main(string[] args) {
      dynamic nesne = 1;

      if (nesne.GetType() == typeof(int)){
         Console.WriteLine("Tabi ki türü int");
      }  else {
         Console.WriteLine("Tüm makale hatalıymış :P");
      }

      nesne = nesne + 1;
   }
}

Bu örneğin derlenmesi sonrası oluşan kodu reflector ile incelersek karşımıza aşağıdaki gibi bir kod çıkacaktır:

internal class Program {
    private static void Main(string[] args) {
        object nesne = 1;
        if (o__SiteContainer0.<>p__Site1 == null) {
            o__SiteContainer0.<>p__Site1 = CallSite>.Create(Binder.UnaryOperation(CSharpBinderFlags.None, ExpressionType.IsTrue, typeof(Program), new CSharpArgumentInfo[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) }));
        }
        if (o__SiteContainer0.<>p__Site2 == null) {
            o__SiteContainer0.<>p__Site2 = CallSite>.Create(Binder.BinaryOperation(CSharpBinderFlags.None, ExpressionType.Equal, typeof(Program), new CSharpArgumentInfo[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType, null) }));
        }
        if (o__SiteContainer0.<>p__Site3 == null) {
            o__SiteContainer0.<>p__Site3 = CallSite>.Create(Binder.InvokeMember(CSharpBinderFlags.None, "GetType", null, typeof(Program), new CSharpArgumentInfo[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) }));
        }
        if (o__SiteContainer0.<>p__Site1.Target.Invoke(o__SiteContainer0.<>p__Site1, o__SiteContainer0.<>p__Site2.Target.Invoke(o__SiteContainer0.<>p__Site2, o__SiteContainer0.<>p__Site3.Target.Invoke(o__SiteContainer0.<>p__Site3, nesne), typeof(int)))) {
            Console.WriteLine("Tabi ki t\x00fcr\x00fc int");
        }
        else {
            Console.WriteLine("T\x00fcm makale hatalıymış :P");
        }

        if (o__SiteContainer0.<>p__Site4 == null) {
            o__SiteContainer0.<>p__Site4 = CallSite>.Create(Binder.BinaryOperation(CSharpBinderFlags.None, ExpressionType.Add, typeof(Program), new CSharpArgumentInfo[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.Constant | CSharpArgumentInfoFlags.UseCompileTimeType, null) }));
        }
        nesne = o__SiteContainer0.<>p__Site4.Target.Invoke(o__SiteContainer0.<>p__Site4, nesne, 1);
    }

    [CompilerGenerated]
    private static class o__SiteContainer0 {
        public static CallSite> <>p__Site1;
        public static CallSite> <>p__Site2;
        public static CallSite> <>p__Site3;
        public static CallSite> <>p__Site4;
    }
}

Görüldüğü gibi yazmış olduğumuz kod içerisine derleyici tarafında oldukça fazla müdahale edilmiş. Yapılan bu müdahale ile birlikte ortaya çıkan bu kodu, basit bir tür dönüşümü ile hızlıca ilerleyeceğimiz aşağıdaki gibi bir kod ile karşılaştırdığımızda hangisinin daha performanslı olacağı aşikar;

class Program {
   static void Main(string[] args) {
      object nesne = 1;

      if (nesne.GetType() == typeof(int)){
         Console.WriteLine("Tabi ki türü int");
      }  else {
         Console.WriteLine("Tüm makale hatalıymış :P");
      }

      nesne = (int)nesne + 1;
   }
}

Tabi ki bu karşılaştırma hiç bir zaman dynamic kullanmayın demek değil; fakat doğru yerde kullanın demek ;)

 

Peki dynamic kullanımı için doğru yer neresidir?

.Net 4.0 DLR (Dynamic Language Runtime, Dinamik Dil Çalışma-Zamanı) ve birlikte gelen dynamic anahtar kelimesi, statik diller (C#, Visual Basic v.b.) ile dinamik diller (Python, Ruby v.b.) arasında bir köprü olmaktadır.

CLR-DLR etkileşimi

Üstelik bu köprü sadece Python ve Ruby dilleriyle de sınırlı olmayıp; Javascript, Silverlight, Ofis/COM ve diğer pek çoklarını da kapsamaktadır.

.Net 4.0 öncesinde dinamik diller ve COM ile iletişim kurmakta yaşadığımız pek çok zorluk DLR ile birlikte tarih olacak gibi görünüyor. Bu noktalardaki dynamic kullanımı gerek geliştirme süresini hızlandırmada, gerekse de kod okunurluğunu arttırmada önemli bir katma değer sağlayacaktır.

Dinamil Dil Çalışma-Zamanı (DLR) nasıl çalışır?

Yazımın başında da belirttiğim gibi CLR bakış açısıyla DLR sadece bir kütüphanedir; fakat hayatımızdan önemli bir yer tutacak bir kütüphane :)

Dinamik dil çalışma-zamanı hakkında daha önce de araştırma yaptıysanız aşağıdaki grafik size tanıdık gelecektir.

dlr2

Statik dillerle dinamik diller arasında bir köprü olan DLR bu grafikte de görüldüğü gibi 4 ana parçadan oluşur:

  1. İfade ağacları (Expression Trees) : İfade ağaçları, Linq ile derinlemesine uğraşanlarınıza tanıdık gelecektir. Yazdığınız ifade, derleyici tarafından MSIL’e dönüştürülmek yerine programsal olarak işlenebilmesi amacıyla ifadeyi anlatan ağaç yapısında nesnelere dönüştürülecektir. Bu dönüşüm sayesinde, çalışacak kodun derlenme-zamanı oluşması nedeniyle statik dillerde sahip olamadığımız esnekliği kazanmaktayız.
  2. Dinamik Dağıtıcı (Dynamic Dispatch) :  En basit ifadeyle dinamik dağıtıcı, çalışma-zamanı metot ve özellik çağrılarına izin veren yapıdır. Dinamik bir tür üzerinde yapılan işlemler dinamik dağıtıcıya yönlendirilmekte, burada yapılmış olan işleme ait ifade ağacı yorumlanmaktadır. Dinamik dağıtıcı sayesinde C# içerisinden dinamik dillere çağrı yapılabilmesi sağlandığı gibi, aynı zamanda dinamik dillerden C# sınıflarına çağrı yapılabilmesine olanak sağlanmıştır. Bu sayede, çift yönlü olarak, her iki yaklaşımın güçlü yönleri aynı anda kullanılabilmektedir. Bir C# sınıfında dinamik dağıtıcı özelliğine sahip olabilmek için sınıfı IDynamicMetaObjectProvider, DynamicMetaObject, DynamicObject ve ExpandoObject gibi arayüz ve sınıflardan türetmek yeterli olacaktır.
  3. Call Site Önbelleği (Call Site Caching)  : Call Site, dinamik türler üzerinden aritmetik işlemler, metot çağrıları gibi çeşitli işlemleri yapabilmenizi sağlayan yerdir. Dinamik dil çalışma-zamanı üzerinde çalıştığı sınıflar ve işlemler hakkındaki bilgileri önbelleğe alarak ardıl çağrılarda bu bilgileri yeniden elde etmek yerine önbellekteki bilgilerden yararlanır. Bu yöntem sayesinde dynamic işlemler reflection yöntemiyle yapılanlarla kıyaslandığında daha hızlı gerçekleşir.
  4. Başlayıcılar (Binders) : Call site’lar içerisinde yer alan ve dinamik dil çalışma-zamanınca kullanılan bağlayıcılar işlemin gerçekleştirileceği .Net framework’ü, COM, silverlight, dinamik diller gibi ortamlarla iletişimi sağlarlar.

Yukarıdaki bilgiler ışığında, yazdığınız bir dinamik kod DLR tarafından öncelikle daha önceden işlenip işlenmediğinin anlaşılması amacıyla önbellekte kontrol edilir. Eğer aynı işlem daha önce çağrıldıysa önbellekte yer alan temsilci (delegate) üzerinden hazırda bulunan adımlarla işlevini yerine getirir. Eğer dinamik kod ilk defa işleniyorsa ifade ağacı ilgili call site’a iletilerek arkaplanda yer alan bağlayıcı yardımıyla çalıştırılır ve sonraki kullanımlarda kullanılmak üzere bir temsilci atanarak önbelleğe alınır.

Yukarıda anlattıklarımdan sonra heyecanlanmamak mümkün değil bence. Şimdiye kadar işlemlerimizi statik olarak gerçekleştirdiğimiz C#, Visual Basic gibi dillere dinamik olarak çalışma-zamanında işlem yapabilme yeteneği eklenmesi kayda değer bir yenilik.

Digg It!DZone It!StumbleUponTechnoratiRedditDel.icio.usNewsVineFurlBlinkList

C#, Türkçe , , ,

object ve dynamic

3. February 2010

Son zamanlar sıkça "dynamic" ve eski dostumuz "object" arasında ne fark olduğu sorusuyla karşılaşıyorum. İkisi arasında bir fark var mı? Bu sorunun yanıtı hem evet hem hayır aslında. Eğer, arka planda yapılan işleri bilmiyorsanız, evet, rahatlıkla object kullandığınız her yerde kullanabilirsiniz ve derleme-zamannda herhangi bir hata ile karşılaşmazsınız. Sanırım yazımın devamını okuyarak kararı sizin vermeniz en doğrusu.

Önceliği eski dostumuz "object"'te verirsem sanırım kimse alınmaz :) object, hepinizinde bildiğini biliyorum ama, "System.Object"'e kısayoldan başka bir şey değil aslında ve C#'ın ilk sürümünden beri hayatımızda. C#'ta neredeyse herşeyin bir nesne olduğu ve object'den türediğini düşünürsek ne kadar güçlü olduğunu anlayabiliriz; neredeyse her değeri atayabiliriz.

Yazımın geri kalanında karşılaştırmayı örnek bir kod üzerinden yapmak daha iyi olacak sanırım. Aşağıda basit bir kod parçacığı göreceksiniz;

object nesne = 1;

if (nesne.GetType() == typeof(int)){
     Console.WriteLine("Tabi ki türü int");
 }  else {
     Console.WriteLine("Tüm makale hatalıymış :P");
}

bu kod parçacığının çıktısını sorsam, eminim ki "Tüm makale hatalıymış :P" diyenleriniz olacaktır, ne de olsa "nesne int değil bir object olarak tanımlanmış"; ama gerçekte çıktı "Tabi ki türü int". Her ne kadar nesne tanımlaması sırasında object olarak belirlenmişte olsa çalışma-zamanında içerisine konulan değer bir int.

Bu örnekte nesne içerisinde her ne kadar bir int türünden veri yer alsa da, üzerinde bir int'e uygulayabileceğiniz toplama işlemi gibi basit bir işlemi yapmaya çalıştığınızda bakın neler oluyor;

object nesne = 1;

if (nesne.GetType() == typeof(int)){
     Console.WriteLine("Tabi ki türü int");
 }  else {
     Console.WriteLine("Tüm makale hatalıymış :P");
}

nesne = nesne + 1;

İlk örneğimiz sorunsuz olarak derlenirken, bu basit değişiklik sonrasında kodumuz derleme sırasında hata alacaktır ve derlemek isteğimizde öncelikle nesne'yi int türüne dönüştürmeliyiz;

object nesne = 1;

if (nesne.GetType() == typeof(int)){
     Console.WriteLine("Tabi ki türü int");
 }  else {
     Console.WriteLine("Tüm makale hatalıymış :P");
}

nesne = (int)nesne + 1;

Bu basit dönüşüm, derleyiciye ne yaptığımızı bildiğimizi belirtmenin bir yolu: nesne’m bir int ifadedir. Bu noktada derleyici için nesne’nin değeri bir object olmaktan çıkar. Tabi ki, önemle not düşmem gerekir ki, tür dönüşümünü yaparak derleme-zamanı hata alınmaması her şeyin %100 doğru olduğu anlamına gelmeyecektir. Bu duruma örnek vermek gerekirse, aşağıdaki kod parçacığında nesneyi string türüne harici olarak dönüştürme işlemi derleme-zamanında bir hataya neden olmayacak; fakat derlenen kod çalıştırıldığında çalışma-zamanı hata alınacaktır:

object nesne = 1;

if (nesne.GetType() == typeof(int)){
     Console.WriteLine("Tabi ki türü int");
 }  else {
     Console.WriteLine("Tüm makale hatalıymış :P");
}

nesne = (string)nesne + 1;

Derleyici, siz harici olarak belirtmediğiniz sürece, nesne'yi object türünden kabul etmekte ve üzerinden başka bir türe ait işlem yapmaya izin vermemekte; fakat kazayla içindeki değerden yukarıdaki örnekte olduğu gibi harici olarak farklı bir tür'e dönüştürülmesi çalışma-zamanı hataya neden olacaktır.

.Net 4.0'la sahne alan önemli yeniliklerden biri olan “dynamic”, kabaca anlatmak gerekirse, harici olarak derleyiciye bu tür üzerinde derleme sırasında herhangi bir kontrol yapılmayacağını belirtir. Yapılacak olan işlemler çalışma-zamanındaki veriler doğrultusunda yorumlanarak yerine getirilir. İlk örneğin dynamic versiyonu da aşağıdaki örnekte de göreceğiniz gibi ilk kullanıma oldukça benzer:

dynamic nesne = 1;

if (nesne.GetType() == typeof(int)){
     Console.WriteLine("Tabi ki türü int");
 }  else {
     Console.WriteLine("Tüm makale hatalıymış :P");
}

Üstelik, ilk object örneğinden farklı olarak, aşağıdaki kod herhangi bir tür değişimine ihtiyaç duymaksızın hatasız şekilde derlenir;

dynamic nesne = 1;

if (nesne.GetType() == typeof(int)){
     Console.WriteLine("Tabi ki türü int");
 }  else {
     Console.WriteLine("Tüm makale hatalıymış :P");
}

nesne = nesne + 1;

Bunun sebebi, dynamic'i tanıtırken de belirttiğim gibi, derleme-zamanında tür üzerinden kontrol yapılmaması. İşte bu object ile dynamic arasındaki en önemli farktır. Nesnenin dynamic ile tanımlanmasıyla birlikte programcı derleyiciye bu nesne içindeki veri türünün ancak çalışma-zamanında belli olabileceğini ve bu sebeple de derleme zamanında hata vermemesi gerektiğini belirtir. Sonuç olarak da, derleyici bu veri türü için yapılan işlemlere müdahale etmez. Aşağıdaki kod parçacığının son satırı bahsettiğim duruma basit bir örnek olacaktır:

dynamic nesne = 1;

if (nesne.GetType() == typeof(int)){
     Console.WriteLine("Tabi ki türü int");
}  else {
     Console.WriteLine("Tüm makale hatalıymış :P");
}

nesne.VarolmayanBirMethod();

     Bu kod parçacığı sizinde göreceğiniz gibi hatasız derlenecek; fakat çalışma zamanında nesne içerisinde VarolmayanBirMethod bulunamadığı için çalışma-zamanı hatası alınacaktır.

 

Peki derleyici dynamic türler üzerinde derleme sırasında kontrol yapmıyorsa nasıl bir işlem yapıyor?

      dynamic tanımlanan türler üzerinde yapılan işlemler (örneğin method çağrıları) hakkında derleme zamanında analiz yapılarak çağrılan metodun ismi, parametre sayısı, parametre türleri, bağlayıcı türü(c# nesnesi, python nesnesi, COM nesnesi) v.b. bilgiler çalışma-zamanında kullanılmak üzere saklanır ve çalışma-zamanında tür’ün aldığı değer doğrultusunda bu bilgiler kullanılarak talep edilen işlemler gerçekleştirilir. Dolayısıla eksik ve/veya hatalı bilgi verilmesi (olmayan bir metod adı, eksik parametre) derleme-zamanı değil çalışma-zamanı hataya neden olacaktır.

     Yapılan bu çalışma-zamanı yorumlama işi tabi ki uygulamanıza ek bir yük getirecektir (işlemci zamanı ve hafıza kullanımı olarak), bu ek yükün hafifletilmesi anlamında gerekli önbellek işlemlerinin yapıldığını da not olarak düşmem gerekir. Bu sayede, örneğin bir döngü içerisinde yapılan bir dynamic çağrıda ilk defadan sonra yeniden bir yorumlama yapılmayarak önbellekte hazır bulunan kod parçacığı işletilir.

    dynamic hakkında unutulmaması gereken önemli bir noktada, arka planda kullanılan Dinamik dil çalışma zamanı kütüphanesinin (Dynamic Language Runtime - DLR) .Net için sadece C# diliyle yazılmış bir kütüphane olduğudur, aynı sizlerin yazdığı  kütüphaneler gibi. Dolayısıyla da yukarıdaki örneklerdeki gibi kullanımlarda hızlı, okunur kod yazılmasını sağlamakla birlikte normalde yapılabilecek basit bir tür dönüşümünden öteye geçilerek arka planda sizin yerinize yapılan çağrılar nedeniyle kodun performansı düşecektir.

"BÜYÜK BİR GÜÇLE BİRLİKTE BÜYÜK SORUMLULUKTA GELMELİDİR"

C# 4.0 ile birlikte gelen önemli özelliklerde birisi olan “dynamic” temelde iki amaca hizmet vermesi için getirilmiştir;

  • C# ve COM birlikte işlerliğinin kolaylaştırılması
  • C#  ve dinamik dillerin (Javascript, Python, Ruby v.b.) birlikte işlerliğinin sağlanması  ve kolaylaştırılması

  Bu amaçlar dışında kullanılalabiliyor olmasına karşın; başta performans olmak üzere, derleme zamanı hataların farkedilememesi v.b. nedenlerle tavsiye edilmemektedir, sadece gerekli olduğunda ve çok dikkatli biçimde kullanılmalıdır.

Digg It!DZone It!StumbleUponTechnoratiRedditDel.icio.usNewsVineFurlBlinkList

C#, Türkçe , , ,

dynamic vs object

2. February 2010

      People keep asking me what is the difference between “dynamic” and the old friend “object”. Well, answer is a little tricky; in order to show the difference, it’s better to take a look at both of them more detailed.

     Let me start with the old friend “object”, that is nothing but a shortcut for “System.Object”; it’s a part of our life since the first release of C#. Object is very powerful, cause you can assign almost any value. In order to compare them lets take a loot a example below:

object obj = 1;

 if (obj.GetType() == typeof(int)){
     Console.WriteLine("of course type is int");
 }  else {
     Console.WriteLine("Whole post is wrong :P");
}

Console.ReadKey();

One may expect "Whole post is wrong :P", since obj is declared as object not the int; but output of the code is simply "of course type is int"; cause value stored at obj is of type int. Although value of obj is of type int and code runs without any problem, as soon as you perform any int related operation, like sum, code will not even compile:

object obj = 1;

 if (obj.GetType() == typeof(int)){
     Console.WriteLine("of course type is int");
} else {
     Console.WriteLine("Whole post is wrong :P");
}

obj = obj + 1;

(error CS0019: Operator '+=' cannot be applied to operands of type 'object' and 'int')

In order to compile your code, you need to cast obj first:

object obj = 1;

 if (obj.GetType() == typeof(int)){
     Console.WriteLine("of course type is int");
} else {
     Console.WriteLine("Whole post is wrong :P");
}

obj = (int)obj + 1;

of course having a compiled code doesn’t means that everything is fine. Anyone can have a compiled code like below, which gives run-time error (InvalidCastException)

object obj = 1;

 if (obj.GetType() == typeof(int)){
     Console.WriteLine("of course type is int");
} else {
     Console.WriteLine("Whole post is wrong :P");
}

obj = (string)obj + 1;

Compiler does not let you perform any mathematical operation on obj, as long as explicitly indicated. But still can be explicitly cast to any other type, which causes run-time exceptions.

Here comes “dynamic” in. Dynamic  comes with C# 4.0 and instructs compiler not to perform additional rules on your type. You can use it just like you do with the “object”:

dynamic obj = 1;

 if (obj.GetType() == typeof(int)){
     Console.WriteLine("of course type is int");
 }  else {
     Console.WriteLine("Whole post is wrong :P");
}

Console.ReadKey();

Also, following code will compile without any compiler error:

dynamic obj = 1;

 if (obj.GetType() == typeof(int)){
     Console.WriteLine("of course type is int");
} else {
     Console.WriteLine("Whole post is wrong :P");
}

obj = obj + 1;

You don’t need to cast obj to int as you do with object. This is the main difference between object and dynamic. By using dynamic keyword, you instruct compiler not to perform additional rules on your type. It will instruct compiler that its type can be known only at run-time, so compiler doesn’t try to interfere.

By using dynamic, your code’s readability will improve, you don’t have to perform additional explicit cast.

“WITH GREAT POWER THERE MUST ALSO COME - - GREAT RESPONSIBILITY!”

Dynamic is very handy; but must use very carefully. It is still as dangerous as object is and you should apply all check-points within your code.

Digg It!DZone It!StumbleUponTechnoratiRedditDel.icio.usNewsVineFurlBlinkList

C#, English ,