.NET Framework ile geliştirdiğiniz uygulamaları genişletebilmek adına, 3.5 yıl önce Microsoft .NET Framework ile geliştirilmiş bir framework sunmuştu hatırlarsanız. Managed Extensibility Framework(MEF) ile uygulamalarımızı artık daha kolay bir şekilde genişletebilir hale gelmiştik. O zamandan bu zamana kadar MEF’in DI(Dependency Injection) ve IoC (Inverse of Control) açısından farkları, eksiklikleri çok tartışılsa da, MEF’in geliştirdiğiniz uygulamaları genişletebilmek adına ciddi anlamda faydalı bir çözüm olduğu sanırım ortak buluşulan nokta. Bu noktada MEF ile çeşitli DI ve IoC yaklaşımlarını uygulayabildiğinizi ama MEF’in bir IoC Container’ı olmadığının altını çizerek bu konuya çok girmeden, asıl konumuza geçiyorum…
.NET Framework 4.5 ile MEF tarafında gelen oldukça faydalı değişikliklerden bahsediyor olacağım. Biraz fazla kod örneği şeklinde olacağı için daha önce MEF ile çalışmamış kişiler için karmaşık olabilir. O yüzden daha önce MEF ile ilgili saçmaladığım şeyleri okumanızı tavsiye ederim.
- MEF ile esneklik kazanıyoruz…
- MEF’de “Part”lara kendi “metadata” bilgilerimizi nasıl ekleriz acaba?
- MEF’i basit bir WPF uygulaması ile daha iyi anlıyoruz…
- Managed Extensibility Framework(MEF)’de ki kataloglar…
- Asp.Net MVC Framework’de MEF ile Controller eklentileri…
MEF’in genişletilebilirliğini tanımlayan ‘Part’ kavramının kullanımı ile gelen yenilik, en faydalı ve kolaylık sağlayan özellik diyebilirim. Attribute bazlı tanımlar ile yarattığımız Part’ları daha sonra MEF tarafında yöneterek uygulamalarımızın genişletilebilirliğini gerçekleştiriyorduk. .NET Framework 4.5 ile Attribute tanımları yapmadan, Part’larımızı tanımlayabiliyoruz artık.
Biraz daha anlaşılabilir olması adına; belli arayüzlerimizi, uygulamalarımıza RegistrationBuilder ile register edebiliyoruz. Bu yeniliğin en güzel tarafı, mevcut geliştirmiş olduğunuz bileşenleri, bileşenlerde ekstra bir değişiklik yapmadan, MEF ile yeni geliştirdiğiniz uygulamalara dahil edebiliyor olmanız.
.NET Framework 4.5 ile MEF tarafında gelen bir başka özellikde Generic arayüz ve sınıfları artık ‘Export’ edebiliyor olmamız. Yani Generic bir sınıfımız var ise, bunu başka uygulamalarda kullanabilmek adına Part olarak yorumlayabiliyoruz.
Bu iki özelliği kod örnekleri ile biraz daha netleştirmeye çalışacağım. Genişletilebilirliğe de örnek olması adına bir tane müzik grubunu soyutlaştıran konsol uygulaması yapıyor olacağım. Müzisyen, enstürman, grup elemanı ve ünlü bir müzisyen grubumuzun özellikleri olacak. Grubumuza, grubumuzun yapısını bozmadan, her hangi bir enstürmanı çalan grup elemanını katabiliyor olacağız en sonunda. Öncelikle bu kavramları oluşturuyor olacağız. Bu kavramları oluştururken MEF ile ilgili ekstra hiç bir şey yapmıyor olacağımızın altını çizmek isterim.
Başlayalım…
//Generic bir Interface ile müzisyen tanımızın için bir arayüz oluşturuyoruz. //Play() metodu ile müzisyenlerin, bir şeyler çalacağını belirtiyoruz. public interface IMusicPlayer<T> where T : class { void Play(); } //Müzisyenlerin çalacağı enstürmanların base sınıfı olarak //MusicalInsturment'ı kullanıyor olacağız. //PlayTheInstrument() metodu ile ilgili enstürmanın nasıl çalınacağını tanımlıyoruz. public abstract class MusicalInstrument { public abstract void PlayTheInstrument(); } //Yukarıda oluşturduğumuz IMusicPlayer arayüzü ve MusicalInstrument sınıfından //generic bir BandMember<T> sınıfı oluşturuyoruz. Bu sınıf ile tanımlayacağımız //enstürmanları çalabilecek grup elemanlarını oluşturuyor olacağız. //Genişletebilirliği sağlayacak temel yapı diyebiliriz. Bu sayede herhangi bir //MusicalInsturment'e sahip olan BandMember'ımız grubumuz //dahilinde müzik yapabilecek. public class BandMember<T> : IMusicPlayer<T> where T : MusicalInstrument { //Generic yapıdan gelen T tipimiz MusicalInstrument tipinde olup, //BandMember'ın çalacağı enstürmanı belirtiyor. protected T _ensturment; public BandMember(T e) { _ensturment = e; } //IMusicPlayer arayüzünden gelen Play() metodu ile müzisyenimiz //bir şeyler çalmasını tetikliyoruz. (: public void Play() { Console.WriteLine(this.ToString() + " plays " + _ensturment.ToString()); _ensturment.PlayTheInstrument(); } } //Son olarak bir tane de enstürman tanımımızı yapalım. //Gitar olsun ilk enstürmanımız. public class Guitar : MusicalInstrument { private string _name = ""; public Guitar(string name = "Custom Guitar") { _name = name; } //Gitara özel bir Solo() metodumuz olsun public void Solo() { Console.WriteLine("Solo with " + _name + " is just amazing"); } public override string ToString() { return _name; } //MusicalInstrument'dan türeyen gitar sınıfımız, base sınıfdan gelen //PlayTheInstrument() metodunu kendi içinde tanımlıyor. Böylece gitara //özel yapıları oluşturabiliriz. public override void PlayTheInstrument() { Console.WriteLine("Guitar is played"); } }
Buraya kadar MEF ile ilgili ekstra hiç bir şey yapmadık. Standart C# yapıları ile grubumuzun nasıl bir oluşum dahilinde oluşabileceğinin tanımlamalarını yaptık.Dikkat ederseniz MEF’in daha önce ki Export kavramına ilişkin herhangi bir geliştirme yapmadım. Şimdi MEF ile bu tanımlamaların nasıl çalışacağını oluşturalım. Bunun için çok komplike bir yapı oluşturmadan, bir konsol uygulaması üstünden gidiyor olacağım.
class Program { //MusicalInstrument çalan BandMember'lardan bir liste tanımlıyoruz. //MEF sayesinde bu listeyi dolduruyor olacağız. public List<BandMember<MusicalInstrument>> Members { get; set; } static void Main(string[] args) { //RegistrationBuilder() MEF'in .NET Framework 4.5 ile gelen yeni //bir sınıf. Bu sınıf sayesinde, belirtebildiğimiz sınıfları //ya da arayüzleri uygulamamıza export edebileceğimizi tanımlıyoruz. //Bu noktada önce ki versiyonlarda yaptığımız attribute bazlı //tanımlamalara gerek kalmıyor. RegistrationBuilder ile BandMember //tipindeki sınıfların export özelliği olduğunu söyleyebiliyoruz. var registerBuilder = new RegistrationBuilder(); //ForTypesDerivedFrom<>() metodu ile direk belli bir sınıf ya da //arayüzü Export edeceğimizi söylüyoruz. //Zaten metod isminden aslında çok net bir anlam çıkarabiliriz. //Bir diğer metod ForTypesMatching<>() ile belli //kriterlere uyan sınıfları da belirtebiliriz. //Bu noktada, yukarıda yaratmış olduğumuz generic bir sınıfı //kullandığımızın altını çizmek isterim. //Bu .NET Framework 4.5 ile gelen yeni bir özellik. registerBuilder.ForTypesDerivedFrom<BandMember<MusicalInstrument>>() .Export<BandMember<MusicalInstrument>>(); //Bundan sonrası aslında önceki MEF özelliklerinden farklı değil. //Bir tane katalog oluşturup, //Import edeceğimiz part'ları MEF'in nasıl bulacağını belirtiyoruz. //Ben bu örnekte hepsini aynı projede yaptığım için AssemblyCatalog kullanıp, //ExecutingAssembly'i verdim. Farklı bir katalog //ile de yapabilirsiniz. Burada catalog'ların genişletildiğini ve ReflectionContext tipinde sınıflar //alarak nelerin export edileceğini belirtebildiğimizi göreceksiniz. RegistrationBuilder()'ı bu şekilde //catalog'lara dahil edebiliyoruz. var catalog = new AggregateCatalog(); catalog.Catalogs.Add(new AssemblyCatalog(Assembly.GetExecutingAssembly(), registerBuilder)); CompositionContainer container = new CompositionContainer(catalog); Program p = new Program(); //Son olarak GetExportedValues() ile grup elemanlarımızı yükleyip, onların metodlarını çağırabiliriz. p.Members = container.GetExportedValues<BandMember<MusicalInstrument>>().ToList(); int i=0; foreach (var item in p.Members) { i++; Console.WriteLine("Band Member #{0}",i); item.Play(); Console.WriteLine("------------"); } Console.ReadLine(); }
MEF ile grubumuzun yapısını da oluşturduktan sonra şimdi grup elemanlarımıza başlayalım.
//İlk grup elemanımız Jimi Hendrix olsun. //Dikkat ederseniz base sınıfından gelen constructor bir MusicalInstrument alıyor. //Bu sayede Jimi Hendrix'i tanımlarken gitar çaldığını bu şekilde tanımlayabilir. //Jimi Hendrix için pek güzel örnek olmasa da, daha sonra enstürmanını değiştirmek //istersek constructor'dan yapmamız yeterli olacaktır. public class JimiHendrix : BandMember<MusicalInstrument> { private string _name = "Jimi Hendrix"; public JimiHendrix() : base(new Guitar("Fender")) { } public override string ToString() { return _name; } }
İlk grup elemanımızı yarattıktan sonra programımızı çalıştırdığımız zaman aşağıdaki gibi bir çıktımız olacaktır.
Şimdi yeni bir grup elemanı daha yaratalım.
//Slayer'ın geçtiğimiz haftalarda ölen gitaristi olsun yeni elemanımız. public class JeffHanneman : BandMember<MusicalInstrument> { private string _name = "Jeff Hanneman"; public JeffHanneman() : base(new Guitar("ESP")) { //Jimi Hendrix sınıfına benzer bir sınıf ama burada //ek olarak base sınıftan gelen enstürmanının -ki burada gitar oluyor //Solo() metodunu çağırıyoruz. Console.WriteLine("Jeff Hanneman's solos are awesome....RIP..."); (_ensturment as Guitar).Solo(); } public override string ToString() { return _name; } }
Daha sonra ana programımıza hiç dokunmadan tekrar çalıştırdığımızda aşağıdaki gibi bir çıktımız olacaktır. Diğer grup elemanımız da görmüş olduğunuz üzere grubumuza dahil oldu. Burada Jeff Hanneman’nın constructor’ında Solo() metodunu çağırmış olmamızın da çıktısını görebilirsiniz.
Son olarak grubumuza bir davulcu ekleyelim isterseniz. (:
public class Drum : MusicalInstrument { private string _name = ""; public Drum(string name = "Custom Drum") { _name = name; } public override string ToString() { return _name; } public override void PlayTheInstrument() { Console.WriteLine("Drum is played"); } } public class DannyCarey : BandMember<MusicalInstrument> { private string _name = "Danny Carey(Tool)"; public DannyCarey() : base(new Drum("SONOR")) { } public override string ToString() { return _name; } }
Son olarak çıktımız…
Umarım biraz olsun .NET Framework 4.5 ile gelen MEF’in yeni özelliklerini anlatabilmişimdir. Ciddi anlamda kolaylık getiren yenilikler. Uygulama tasarımlarınızda düşünmenizde fayda sağlayacağına inanıyorum…
Yazıda geçen kodları aşağıdan indirebilirsiniz.