Günümüz iş uygulamalarında, artan ihtiyaçlar doğrultusunda yazılım geliştiriciler olarak bizler, ihtiyaçları karşılamak için pek çok farklı tasarım modellerini hayatımıza sokmak zorunda kalıyoruz. Bu modellerden son zamanlarda en popüler olanı ise .Net altyapısını kullanarak CodeDom sınıfı ile dinamik kod üretip, üretilen kodun uygulamada dinamik çalıştırılmasıdır. Böylece, müşteriden müşteriye değişen ihtiyaçlar nedeniyle, parametrik yapılarla uygulamanın çatısını bozabilecek seviyeye gelebilen değişikliklerin önüne geçmiş oluyoruz.
Peki, CodeDom sınıfını kullansak da kullanmasak da, bir şekilde dinamik oluşturduğumuz kodu çalıştırmak istediğimizde ne gibi problemlerle karşılaşırız?
.Net altyapısında dinamik oluşturulan dosyalar, aynı uygulama tarafından bir kez yüklendiğinde bellekten boşaltma şansınız kalmaz.
Eğer uygulamanız özellikle sistem kaynaklarına çok ihtiyaç duyan bir uygulama ise (web uygulaması gibi) bu durumda dinamik oluşturulan kodlarınızı belleğe yükleyip, kullanıp, işiniz bittiğinde de kaldırmak durumunda kalırsınız. İşte tam bu noktada da AppDomain sınıfı devreye girer. AppDomain sınıfı, çalıştırılan uygulamalar için izole bir ortam oluşturmanızı sağlayan diğer bir deyişle .Net uygulamaları ile kendi içerisinde kullandığı .net assembly dosyalarının birbirinden bağımsız çalışmasını sağlayan bir sınıftır. Avantajlarını sıralamak gerekirse;- Yüklenen dosyaların bellekten boşaltılmasını sağlar.
- Dinamik oluşturulan kodlarınız çalıştırıldığında oluşabilecek herhangi bir kararsızlık anında, ana uygulamanız bundan hiç etkilenmeden, yaratmış olduğu AppDomain kopyasını silerek çalışmasına kaldığı yerden devam eder.
Örnek Senaryo ve Kodlaması:
Uygulamanız tarafından belirlenen bir dizin içerisinde oluşturulan dinamik kod dosyalarının yüklenmesi, çalıştırılması ve bellekten atılması…
Burada, “belirlenen bir dizin” in altını çizmek zorundayız çünkü eğer uygulamanız bir web uygulaması ise, çalışma anında “Bin” dizini altında yapılacak herhangi bir dosya işlemi, IIS tarafından izlendiği için uygulamanızın sonlanmasını tetikler. Bunu önlemek için, derlenen dosyaların, yine web uygulamanızın erişebildiği farklı bir dizinde olması gerekmektedir. Böyle bir dizin, yine uygulamanızın kök dizini altında olup, “Bin” dizini ile aynı seviyede olabilir.
AppDomain kullanımı için öncelikle, AppDomainSetup nesnesinden bir kopya yaratmanız gerekir. Sonrasında ise özelliklerinin verilmesi gerekmektedir. Bu sınıfta öne çıkan özellikler aşağıdaki gibidir:
- ActivationArguments:.Net manifest tabanlı modeli belirler. Uygulamanın bağımlı olduğu dosyalar, güvenlik gereksinimi vs gibi uygulamanın çalışması için gereken bilgileri içerir.
- ApplicationBase: Uygulamanın bulunduğu dizini gösterir.
- PrivateBinPath:AppDomain nesnesinin, çalışma anında arama yapacağı dizinleri belirler. “;” ayıraç ile birden fazla dizin verilebilir.
AppDomain.CreateDomain(“HerhangiBirIsim”,AppDomain.CurrentDomain.Evidence,ads); |
Burada ikinci parametre için kodu çalıştıran ortamın “Evidence” nesnesi veya “null” değer geçirilebilir. Her iki durumda da, kodu yaratan ortamın kimlik bilgisi kullanılmış olur. Son olarak da, derlenen kodu çalıştırmak için gereken kodun yazılması gerekir.
ICodeExecution execCode = workerAppDomain.CreateInstanceAndUnwrap(fAssemblyNameWithNoExt, String.Concat(fDefaultNameSpace, ".", fClassNameToInvoke)) as ICodeExecution; execCode.DoExecute(ref executionParams); |
Burada CreateInstanceAndUnwarp fonksiyonuna, yaratılan assembly nin dosya uzantısı haricindeki adı ve yaratılacak nesnenin tam adı (bulunduğu isim uzayı [namespace] ve sınıf adı) parametre olarak geçirilir. Dönen nesne ise, her iki “AppDomain” in (kodun çalıştırıldığı ve yaratılan) bildiği bir arayüze tip dönüşümü ile kullanılabilir. Dolayısıyla burada bazı dikkat edilmesi gereken noktalar vardır;
- Hem derlenen assembly nin referanslarında hem de uygulamamızda kullanılmak üzere hazırlanması gereken bir dll ve bu dll içerisinde tip dönüşümü için kullanacağımız bir arayüz (örn: ICodeExecution) olmalıdır. Bu arayüz üzerinde tanımlı yordam ve özellikler üzerinden “AppDomain” ler arası veri alışverişi gerçekleşir.
public interface ICodeExecution { void DoExecute(ref object[] executionParams); } |
Ana uygulamadan yaratılan “AppDomain”e çalıştırma anında parametre geçirilmesi gerekiyorsa (örn : object[] executionParams), “Serializable” olarak işaretlenmelidir.
[Serializable] public class ExecutionParams { private int result = 0; public int Value1 { get; set; } public int Value2 { get; set; } public void CalculateResult() { result = Value1 + Value2; } public int GetResult() { return result; } } |
- Ek olarak bu arayüzü kullanan soyut bir sınıfımızın olması dinamik kodlamanın yapılacağı sınıfın altyapısını oluşturmamızı kolaylaştırır. İki farklı “AppDomain” in .Net remoting ile haberleşmesi için gereken tanımlanın bu soyut sınıfta yapılması, dinamik kodlamanın yapılacağı sınıfların da bu soyut sınıftan türetilmesi daha kolay ve hatasız kod oluşumuna olanak sağlar. Dolayısıyla sınıf, MarshalByRefObject nesnesinden türetilmeli ve yukarıda belirtilen ICodeExecution arayüzünü kullanmalıdır.
public abstract class CodeExecution : MarshalByRefObject, ICodeExecution { #region [_PUBLIC_] public CodeExecution() { } #region ..ICodeExecution Members.. public abstract void DoExecute(ref object[] executionParams); #endregion #endregion } |
public abstract class CodeExecution : MarshalByRefObject, ICodeExecution { #region [_PUBLIC_] public CodeExecution() { } #region ..ICodeExecution Members.. public abstract void DoExecute(ref object[] executionParams); #endregion #endregion } public interface ICodeExecution { void DoExecute(ref object[] executionParams); } [Serializable] public class ExecutionParams { private int result = 0; public int Value1 { get; set; } public int Value2 { get; set; } public void CalculateResult() { result = Value1 + Value2; } public int GetResult() { return result; } } |
AppDomain workerAppDomain = null; try{ AppDomainSetup ads = new AppDomainSetup(); ads.DisallowBindingRedirects = false; ads.DisallowCodeDownload = true; ads.ActivationArguments = AppDomain.CurrentDomain.SetupInformation.ActivationArguments; ads.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory; ads.PrivateBinPath = String.Concat(fCompilationFolder, ";", "Bin"); workerAppDomain = AppDomain.CreateDomain(fAssemblyNameWithNoExt, AppDomain.CurrentDomain.Evidence, ads); ICodeExecution execCode = workerAppDomain.CreateInstanceAndUnwrap(fAssemblyNameWithNoExt, String.Concat(fDefaultNameSpace, ".", fClassNameToInvoke)) as ICodeExecution; execCode.DoExecute(ref executionParams); } finally { AppDomain.Unload(workerAppDomain); } |
Tabii ki bu sadece örnek bir kod. Yüklenen assembly içerisinde oluşacak herhangi bir hatanın uygulamamıza zarar vermemesi için try..catch bloğu ile korunması gerekmektedir.
Ali KALFAOĞLU
Yorumlar
Yorum Gönder