Microsoft “Roslyn” CTP ile Scripting

   Bir önceki yazımda sizlere Microsoft “Roslyn” CTP’sinin yayınlandığından bahsetmiş ve dilim döndüğünce “Roslyn”’in ne olduğunu, neden önemli olduğunu ve hangi noktalarda işimize yaracağını paylaşmıştım. Bu yazımda ise artık elimizi koda bulaştırmanın zamanı geldi diye düşünerek Roslyn’e giriş yapıyorum. Pek çok farklı noktada kullanılabileceğiniz Roslyn CTP’nin öncelikli olarak görece daha kolay ve pek çok yazılım geliştirici açısında da daha sık kullanılacağını düşündüğüm betik (script) desteğini anlatacağım.

   Pek çok yazılım geliştiricinin hayalidir 3.parti firmalar/kişiler tarafından genişletilebilecek uygulamalar yazmak. Pek çoğumuz bunu interface/abstract base class yardımıyla sunduğu API’ler ve reflection yardımıyla yapabilmekte; ama bu yaklaşımın ufak bir sorunu var: son kullanıcı hiç bir zaman işin içinde olmuyor. Geliştirilecek eklentiler ancak 3.parti bir firma ya da konusunun ehli bir yazılım geliştirici tarafından yazılabilmekte, derlenen assembly ise uygulama başlarken ya da başladıktan sonraki bir zamanda arayüzler üzerinden yüklenmekte. Burada kaçırılan ya da desteklenmeyen nokta ise uygulamaya son kullanıcının da müdahale etmek, ihtiyaçlar doğrultusunda genişletmek isteyebileceği.

   Bir ofis uygulamasının ele alalım, Visual Basic ile yazılan bir macro sayesinde temel düzeyde makro bilgisine sahip “amatör” yazılımcılar bile harikalar yaratabilmekte, benzer durum Visual Studio için de geçerli. Basit bir kaç komut ve uygulamanın sunduğu ortam sayesinde uygulamayı geliştirenlerin aklına gelmeyen çeşitli senaryolar ortaya çıkara harika makrolar yazılabilir. Roslyn CTP’sinin bize sunduğu ilk avantaj işte tam böylesi bir durumda karşımıza çıkmakta; scripting.

   Roslyn ile birlikte sahip olacağımız script desteğini açıklamak adına oldukça basit bir uygulama ile başlayalım isterseniz. Üzerinde sade bir textbox ve buton olan bir windows forms ekranı;

Roslyn ile scriptin denemelerimizi yapacağımız basit uygulamamızın ana ekranı

   Bu basit uygulamamızdaki hedefimiz text alana girilen matematiksek ifadeleri “çalıştır” butonuna basıldığında işleterek sonucu kullanıcıya göstermek olsun. Uygulamamızın bu basit isterini incelediğimizde aslında temel düzeyde verilen ve sadece dört işlemle sınırlı olan bir betiğin işletilmesi olarak görebiliriz.

   Uygulamamızı geliştirmeye Roslyn scripting kütüphanelerini ekleyerek başlayalım. Roslyn CTP’sinin kurulumu sonrasında, ihtiyacımız olan tüm kütüphaneler GAC’da kayıtlı olarak bulunacaktır. Eklememiz gereken kütüphaneler ise;

  • Roslyn.Compilers
  • Roslyn.Compilers.CSharp

   Bu noktada belirmem lazım ki uygulamamın sadece C# scriptlerini desteklediği varsayımıyla hareket ediyorum. Sizler geliştirdiğiniz uygulamalarda C# yerine/yanında Visual Basic ile script desteği sunmak isterseniz Roslyn.Compilers.VisualBasic referansını eklemeniz gerekli olacaktır.

   Referansları ekledikten sonra uygulamamızın ana ekranındaki çalıştır butonunun click olayından önce son kullanıcının yazacağı aritmetik ifadenin çalıştırılacağı script motorunun ilklendirilmesi gerekli. Bunun için sınıfımız içerisinde bir değişken oluşturmalıyız;

private ScriptEngine engine = new ScriptEngine();

  Tabi referansları eklemek yukarıdaki kodu derlenmesi için tek başına yeterli olmayacaktır. Derleyici bizden ScriptEngine sınıfının nerede bulunduğunu belirtmemizi isteyecektir. Bunun için aşağıdaki using satırını da eklemelisiniz;

using Roslyn.Scripting.CSharp;

   Hemen ardından da kullanıcının textbox’a yazdığı ifadeyi çalıştıralım;

var sonuc = engine.Execute<long>(betikIcerigi.Text);

  Bu kadar kolay… Burada kullanıcının her zaman için sonucu long olan bir ifade girdiği varsayımıyla hareket ettiğim için script motorumuza çalışmasını söylerken sonucu long olarak istediğimi de belirtmeyi ihmal etmiyorum. Bu sayede gereksiz bir cast işleminden kurtulmuş oluyorum. Son olarak da işlemin sonucunu kullanıcımıza gösterelim;

MessageBox.Show("Yapılan işlem sonucu : " + sonuc);

  Bu üç satırlık kodu butonumuzun click fonksiyonu içerisine yazarak uygulamamızı çalıştırdığımızda  (1+3)*(9-2*2) gibi bir matematiksel işlem için 20 sonucunu verdiğini aşağıdaki ekran görüntüsünde görebiliriz;

islemSonucu

   Bu basit matematiksel işlemlere bakarak yapabileceklerimizin sadece bunlar olduğunu kesinlikle düşünmeyin, unutmayın elimizin altında C# var! Örneğin aşağıdaki gibi basit bir script yazarak özyenilemeli (recursive) olarak faktöriyel hesabını kolaylıkla yapabiliriz;

long faktoriyel(int n) {   
    if(n==1) { return 1; }

    return faktoryel(n-1) * n;
 }

faktoryel(5);

Roslyn'in sunduğu C# desteği sayesinde uygulamalarımızda güçlü bir scipt desteği sunabiliriz

   Yukarıdaki iki örnekte ScriptEngine’e string türünde bir içerik sunmakla birlikte ExecuteFile fonksiyonunu kullanarak istersek dosya sisteminde bulunan bir script’i çalıştırmamız da mümkün.

  Gördüğünüz gibi Roslyn CTP ile uygulamamızı iki satırlık basit bir kod yardımıyla script desteği sunar hale getirebiliyoruz; fakat gerçek hayatta uygulamalarımız bu örneklerden çok daha karmaşık isterlerle karşımıza çıkacaktır.Örneklerimizde dikkat ederseniz scripti çalıştıran host uygulamayla ya da bir önceki çalıştırılan script ile herhangi bir etkileşim bulunmamakta; ama gerçek hayattaki pek çok senaryo için böylesi bir ister kaçınılmaz olarak karşımıza çıkacaktır. Şanslıyız ki Roslyn geliştiricileri böylesi senaryoları da unutmamışlar ve birden fazla farklı çalıştırma arasında verilerin iletilebilmesi için oturum mantığını kullanmışlar.

   Fabrika paterni ile tasarlanmış olan Session nesnesinin bir örneğini alabilmek için Create fonksiyonu kullanılabilir. Devamında aynı oturum içerisinde çalıştırılmasını istediğimiz her bir script için ScriptEngine’in Execute fonksiyonuna kullanılacak oturum nesnesi geçilmesi yeterli olacaktır.

private ScriptEngine engine = new ScriptEngine();
private Session oturum = Session.Create();

//...

private void calistir_Click(object sender, EventArgs e) {
    var sonuc = engine.Execute<long>(betikIcerigi.Text, oturum);

    MessageBox.Show("Yapılan işlem sonucu : " + sonuc);

}

  Kodumuzu yukarıdaki şekilde düzenledikten sonra aşağıdaki iki script’i arka arkaya çalıştırdığımızda ikinci script’in sorunsuz olarak çalıştığını ve 8 sonucunu verdiğini görebilirsiniz.

int x = 3;
x + 5;

   Script host eden uygulama ile etkileşim için yine Session nesnesinin Create fonksiyonu kullanılabilir. Bu fonksiyonun host nesneyi parametre kabul eden overload’ı yardımıyla istediğimiz etkileşim sınıfını script kullanımına sunabiliriz. Bu noktada sizlere tavsiyem tüm uygulamanıza ya da formunuza doğrudan erişim vermemeniz, bunun yerine güvenlik adına sınırlarınızı sizin çizdiğiniz bir ara sınıf oluşturup vermeniz. Bu sayede script’lerin neler yapıp neler yapamayacağını daha netleştirmiş olursunuz. Örnek vermek gerekirse, aşağıdaki kod script’lere uygulamanızın ana penceresinin başlığını değiştirebilme imkanı sağlarken formun tamına erişemeyecekleri için aynı zamanda uygulamanızı koruyacaktır;

namespace Com.Enterprisecoding.Roslyn.ScriptingDemo {
    public partial class AnaEkran : Form {
        private ScriptEngine engine;
        private Session oturum;
        private HostEtkilesim hostEtkilesim;

        public AnaEkran() {
            InitializeComponent();

            engine = new ScriptEngine();
            hostEtkilesim = new HostEtkilesim(this);
            oturum = Session.Create(hostEtkilesim);
        }

        private void calistir_Click(object sender, EventArgs e) {
            engine.Execute(betikIcerigi.Text, oturum);
        }
    }

    public class HostEtkilesim {
        private Form anaEkran;

        public HostEtkilesim(Form anaEkran) {
            this.anaEkran = anaEkran;
        }

        public void UygulamaBasliginiDegistir(string yeniBaslik) {
            anaEkran.Text = yeniBaslik;
        }
    }
}

  Kodumuzu çalıştırdığımızda HostEtkileşim içinde tanımlı olan tüm fonksiyonlar script’imiz içerisinde kullanılabiliyor olacaktır. Burada dikkat edilmesi gereken fonksiyonların script içerisinde kullanılması sırasında aşağıdaki ekran görüntüsünde de olduğu gibi doğrudan fonksiyon adının verilmesidir.

HostNesneFonksiyonu

 

   Benzer yaklaşıkları kullanarak sizde kendi uygulamalarınıza oldukça kolay şekilde script desteği sunabilirsiniz.

   Son olarak sizleri uyarmam gereken bir nokta daha bulunuyor; ScriptEngine’in Execute fonksiyonu her çağrıldığında çalışan script için geçici bir assembly oluşturularak uygulama tarafından yüklenecektir. Bu durumda uzun soluklu çalışan uygulamalarda çok fazla script çalıştırılması halinde bir problem oluşturacaktır.

Hafızada yüklü kalan assembly'ler

   Bu durum .net framework’ün bir application domain’de yüklenen assembly’leri sadece bu application domain sonlandığında kaldırıyor olması kaynaklıdır. Bu durumu düzeltmek adına scriptler ayrı bir application domain içerisinde çalıştırılarak işleri bitince bu ayrı application domain sonlandırılabilir.

Fatih Boy

Ankara'da yaşayan Fatih, bir kamu kurumunda danışman olarak çalışmaktadır. ALM süreçleri, kurumsal veri yolu sistemleri, kurumsal altyapı ve yazılım geliştirme konularında destek vermektedir. Boş zamanlarında açık kaynak kodlu projeler geliştirmeyi ve bilgisini yazdığı makalelerle paylaşmayı seven Fatih, aynı zamanda Visual C# ve Visual Studio teknolojileri konusundan Microsoft tarafından altı yıl üst üste MVP (En Değerli Profesyonel) ödülüne layık görülmüştür. İş hayatı boyunca masaüstü uygulamaları, web teknolojileri, akıllı istemciler gibi konularda Asp.Net, Php, C#, Java programlama dilleri ve MySql, MsSql ve Oracle gibi veritabanı yönetim yazılımları ile çalışmıştır. İngilizce ve Türkçe olarak yayınlanan makalelerini gerek İngilizce bloğunda, gerekse de Türkçe bloğunda bulabileceğiniz gibi web sitesinden de açık kaynak kodlu geliştirdiği yazılımlarına ulaşabilirsiniz. vCard - Twitter - Facebook - Google+

2 yorum

  1. Mehmet Şahin   •  

    Elinize sağlık. Devamını bekleriz.

Bir Cevap Yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir