Makale Özeti

Assembly Caching ile Silverlight uygulamalarınızdaki harici Assembly'lerin önbelleklenmesini sağlayarak daha hızlı açılış süreleri yakalayabilirsiniz. Bu yazımızda farklı kütüphanalerin cachelenmesi ile ilgili Assembly Caching özelliğini inceliyoruz.

Makale

Silverlight uygulamalarınızda harici kontroller kullanmamak elde değil. Bugün ister Silverlight SDK ile beraber gelen kontroller olsun ister Silverlight Toolkit ile beraber gelenler hepsi de neredeyse hayati öneme sahip. Tüm bu senaryoda XAP dosyamızın büyüklüğünü olabildiğince ufak tutmak da tabi bir diğer hedefimiz. Bu çerçevede zaten hali hazırdaki çoğu kontrol de ayrı kütüphanalere saklanmış durumda. Örneğin Silverlight SDK ile beraber gelen ve Visual Studio içerisinde varsayılan yüklemelerle Toolbox'a yerleşen DataGrid aslında harici bir DLL içerisinde bulunuyor. Siz herhangi bir şekilde uygulamanıza DataGrid eklediğinizde akraplanda söz konusu DLL de projenize eklenmiş oluyor. Tabi bu durum gün geçtikçe XAP dosyanızın şişmesine neden olabiliyor çünkü malum, kullandığınız tüm DLL'lerin XAP'ın içerisinde yerleştirilmek durumunda.

Yazıma böyle uzun ve konumuzla alakası pek de belli olmayan bir yazıyla başlamamın emin olun bir nedeni var :) Konumuz olan "Assembly Caching" aslında da tam bir önceki parafragta bahsettiğimiz sorunları kısmen de olsa çözmeyi planlıyor. Sonuçta eğer DataGrid kullanacaksanız DLL'ini istemciye göndermek zorundayız, bunu engelleyemeyiz fakat nasıl gönderileceği ile ilgili belki bazı değişiklikler yapabiliriz...

Yeni  bir proje yeni bir hayat....

Şimdi gelin Visual Studio'da yeni bir Silverlight projesi yaratalım ve araç çubuğundan bir DataGrid tutup hemen sahneye atın. Bu işlemi yapmanızla beraber XAML tarafında DataGrid için gerekli namespace'lerin tanımlandığını ve bazı DLL'lerin otomatik olarak referans alındığını göreceksiniz. Hemen başka kod yazmadan projenizi Build ederek XAP dosyasının da uzantısını değiştirip ZIP yapıp içerisine bir göz atalım.

DataGrid ile normal bir Silverlight projesi.
DataGrid ile normal bir Silverlight projesi.

Yukarıdaki gördüğünüz üzere bir DataGrid için toplam 4 ayrı DLL XAP dosyasına eklenmiş durumda. Özellikle DataGrid'in kendisinin bulunduğu DLL 430KB. Tabi tüm bunlar XAP içerisinde ZIP'lendiği için daha ufalıyor fakat şu anda bile XAP dosyamızın büyüklüğü 206KB. Bu arada assembly_caching_VB.dll dosyası kafanızı karıştırmasın, benim yaratmış olduğum örnek projenin adı assembly_Caching_VB olduğu için aslında o dosya projemizin kendi kodlarını içeriyor.

Peki ne yapabiliriz?

Daha önce de dediğim gibi sonuçta bu DLL'lerin istemciye gitmesi şart. Yapabileceğimiz tek şey uygulamadan bağımsız olarak bu DLL'lerin istemci tarafında önbelleklenebilmesini sağlamak. Genelde bu şekilde uygulama geliştirirken uygulamanız sürekli değişebilir fakat kullandığınız hazır kütüphanaler genelde hep aynıdır. Yani bizim DataGrid olan uygulamamız değişse ve cachlenmese de aslında DataGrid'in kendisinin bulunduğu DLL rahatlıkla daha uzun vadeli olarak cachelenebilir.

Tabi bu gibi bir Cachelemenin tarayıcı tarafından yapılabilmesi için bu DLL'lerin ayrı paketler olarak istemciye gönderilmesi gerekiyor. Bu gibi bir senaryoyu kendi kodlarımızı yazarak oluşturabiliriz fakat Silverlight 3.0 ile beraber gelen Assembly Caching mekanizması zaten bunu yapıyor. O nedenle pek de kod yazmadan hemen işimizi halledebiliriz.

Silverlight projenize sağ tuş ile tıklayarak gelen menüden "Properties" komutunu verip karşınıza çıkan ekranda "Reduce XAP Size..." seçeneğini işaretleyebilirsiniz.

Assembly Caching'i aktif hale getiriyoruz.
Assembly Caching'i aktif hale getiriyoruz.

Hemen projemizi yeni build edip ASP.NET projemizdeki ClientBin klasörü içerisine bir göz atalım. Artık ClientBin içerisinde XAP dosyamızın yanı sıra ayrı ZIP dosyaları da bulunuyor. Bu dosyalar bir önceki adımda XAP dosyası içerisindeki DLL'lerin ZIP'lenmiş halleri. Anlayacağınız söz konusu DLL'ler artık XAP içerisinde değiller ve XAP dosyamız 5KB'a düşmüş durumda. DLL'ler harici ZIP'ler şeklinde paketlenerek XAP ile aynı konuma yerleştirilmiş.

Assembly'ler ayrı ZIP'lerde...
Assembly'ler ayrı ZIP'lerde...

Ne fark etti derseniz aslında konu çok basit. Yukarıdaki şekli ile DLL'ler ayrıldığında yazılımcıların bu DLL'leri ayrı ayrı update etme şansı oluyor. Sonuç olarak sunucu tarafındaki HTTP Header'a LastModifiedDate'e bakacak olan tarayıcı taraflı cachleme mekanizmaları uygulama değişmiş olsa da DLL'leri içerisinde ZIP'ler değişmemişse DLL'lere ait ZIP'leri indirmeyecek ve önbellektekini kullanacaktır. Bu durum da tabi ki performansta ciddi bir artış yaratır.

[AppManifest.xaml / Assembly Caching Öncesi]

<Deployment xmlns="http://schemas.microsoft.com/client/2007/deployment" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" EntryPointAssembly="assembly_caching_VB" EntryPointType="assembly_caching_VB.App" RuntimeVersion="3.0.40624.0">

  <Deployment.Parts>

    <AssemblyPart x:Name="assembly_caching_VB" Source="assembly_caching_VB.dll" />

    <AssemblyPart x:Name="System.ComponentModel.DataAnnotations" Source="System.ComponentModel.DataAnnotations.dll" />

    <AssemblyPart x:Name="System.Windows.Controls.Data" Source="System.Windows.Controls.Data.dll" />

    <AssemblyPart x:Name="System.Windows.Controls.Data.Input" Source="System.Windows.Controls.Data.Input.dll" />

    <AssemblyPart x:Name="System.Windows.Data" Source="System.Windows.Data.dll" />

  </Deployment.Parts>

</Deployment>

[AppManifest.xaml / Assembly Caching Sonrası]

<Deployment xmlns="http://schemas.microsoft.com/client/2007/deployment" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" EntryPointAssembly="assembly_caching_VB" EntryPointType="assembly_caching_VB.App" RuntimeVersion="3.0.40624.0">

  <Deployment.Parts>

    <AssemblyPart x:Name="assembly_caching_VB" Source="assembly_caching_VB.dll" />

  </Deployment.Parts>

  <Deployment.ExternalParts>

    <ExtensionPart Source="System.ComponentModel.DataAnnotations.zip" />

    <ExtensionPart Source="System.Windows.Controls.Data.zip" />

    <ExtensionPart Source="System.Windows.Controls.Data.Input.zip" />

    <ExtensionPart Source="System.Windows.Data.zip" />

  </Deployment.ExternalParts>

</Deployment>

Bildiğiniz üzere her XAP dosyası içerisinde Assembly'lerin adreslerini tutan birer de Manifest dosyası bulunuyor. Bu manifest dosyalarına dair birer örneği yukarıda inceleyebilirisniz. Assembly Caching aktif hale getirildikten sonra artık Assembly'lerin birer ExtensionPart olarak geçtiğini ve uygun ZIP dosyalarının adreslerinin de Source olarak verilmiş olduğunu görebiliyoruz.

Ya Kendi Kütüphanalerimiz?

Dikkat ettiyseniz yukarıdaki kütüphanalerin hepsi de Microsoft kütüphanaleriydi. Aynı sistemi kullanarak Silverlight projenize kendi yarattığınız bir kütüphaneyi referans alırsanız Assembly Caching aktif olsa da DLL'inizin yine XAP dosyası içerisine yerleştirildiğini göreceksiniz. Gelin bu durumu nasıl değiştirebileceğimize göz atalım.

İlk olarak ileriki aşamalarda Assembly'miz ile ilgili bir Public Key Token'a ihtiyacıımz olacağı için Assembly'mizin Strong Name'e ihtiyacı var. Bunun için Silverlight ile aynı Solution içerisine eklediğimizi varsaydığım örnek "Silverlight Class Library" projesine sağ tuş ile tıklayarak gelen menüden "Properties" komutunu verip "Signing" tabına geçmemiz gerekiyor.

Assembly'miz Strong Name ekliyoruz.
Assembly'miz Strong Name ekliyoruz.

Uygun bir "Key File Name" verip yaratacağınız key dosyasını seçerek bu işlemi tamamlayabilirsiniz. Daha önce de bahsettiğim üzere bize esas lazım olan Public Key Token olacak. Söz konusu key'i alabilmek için Class Library'i build ettikten sonra .NET Framework SDK ile beraber gelen Strong Name Utility'yi kullanmamız şart. Visual Studio ile beraber yüklenen "Visual Studio 2008 Commant Prompt"u başlat menüsünden bulup çalıştırdıktan sonra sn -Tp Assembly Adı şeklinde gerekli komutu çalıştırabilirsiniz.

Bize "Public Key Token" lazım!
Bize "Public Key Token" lazım!

Şimdi aldığımız bu Token ile aşağıdaki formatta bir XML dosyası yaratıp DLL'imiz ile aynı yere yani bin klasöründe Release altına koymalıyız.

[OrnekLibrary_VB.extmap.xml]

<?xml version="1.0"?>

<manifest xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

          xmlns:xsd="http://www.w3.org/2001/XMLSchema">

  <assembly>

    <name>OrnekLibrary_VB</name>

    <version>1.0.0.0</version>

    <publickeytoken>266fe0f58a4900bb</publickeytoken>

    <relpath>OrnekLibrary_VB.dll</relpath>

    <extension downloadUri="OrnekLibrary_VB.zip" />

  </assembly>

</manifest>

Gördüğünüz üzere dosya içerisinde DLL'in adı, sürüm numarası, Assembly adı ve Token bulunuyor. Ayrıca extension tagına ait downloadUri özelliğinde de dosyanın adresini veriyoruz. Adres kısmında eğer yukarıdaki şekilde bir dosya adı verirseniz Visual Studio sizin adınızda DLL'i söz konusu dosya adı ile ZIP'ler ve ClientBin içerisinde yerleştirir. İsterseniz downloadUri'ye harici bir adres de verebilirsiniz. Hatta bu adres XAP'ın bulunduğu alan adı haricinde de olabilir. Böyle bir durumda Visual Studio ZIP üretmeyecektir. Unutmayın harici bir alan adında caching yapacaksanız kesinlikle o alan adında ZIP dosyaları için uygun ClientAccessPolicy.xml dosyalarının da oluşturulmuş olması gerekir, aksi halde cross-domain-request yapılamaz.

Dosyamız hazır olduğuna göre onu DLL ile aynı yere kaydedebilirsiniz. Kaydederken Assembly Adı ve sonrasında da extmap.xml yazmanız şart.

Artık herşeyimiz hazır olduğuna göre Silverlight projemizi build edebiliriz. ClientBin içerisinde DLL'inizi ayrıca ZIPlenmiş olarak göreceksiniz!

Neden kullanayım?

Assembly Caching kullanmak uygulamanızın açılışını hızlandırmaz. Eğer hedef makinede daha önce uygulama açılmamış ise yine tüm kütüphanaler indirileceği için birşey değişmeyecektir. Fakat ikinci ve sonraki açılışlarda tarayıcı harici kütüphanaleri cachlediği için uygulamanız değişse de çok daha hızlı açılacaktır. Siz reference DLL'leri değiştirmedikçe ve harici kütüphanalere dair dosyalar yenilenmedikçe cacheleme tarayıcı tarafından yapılacaktır.

Özellikle harici bir alan adında kütüphaneleri önbelleklemek belki de aynı kütüphanaleri farklı uygulamalarda kullananlar için uygun olabilir. Böylece merkezi bir yönetim de kısmen sağlanmış olacaktır. Tüm bunları yaparken de ek kod yazmak zorunda kalmamak güzel bir avantaj olsa gerek.

Hepinize kolay gelsin!

Dikkat: Assembly Caching Silverlight 3 Beta iken çok daha farklı çalışıyordu. Bu konuda internette bir araştırma yaptığınızda karşılaşacağınız kaynakların çoğu size Assembly Caching'in sadece MS kütüphanaleri için yapılabileceğini ve kütüphanalerin MS tarafından host edildiğini söylemeyebilir. Silverlight 3 Beta'yken durum böyleydi  fakat artık değil. MS kütüphanaleri dahil olmak üzere hostingi sizin yapmanız gerekiyor ayrıca yukarıda da gördüğünüz üzere kendi kütüphanalerinizde de bu özelliği kullanabiliyorsunuz.