Posts Tagged With 'MEF'

  • Oct
  • 28
  • 2011

MEF 2.0 Preview 4 yayınlandı…

Tags: , , | View: 172 | Comments:

MEF’in(Managed Extensibility Framework) geliştirmesi devam eden 2.0 sürümünün Preview 4′ü yayınlandı. Diğer Preview’lara nazaran büyük yenilikler içeren bu yeni sürümü, MEF ile ciddi anlamda uğraşan kişilere tavsiye ederim. İndirmek için bu adrese gitmeniz yeterli.

Kısaca benim neler en çok hoşuma gitti onlardan bahsetmek isterim, belki daha cazip gelir bu haber bu sayede (:

Benim en çok hoşuma giden yenilik MEF Part’larını belirtmek için kullandığımız attribute’ları yeni gelen RegistrationBuilder ile sayesinde farklı bir şekilde belirtebiliyor olmamız. Mesela belli bir sınıfta belirtilen Part’larımızdan türüyen export edilecek diğer sınıflarımız için [Export] attribute’unu tanımlamak gerekmekteydi. Hem kendini tekrarlayan bir yapı oluşmasıns sebep olan hem de bazen unutulabilen ve  runtime’da fark edilebilen bir durum oluyordu. Yeni gelen RegistrationBuilder ile aşağıdaki gibi Export edilecek Part’larımı daha kolay ve anlaşılır bir şekilde tanımlayabiliyoruz.

 //ForTypesDerivedFrom() metodu ile BaseModule gibi kendi sınıfımızdan
 //türümiş tüm objelerin export edilebileceğini söylüyoruz.
 var builder = new RegistrationBuilder();
 builder.ForTypesDerivedFrom().Export();

//MEF kataloglarına da RegistrationBuilder objemizi verebiliyoruz
 var catalog = new AssemblyCatalog(System.Reflection.Assembly.GetExecutingAssembly(), builder);
 

Generic olan sınıfları artık Export edebiliyoruz. Yani IGenericType<T> gibi bir arayüzümüz olduğunda ve bundan türeyen sınıflarımızı Export etmek istediğimiz de [Export(IGenericType<>)] şeklinde attribute’u güncellemek yeterli olacak.

Ayrıca Exception mesajlarında ki iyileştirmeler de Run-time’da alınan hataları biraz daha açıklayıcı hala getiriyor. Bu da hata tespiti için çok can kurtaracak.

Bu arada MEF 2.0 versiyonu, .NET Framework 4.5′ün içinde de geliyor olacak. Ama tabi ondan önce çıkar mı bilemiyorum…Çıkarsa ne ala…Neyse şimdilik bu kadar…

 

 

 

 

  • May
  • 15
  • 2010

Asp.Net MVC Framework’de MEF ile Controller eklentileri…

Tags: , , , | View: 1,000 | Comments:

Yine bir MEF yazısı…Bu sefer MEF’in kullanım alanlarını inceleyerek MEF’in gerçekten faydalı olabileceğini biraz daha net bir şekilde kavramaya çalışacağız. Bir önceki yazımda, Asp.Net MVC Framework’de “Controller” kavramını basitçe özetleyerek kendi çapımda anlatmaya çalışmıştım. Ne kadar faydalı oldu bilemeyeceğim ama bu sefer de, Asp.Net MVC Framework’de MEF’i nasıl uygulayabiliriz bunu anlatmaya çalışacağım.

Başlıyoruz…

Asp.Net MVC uygulaması olarak, Visual Studio’da proje olarak gelen Asp.Net MVC projesini kullanıyor olacağız. Baştan bir MVC uygulaması yazmaya şimdi gerek yok, hazır yazılmışı olduğunu farz ederek bu proje şimdilik işimizi görecektir.

Aşağıdaki gibi bir proje yapımız olduğuna göre, tam olarak ne yapacağız buna geçelim. Amacımız MEF’in esnekliğini kullanarak Asp.Net MVC uygulamamızı esnetmek. Yani takıp çıkarabileceğimiz, bir birinden bağımsız bileşenler ile uygulamamıza genişletebilir bir yapı yapmak. Bu noktada takıp çıkarabileceğimiz bileşenler derken bunların “Controller”lar olduğunu belirtmekte fayda var sanırım.

“Controller”ların takıp çıkarılması olayını biraz daha açıp, “Controller”ların ayrı *.dll’ler şeklinde Asp.Net MVC uygulamamıza entegre olabilen bileşenler demek sanırım daha açık olacaktır.

Önce “Controller”ımızı yapalım…

Takıp çıkarılabilen bir “Controller” için Visual Studio’da yeni bir “Class Library” projesi yapmamız gerekmekte. Bu projenin çıktısı bizim MVC uygulamamıza dışardan ekleyebileceğimiz “Controller”ımız olacak. Projemizin aşağıdaki gibi bir yapısı olması gerekmekte. (Not: Poll ismini bu örnekte anket yaptığımızı farz ederek koydum)

Index.asp’miz View tarafımız *.cs dosyamız ise “Controller”ımız olacak. Aşağıdaki kod örneği de şimdilik yeterli olacaktır.

using System.Text;
using System.Web.Mvc;
using System.ComponentModel.Composition;
using Common;

namespace Plugins.Poll
{
    [Export("MVCController",typeof(IController))]
    [PluginMetadata("Poll", "1.0", "Arda")]
    [PartCreationPolicy(CreationPolicy.NonShared)]
    public class Poll : Controller
    {
        public ActionResult Index()
        {
            return View("~/Plugins/Views/Poll/Index.aspx");
        }
    }

}
  • May
  • 04
  • 2010

Managed Extensibility Framework(MEF)’de ki kataloglar…

Tags: , | View: 796 | Comments:

MEF‘in en önemli bileşenlerinden biri katalog(catalog) kavramı. Belli ihtiyaçlar ve yaklaşımlar doğrultusunda MEF içinde kullanabileceğimiz çeşitli kataloglar mevcut. Bunların ne olduğuna geçmeden, bu katalog kavramı tam olarak ne önce bunu anlayalım…

Managed Extensibility Framework(MEF)‘de, “Part”lar bu kataloglar ile framework bünyesine dahil olur. Bir başka deyişle, MEF, [Export] ettiğimiz bileşenlerimizi(ki bunlar “Part”lar oluyor) bu kataloglar yardımı ile bulur. Yapboz parçalarının tutulduğu kutu, ya da bilgisayar parçalarının birleştiği kasa gibi düşünebiliriz aslında. Bu kataloglar neler, kısaca bakalım bir, ne çıkacak içlerinden…:)

DirectoryCatalog

Adından aslında anlaşılıyor. “DirectoryCatalog”, MEF’de “Part”ların, belli bir dizindeki assembly’ler içinde aranmasını sağlıyor. Yani MEF ile geliştirdiğimiz uygulama eğer DirectoryCatalog kullanıyrsa, belirttiğimiz bir dizin içerisindeki dosyalarda(*.dll gibi), [Export] ettiğimiz tipleri arayacaktır. Sanırım MEF’de en çok kullanılan, daha doğrusu tercih edilen katalog budur…Yanlız burda hatırlatmak gereken bir nokta var. DirectoryCatalog’u Silverlight’ın mimarisinden dolayı, Silverlight uygulamalarında kullanamıyoruz.

AssemblyCatalog

MEF’in “Part”ları bellirtilen assembly’de aramasını sağlayan katalog tipi AssemblyCatalog oluyor. Uygulamamızın kendi içinde gizlediğimiz [Export]‘ları kullanabilmek adına kullanımı tercih edilebilir. Ya da “reflection” ile yarattığımız “assembly”leri kullanmak için de kullanabiliriz.

TypeCatalog

MEF’in belli tipte “Part”ları aramasını belirttiğimiz katalog tipi de TypeCatalog oluyor. Bu uygulama içersindeki belirttiğimiz tipte olan tüm [Export]‘ları MEF tarafından kullanılabilir hale getiriyor.

AggregateCatalog

MEF’in birden fazla “Catalog” ile arama yapmasını istediğimiz zaman AggregateCatalog’dan faydalanıyoruz. Bu katalog tipi, çalışma zamanında farklı katalogları bünyesine ekleyerek, birden fazla katalog tipinde MEF Part’larının aranmasını sağlıyor.

Bu sefer hiç kod yazmadık ama MEF için önemli bir kavramı biraz daha netleştirmeye çalıştık. Umarım faydalı olmuştur. Bu kataloglar ile örnekleri ilerleyen yazılarda yapıyor olacağım…Farklarını,artılarını,eksilerini çok daha iyi beraber anlıyor oluruz…Şimdilik bu kadar…

  • May
  • 02
  • 2010

MEF Part’larının metodlarını nasıl çağırıyoruz?

Tags: , | View: 614 | Comments:

Arayı çok soğutmadan yeni bir MEF yazısı ile MEF’i biraz daha iyi anlama yönündeki girişimlerimiz sürüyor.Sürecekte…Önceki yazılarımda MEF ile ilgili örnekler vererek, az biraz MEF’in ne olduğunu çözmeye yaklaşmıştık hatırlarsanız.Tekrar göz atmak gerekirse;

  1. MEF ile esneklik kazanıyoruz…
  2. MEF’de “Part”lara kendi “metadata” bilgilerimizi nasıl ekleriz acaba?
  3. MEF’i basit bir WPF uygulaması ile daha iyi anlıyoruz…

Bu sefer de MEF’in bir den fazla “Part”ı, kendi tabirimiz ile “plugin”lerimizi nasıl canlandırdığını ve metodlarını nasıl çağırabileceğimizi anlatmaya çalışacağım. Ve önceki yazılarda ki gibi, kod yazıyoruz…Yaşasın kod yazmak… :)

Önceki yazılardan hatırlarsanız işe yaramayan bir IPlugin arayüzümüz vardı…Bu işe yaramayan arayüzümüzü biraz daha anlamlı kılarak bir metod eklemek ile başlayalım önce. Bu metodu ekleyerek, bu arayüzden türeyen her “Part”ın(yani plugin’nin) bu metodu tanımlamasını zorunlu hale getiriyoruz.

    public interface IPlugin
    {
        //Bu arayüzü kullana nesneler
        //bu metod ile kendi içindeki
        //bir değeri dışarı versin...
        string GetPrivateData();
    }

Bu arayüzümdeki değişikliği önceki yazılardan aşina olduğumuz FirstWPFPlugin projesine de yansıtıyoruz.

public partial class FirstWPFPlugin : UserControl,IPlugin
    {
        //İçerde gizlediğimiz gereksiz bir değer
        private string _xValue = "";
        public FirstWPFPlugin()
        {
            InitializeComponent();
            _xValue = "Birinci plugin içinde ki private X değişkeninin değeri";
        }

        //Sanırım fazla açıklamaya gerek yok (:
        private void button1_Click(object sender, RoutedEventArgs e)
        {
            MessageBox.Show("Hello world");
        }

        //IPlugin arayüzünden gelen metodumuz...
        public string GetPrivateData()
        {
            return _xValue;
        }
    }

Bu şekilde FirstWPFPlugin’miz artık GetPrivateData metodu ile kendi içindeki bir değişkeni dışarı veriyor. Şu ana kadar bir şey yok…Şimdi MEF tarafına geçelim…Öncelikle ilk yazımdaki “Import” kavramını hatırlamakta fayda var. IPlugin arayüzü ile “Export” ettiğimiz “Part”ları, PluginFactory sınıfımız ile “Import” ediyor olacağız. Bu sayede arayüzümüz bize sağladığı metodları, ana uygulamamızda kullanabileceğiniz. Bu arayüzden türeyen bir çok plugin olabileceğinden, arayüzün metodu her plugin için farklılık gösterebilecektir.

Burda önemli olan bir diğer noktada ana uygulamamıza birden fazla plugin yükleyebiliyor olabilmemiz. Bunun için “ImportMany” özelliğini kullanıyor olacağız. Bunun için PluginFactory sınıfımızda aşağıdaki gibi bir değişken tanımlamamız gerekmekte.

    public class PluginFactory
    {

        //ImportMany özelliği ile IPlugin arayüzünden türeyen ve
        //"PluginForWPF" şeklinde belli bir kontrat ile tanımladığımız
        //plugin(Part)'ları yükleyebileceğimizi belirtiyoruz.
        //IEnumerable tipinde olması, birden fazla plugin olabileceği
        //için gerekli...
        [ImportMany("PluginForWPF", typeof(IPlugin))]
        IEnumerable<Lazy<IPlugin, IPluginMetaData>> Plugins;
        .
        .
        .
        .

Şimdi PluginFactory sınıfımızda yazacağımız bir metod ile IPlugin arayüzünden türeyen “Part”larımızı, ya da kendi tanımlamamız ile plugin’lerimizi bu “Plugins” değişkenine atamamız lazım…Bunun için de aşağıdaki gibi bir kod şimdilik işimizi görecektir.

        //Kullanıcı arayüzümüzden çağıracağımız bu metod ile "Plugins"
        //değişkeninin yaratıyoruz.
        public string ExecutePluginMethod(string pluginName)
        {
            //Önceki yazılarda container nesnesini, PluginFactory
            //sınıfımızı yaratırken yaratmıştık hatırlarsanız.
            //container'ımızın, ComposeParts() metodu ile,
            //PluginFactory sınıfının içindeki Import özelliği olan
            //değişkenleri yaratıyoruz. this parametresi PluginFactory
            //nesnesini belirtiyor.
            container.ComposeParts(this);

            //Birden fazla plugin yani "Part" olabileceği için,
            //hangi plugin'nin metodunu çalıştırmak için LINQ ile
            //yüklenen plugin'lerin metadata'larını sorgulayarak,
            //parametrede gelen plugin ismi ile istediğimiz plugin'i
            //alıyoruz.
            IPlugin s = (IPlugin)Plugins.Where(e => e.Metadata.Name == pluginName).FirstOrDefault().Value;

            //Ve daha sonra IPlugin arayüzümüzün istediğimiz metodunu çalıştırıyoruz.
            return s.GetPrivateData();
        }

Artık ana uygulamamıza yüklenen “plugin”lerin istediğimiz metodlarını çalıştırabiliriz. Benzer bir şekilde bir çok metodu çalıştırmak artık sizin elinizde…Şimdilik bu kadar…Bir sonraki MEF yazısında tüm bu örneklerdeki kavramları daha iyi anlıyor olacağız.Çok pratik yaptık, biraz da teoriye inelim değil mi…(:

Not: Bu yazıda geçen proje örneğini buradan indirebilirsiniz.

  • May
  • 01
  • 2010

MEF’i basit bir WPF uygulaması ile daha iyi anlıyoruz…

Tags: , , | View: 1,165 | Comments:

Önceki bir kaç yazımda MEF(Managed Extensibility Framework) ile ilgili bir şeyler yazmış, kısaca ve basitçe anlatmaya çalışmıştım. Bu yazımda onları birleştirerek ve ilerki yazılarda da kullanabileceğimiz bir örnek olması adına MEF’in WPF ortamında basitçe uygulanmasına değinerek MEF’i biraz daha iyi anlamaya çalışacağız.

Biz yazılımcılar daha çok kod kavramını sevdiğimiz için :) çok karmaşık olmasa da diğer yazılara nazaran biraz daha kod örneği içeren bir yazı olacak bu şimdiden belirtim. Bu arada kodları elimden geldiğince çok basit yazıp, ilerleyen yazılarda başka konular ile onları değiştirerek biraz daha eli yüzü düzgün hale getireceğiz.

Ne yapacağız?

Gerçek hayatta kullanamayacağımız, anlamsız bir WPF “plugin” uygulaması yapıyor olacağız. Gerçek hayatta kullanamayacağız belki ama kullanılabilir uygulamalar yapmamız açısından vizyon katacak bir örnek olacak.

Başlıyoruz…

Öncelikle yandaki resimdeki gibi bir proje yapısı oluşturalım. “WPFMEF” ana WPF uygulamamız olacak. Bu uygulama, yüklenen “plugin”leri çalıştırmakla yükümlü olacak. “FirstWPFPlugin” ve “SecondWPFPlugin” projeleri ise “WPF User Control” olarak yaratacağımız, WPF’e kullanıcı kontrolü olarak yükleyeceğimiz,”plugin”ler olacak...”Common” projesi ise tüm uygulamalarda ortak olarak kullnabileceğimiz bileşenleri içerecek.

Ana uygulama…

“WPFMEF” projesi az önce belirttiğim gibi MEF ile oluşturduğumuz “plugin”leri çalıştaracak uygulama. Basitçe aşağıdaki gibi bir ekran görüntüsüne sahip.

Ya da aşağıdaki gibi bir XAML’e…

<Window x:Class="WPFMEF.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="502" Width="775">
    <Window.Resources>
        <DataTemplate x:Key="ListBoxDataTemplate">
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="100"></ColumnDefinition>
                    <ColumnDefinition Width="90"></ColumnDefinition>
                </Grid.ColumnDefinitions>
                <TextBlock Grid.Column="0"  Text="{Binding Path=Name}"></TextBlock>
                <Button Grid.Column="1" Width="50" Content="Çalıştır" CommandParameter="{Binding Path=Name}" Click="Button_Click"></Button>
            </Grid>

        </DataTemplate>

    </Window.Resources>
    <Grid>
        <ListBox Height="374" ItemTemplate="{StaticResource ResourceKey=ListBoxDataTemplate}"   HorizontalAlignment="Left" Margin="12,54,0,0" Name="listBox1" VerticalAlignment="Top" Width="190" />
        <Label Content="Sistemde yüklü pluginler:" Height="28" HorizontalAlignment="Left" Margin="0,20,0,0" Name="label1" VerticalAlignment="Top" Width="190" />
        <Canvas Height="374" HorizontalAlignment="Left" Margin="211,54,0,0" Name="canvas1" VerticalAlignment="Top" Width="502" />
        <Label Content="Plugin içeriği" Height="28" HorizontalAlignment="Left" Margin="211,20,0,0" Name="label2" VerticalAlignment="Top" />
    </Grid>

</Window>

Fark etmiş olduğunuz gibi basit bir ekran…Plugin’lerin listeneceği bir “Listbox” ve seçilen “plugin”nin çalışması için bir “Canvas” kontrolü içeriyor temel olarak. Bu uygulama için “plugin”leri yönetecek bir uygulama demiştik en başta hatırlarsanız. Peki nasıl yönetecek?…

Bunun için bu projede PluginFactory diye bir sınıf yaratmamız lazım. Bu sınıf ile “plugin”lerimizi yöneteceğiz. Sonraki yazılarda da bu sınıfı geliştirip, güzelleştiriyor olacağım. Aşağıdaki kod parçası şimdilik işimize yarayacaktır. Yorum şeklinde kodları açıklamaya çalıştım.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel.Composition.Hosting;
using Common;
using System.Windows.Controls;

namespace WPFMEF
{
    public class PluginFactory
    {
        //Plugin'lerimiz yüklü olduğu dizin
        private string pluginPath;

        //Plugin'lerimizin yüklü olduğu dizinin
        //MEF tarafındaki karşılığı.Adından da
        //anlaşıldığı üzere katalog gibi düşünebiliriz.
        //Uygulamada Plugin'lerimizin tutulduğu
        //yer de diyebiliriz.
        private DirectoryCatalog catalog;

        //Import edilen Part'ların,yani plugin'lerin
        //yaşam sürecini sürdürdüğü kısım olarak tanımlamak
        //bu aşamada yeterli olacaktır.
        private CompositionContainer container;

        //PluginFactory sınıfımızı yaratıyoruz.
        //Bunun için dışarıdan, plugin'lerin sistem üzerinde
        //nerede olduklarını parametre olarak veriyoruz.
        //İçeride ilgili atamaları yapıp,gerekli nesneleri
        //yaratıyoruz.
        public PluginFactory(string pluginPath)
        {
            try
            {
                this.pluginPath = pluginPath;
                this.catalog = new DirectoryCatalog(pluginPath);
                this.container = new CompositionContainer(catalog);
            }
            catch (System.IO.DirectoryNotFoundException ex)
            {
                throw ex;
            }
            catch (Exception ex)
            {
                throw ex;
            }

        }

        //Bu metod ile sisteme yüklenmiş tüm "MEF Part"larını,
        //ya da kendi tabirimiz ile "plugin"leri listeliyoruz.
        //Önceki yazılarımda bahsetmiş olduğum "metadata" bilgisine
        //sahip olan "plugin"leri listeliyoruz.
        public List<IPluginMetaData> GetAllPlugins()
        {
            List<IPluginMetaData> list = new List<IPluginMetaData>();

            //Burada kendi interface'imiz ile yarattığımız,MEF Part'larını
            //alıyoruz.Belirttiğimiz dizindeki diğer 'Part'lar bu sayede gelmeyecektir.
            //Ayrıca bu noktada yine kendi yarattığımız IPluginMetaData'sı ile
            //'Part'ları listemize ekliyoruz.
            //"PluginForWPF" parametresi 'Plugin'lerimizi işaretlediğimiz
            //bir kontrat bilgisi olarak bu metodda kullanılıyor. "Plugin" yaratırken
            //tekrardan dönüyor olacağız.
            var controllerExport = container.GetExports(typeof(IPlugin),
                                                        typeof(IPluginMetaData),
                                                        "PluginForWPF");
            //Yukarıdaki ifadeyi LINQ şeklinde kullanarak bir List<> tipindeki
            //değişkene atmak tabi ki mümkün.
            //Ancak biraz daha açık olması adına aşağıdaki gibi bir kod bloğunu
            //listemizi oluşturmak adına kullanabiliriz.
            foreach (var item in controllerExport)
            {
                if (item.Metadata is IPluginMetaData)
                    list.Add((IPluginMetaData)item.Metadata);

            }
            return list;

        }

        //Bu metod ile ismini verdiğimiz "Part"ı ya da kendi tabirimiz ile
        //"Plugin"ni çekip kullanıyoruz.Bir önceki metoddaki yaklaşımın
        //çok benzeri...
        public UserControl LoadPlugin(string name)
        {
            var controllerExport = container.GetExports(typeof(IPlugin),
                                                        typeof(IPluginMetaData),
                                                        "PluginForWPF");

            foreach (var item in controllerExport)
            {
                IPluginMetaData metaData = item.Metadata as IPluginMetaData;
                if (metaData.Name == name)
                {
                    return (UserControl)item.Value;
                }
            }

            return null;
        }

    }
}

Şimdi ana uygulamamızdan bu PluginFactory sınıfını kullanarak, ana ekranda “plugin”lerimizi yönetebileceğiz. Bunun için de ana uygulamızda aşağıdaki gibi bir kod kullanmamız gerekecek.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace WPFMEF
{

    public partial class MainWindow : Window
    {
        private PluginFactory _pluginFactory;
        public MainWindow()
        {

            InitializeComponent();
            LoadPlugins();
        }

        //"Plugin"lerimizi yada MEF'deki adıyla Part'ları
        //Ekrandaki ListBox kontrolüne ekliyoruz. Bu sayede
        //sistem tarafından hangi "Part"lar yüklenmiş bunları
        //görebiliyoruz
        private void LoadPlugins()
        {
            _pluginFactory = new PluginFactory(@"C:\WpFPlugins");
            Binding binder = new Binding();
            binder.Source = _pluginFactory.GetAllPlugins();
            listBox1.SetBinding(ListBox.ItemsSourceProperty, binder);
        }

        //Yüklenen "Plugin"leri çalıştırmak için bu event'i kullanıyoruz.
        //Bu "event" ListBox kontrolünde listelenen "Plugin" isimlerine tıklandığı
        //zaman çalışan bir metod.
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            canvas1.Children.Clear();
            Button button = sender as Button;
            UserControl control = _pluginFactory.LoadPlugin(button.CommandParameter.ToString());
            canvas1.Children.Add(control);
        }
    }
}

Sıra geldi “Plugin”lere…

Öncelikle “Common” projesinde aşağıdaki gibi bir sınıf tanımlamamız lazım. Bu sınıf ile ilgili ayrıntıları önceki MEF yazılarımdan tekrar tazeleyebilirsiniz.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel.Composition;

namespace Common
{
    //"Metadata"mızın içeriğni belirliyoruz.
    //Ne gibi bilgiler tutabileceğimizi istediğimiz gibi tanımlayabiliriz.
    public interface IPluginMetaData
    {
        string Name { get; }
        string Version { get; }
        string Author { get; }

    }

    public interface IPlugin
    { }

    //ExportAttribute tipinde, "Attribute" tanımlıyoruz ki
    //Yaratacağımız Part'da bu "Attribute"u kullanabilelim
    //Burda önemli olan constructor'da base'i çağırıp
    //ExportAttribute'da hangi arayüz ile tanımlama yapacağımızı belirtmek.
    [MetadataAttribute]
    [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
    public class PluginMetadataAttribute : ExportAttribute
    {
        public PluginMetadataAttribute(string name, string version, string author)
            : base(typeof(IPluginMetaData))
        {
            Name = name;
            Version = version;
            Author = author;

        }

        //IPluginMetaData arayüzünde ki özellikleri burada da tanımlıyoruz
        //Hepsini tanımlama zorunda değiliz.Tanımlamadıklarımız varsayılan
        //değerleri ile gelecektir.
        public string Name { get; set; }
        public string Version { get; set; }
        public string Author { get; set; }

    }
}

Plugin’ler için gerekli temel şeyleri yarattıktan sonra, ilk plugin’imizi kodlayabiliriz. Yukarda da belirttiğim gibi, bu ilk plugin bir WPF kullanıcı kontrolü olacak.”FirstWPFPlugin” projesinde ilgili kodları yazıyor olacağız. Çok basit ve anlamsız bir arayüz oluşturalım isterseniz…

<UserControl x:Class="Plugins.FirstWPFPlugin"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             mc:Ignorable="d"
             d:DesignHeight="300" d:DesignWidth="300">
    <Grid>
        <Button Content="Tıklayalım Bakalım" Click="button1_Click" Height="23" HorizontalAlignment="Left" Margin="77,107,0,0" Name="button1" VerticalAlignment="Top" Width="135" />
        <Label  Content="Bu ilk WPF Plugin'inimiz." Height="28" HorizontalAlignment="Left" Margin="77,84,0,0" Name="label1" VerticalAlignment="Top" FontStretch="Expanded" Width="149" />
    </Grid>
</UserControl>

Şimdi de aynı anlamsızlıkta kod tarafını yazalım.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Common;
using System.ComponentModel.Composition;

namespace Plugins
{

    //Export, özelliğini bu yarattığımız kontrole vererek
    //MEF'e bu kontrolün bir PART olarak Import edileceğini
    //söylüyoruz. Bu noktada IPlugin şeklinde önceden yarattığımız
    //interface'i de parametre olarak vermemiz gerekiyor.
    //MEF'de Export ettiğimiz tüm bileşenlerin belli bir kontart ismi
    //yada belli bir arayüz tipinden geliyor olması gerekmekte.
    //"PluginForWPF" bizim kontrat ismimiz, IPlugin'de arayüzümüz.
    [Export("PluginForWPF", typeof(IPlugin))]
    //PluginMetadata özelliği ise Export ettiğimiz sınıflara
    //ekstra olarak verebileceğimiz metadata bilgisini içeren,
    //yine kendi yarattığımız bir özellik sınıfı.
    [PluginMetadata("FirstWPFPlugin", "1.0", "Arda")]
    //Bu kısma ilerleyen yazılarda değiniyor olacağım
    [PartCreationPolicy(CreationPolicy.NonShared)]
    //WPF kullanıcı kontrolü olduğundan UserControl sınıfından
    //türüyor olması gerekmekte.IPlugin ise bizim kendi arayüzümüz.
    public partial class FirstWPFPlugin : UserControl,IPlugin
    {
        public FirstWPFPlugin()
        {
            InitializeComponent();
        }

        //Sanırım fazla açıklamaya gerek yok (:
        private void button1_Click(object sender, RoutedEventArgs e)
        {
            MessageBox.Show("Hello world");
        }
    }
}

Bu projeyi “Build” edip daha sonra oluşan *.dll’i, ana uygulamamızın “plugin”leri kontrol ettiği dizin altına koymamız gerekmekte. Bu örnekte bu dizin “C:\WpfPlugins” oluyor…(:

Ana uygulamamız bu dizine bakıp, bu dizindeki “plugin”leri yüklüyor olacak. Uygulamayı çalıştırdığımızda aşağıdaki gibi bir ekran görüntüsü elde ediyor olacağız.

Umarım MEF ile ilgili olarak en azından bir şeylerin aklınızda oluşmasında yardımcı olan bir yazı olmuştur. İlerleyen yazılarda bu örnekleri geliştirip çok daha faydalı örnekler yapıyor olacağız. Bundan dolayı her türlü sorunuzu yada fikrinizi beklerim…

Not: Bu yazıdaki örnek kodları ve projeyi buradan indirebilirsiniz.(Visual Studio 2010 projesi)

Not: Yukarıda bahsettiğim “SecondWPFPlugin”den bahsetmedim ama ekteki kodlarda oda mevcut.

  • Apr
  • 24
  • 2010

MEF’de “Part”lara kendi “metadata” bilgilerimizi nasıl ekleriz acaba?

Tags: , | View: 430 | Comments:

Önceki yazılarımdan bir tanesinde hatırlarsanız MEF’e giriş yapmıştık. Çok basit bir uygulama ile MEF’in nasıl işlediğini anlatmaya çalışmıştım.

Bu yazımda biraz daha derin konulara girip, “extension”larımıza(MEF’de ki adı “Part” oluyor) nasıl kendi “metadata” bilgilerimizi verebileceğimizi anlatmaya çalışacağız. Öncelikle neden böyle bir şeye ihtiyaç duyuyoruz, buna bir bakalım.

MEF ile geliştirdiğimiz bir uygulamayı doğası gereği esnek bir yapıda oluyor. Bu esnekliği MEF sağlıyor bize. Kendi yarattığımız arayüzlerden türeterek uygulamıza MEF Part’ları katabiliyoruz. Ama uygulamamızın sadece arayüzlerden türeyen “Part”lar ile esneklik kazanıyor olması bazen istenmeyen bir şey olabilir. Mesela IPlugin diye bir arayüzümüz var diyelim. Ana uygulamamız, bu arayüzden türeyen nesneleri kendi içinde çalıştırabiliyor olsun. IPlugin arayüzünü kullanarak kötü niyetli biri uygulamamızda kötü şeyler yapabilir. Bundan dolayı uygulamamızda “metadata” diye adlandırabileceğimiz ekstra içerik sağlayan özellikleri kullanmakta fayda var. Bu sayede IPlugin arayüzünden türeyen nesneleri kontrol edebiliriz. Ve uygulamamız için geçerli olmayan “metadata”ları uygulamamızın çalıştırmasını engelleyebiliriz.

Peki kendi geliştirdiğimiz “Part”lara kendi “metadata”larımızı nasıl ekleyeceğiz?

Çok basit…Öncelikle kendi “metadata”mızın ne gibi bilgiler içereceğini tanımlamamız lazım. Bunun için bir “interface” ya da “class” kullanabiliriz. Ama ben “interface”i seçeceğim bu örnekte. Aşağıdaki gibi bir kod işimizi görecektir.

    9     //"Metadata"mızın içeriğni belirliyoruz.

   10     //Ne gibi bilgiler tutabileceğimizi istediğimiz gibi tanımlayabiliriz.

   11     public interface IPluginMetaData

   12     {

   13         string Name { get; }

   14         string Version { get; }

   15         string Author { get; }

   16 

   17     }

Daha sonra bu arayüzü kullanan bir “Attribute” yaratmamız gerekmekte ki, geliştireceğimiz “Part”lara bu “metadata” özelliğini verebilelim. Bunun içinde aşağıdaki gibi bir kod işimizi görecektir.

   21     //ExportAttribute tipinde, "Attribute" tanımlıyoruz ki

   22     //Yaratacağımız Part’da bu "Attribute"u kullanabilelim

   23     //Burda önemli olan constructor’da base’i çağırıp

   24     //ExportAttribute’da hangi arayüz ile tanımlama yapacağımızı belirtmek.

   25     [MetadataAttribute]

   26     [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]

   27     public class PluginMetadataAttribute : ExportAttribute

   28     {

   29         public PluginMetadataAttribute(string name, string version,string author)

   30             : base(typeof(IPluginMetaData))

   31         {

   32             Name = name;

   33             Version = version;

   34             Author = author;

   35 

   36 

   37         }

   38 

   39         //IPluginMetaData arayüzünde ki özellikleri burada da tanımlıyoruz

   40         //Hepsini tanımlama zorunda değiliz.Tanımlamadıklarımız varsayılan

   41         //değerleri ile gelecektir.

   42         public string Name { get; set; }

   43         public string Version { get; set; }

   44         public string Author { get; set; }

   45 

   46     }

   47 

Artık yaratacağımız “Part”lar için kendi tanımlamamızı yapabileceğimiz “metadata” yapısına sahibiz. Şimdi tek yapmamız yaratacağımız “Part”da bu “metadata” özelliğini kullanmak.Bunun için de aşağıdaki gibi bir kod bloğu kullanmamız yeterli olacaktır.

   48     [Export(typeof(IPlugin))]

   49     [PluginMetadata("İlk Plugin", "1.0", "Arda")]

   50     [PartCreationPolicy(CreationPolicy.NonShared)]

   51     public class FirstPlugin : IPlugin

   52     {

   53 

   54     }

Dikkat ederseniz “PluginMetadata”, “Attribute”u bizim kendi yarattığımız “attribute” sınıfı.

İlerleyen yazılarda da bu “metadata” bilgilerini nasıl okuyup, MEF’de nasıl “Import” işlemi yapabiliyoruz bunu anlatıyor olacağım.Şimdilik bu kadar…

  • Mar
  • 06
  • 2010

“MEF” ile esneklik kazanıyoruz…

Tags: , , | View: 745 | Comments:

2008’de Microsoft “MEF” yani Managed Extensibility Framework isimli yeni bir framework üzerinde çalıştığını açıklamıştı.  Basitçe, biz geliştiricilere, “plug-in” yapısını destekleyen uygulamalar geliştirmemiz için yöntemler sunan bir framework diyebiliriz “MEF” için.

.NET Framework 4.0’a kadar, Microsoft, .NET’in içine koymuyor, “preview” adı altında sürekli geliştirmeler yapıyordu. .NET 4.0 ile beraber artık “MEF”i gönül rahatlığı ile kullanabiliyor olacağız.

Bu kısa girişten sonra, biraz daha derinlere inelim ve “MEF”in bize neler sağladığına ve neden kullanmamız gerektiğine bakalım. Öncelike “MEF” gibi bir framework’e neden ihtiyacımız var bunu anlamamız lazım. Tekrar kullanılabilirlik(Reusability) ve esneklik(extensibility), bir yazılımın yaşam sürecinde mutlaka bir şekilde karşımıza çıkan iki kavram. Geliştirdiğimiz yazılımlar çeşitli ihtiyaçlardan dolayı, ek özellikler ile genişletilmek istenebilir. Ya da geliştirdiğimiz yazılımı başka bir konfigürasyon ya da modüller ile başka bir şekilde kullanmamız da gerekebilir. Bu iki kavramı yazılımlara uygulamak oldukça zor ve sıkıntılıdır. Hele ki mimari tasarım sırasında bu iki kavramı göz ardı ettiysek sıkıntı çok daha büyük olur. Tabi ki bir yazılım illa ki esnek ya da tekrar kullanılabilir özelliğinin olması gerekmiyor. Ancak kişisel görüşüm, yazılımın yaşam süresinin uzun olabilmesi için bu iki kavramı karşılayabiliyor olması gerekmekte.

Esneklik ve tekrar kullanılabilirlik özelliklerine sahip bir yazılım ihtiyacı çok iyi bir tasarım gerektirir. Tasarımdan sonra ki geliştirme sürecini de bu iki kavram oldukça zorlar. Bu noktada “MEF”, .NET ile uygulama geliştirenlere bu süreci biraz daha kolaylaştırmak için elinden geleni yapıyor diyebilirim.

Bir PC’yi düşünelim…Anakartı ve bu kartın üzerine takılabilen ek kartlar ile çalışabilir bir sistem…X ekran kartı ile oynayamadığımız oyunları, X ekran kartını çıkartıp, Y ekran kartını takarak oynayabiliyoruz.Bunu ekran kartlarının, anakart ile belli bir arayüz standartı ile iletişim kurabilmesinden dolayı yapabiliyoruz.

“MEF” ile de bu tarz,“plug-in” yaklaşımı olan uygulamalar geliştirebiliyoruz. Run-time’da çıkarılıp, takılabilen “plug-in”ler ile uygulamalarımızı genişletebiliyoruz. Ve bütün bunlar System.ComponentModel.Composition.dll altında bulunan metod ve arayüzler sayesinde…

Kod üzerinden gidip ilk “MEF” uygulamamızı yaparsak sanırım bazı şeyler daha anlaşılır olur.Az önce yukarıda bahsetmiş olduğum PC örneği üzerinden gidiyor olacağım. Öncelikle aşağıdaki gibi 3 tane proje yaratıyoruz. Bunlardan biri ana uygulamamız(CustomPC,konsol uygulaması), biri arayüzümüz(DisplayAdapter) ve diğeri de ana kartımız(Xvidia). (:

Bu aşamada CustomPC ve XVidia projelerine System.ComponentModel.Composition.dll’i referans olarak eklememiz gerekmekte.

Öncelikle ana uygulamamız ve ekran kartımızın iletişim kuracağı arayüzümüzü oluşturalım. DisplayAdapter projesinde IDisplay.cs isimli aşağıdaki kodu içeren bir dosya yaratıyoruz;

46 using System;

47 using System.Collections.Generic;

48 using System.Linq;

49 using System.Text;

50

51 namespace DisplayAdapter

52 {

53 public interface IDisplayAdapter

54 {

55 void Display();

56 }

57 }

Bu arayüz sayesinde ana uygulamamız, “plug-in” olarak geliştirdiğimiz diğer bileşenleri çalıştırabiliyor olacak. Bundan sonra bu arayüzden yaratılan “plug-in”i geliştirebiliriz. Bir bakıma “ekran kartı”nı…(:

Bunun içinde XVidia projesinde aşağıdaki kodu yazmamız gerekmekte;

58 using System;

59 using System.Collections.Generic;

60 using System.Linq;

61 using System.Text;

62 using System.ComponentModel.Composition;

63

64 namespace DisplayAdapter

65 {

66 [Export("",typeof(IDisplayAdapter))]

67 public class XVidiaIDisplayAdapter

68 {

69

70 public void Display()

71 {

72 Console.WriteLine(“This is XVidia Display Adapter”);

73 }

74 }

75 }

Bu noktada “Export” özelliği dikkatimizi çekmiş olmalı. “MEF”‘de “Composable Parts” diye adlandırılan ve “MEF”in temel taşı olan bir kavram var. “Composable Parts” belli servisleri(metod) dışa sunan ve belli servisleride(metod) kullanan birimler. Ana uygulamalar ve “plug-in”ler bu birimlere göre çalışmakta. Yukarıdaki kod bloğunda “Export” özelliğini kullarak, XVidia sınıfını “IDisplayAdapter” arayüzünün metodlarını dışa sunduğunu göstermiş olduk.

Şimdi sıra “plug-in”leri çalıştıracak ana uygulamamıza geldi. Bunun için CustomPC projesinde aşağıdaki kodu içeren bir dosya yaratmamız yeterli.

82 using System;

83 using System.Collections.Generic;

84 using System.Linq;

85 using System.Text;

86 using System.ComponentModel.Composition.Hosting;

87 using System.Reflection;

88 using DisplayAdapter;

89 using System.ComponentModel.Composition;

90

91 namespace CustomPC

92 {

93 public class Computer

94 {

95 //Hangi arayüzün “MEF” tarafından import

96 //edileceğini belirtiyoruz.

97 [Import(typeof(IDisplayAdapter))]

98 public IDisplayAdapter DisplayAdapter { get; set; }

99

100 static void Main(string[] args)

101 {

102 Console.WriteLine(“PC’s configuration”);

103 Computer comp = new Computer();

104 comp.Init();

105

106 Console.ReadLine();

107 }

108

109 public void Init()

110 {

111 try

112 {

113 /*

114 * Plug-in’lerin olduğu yeri belirtmek için “MEF”

115 * ile gelen katalog kavramından faydalanıyoruz.

116 * İlerleyen yazılarda bu katalog kavramının

117 * derinlerine dalıyor olacağım.Şimdilik “plug-in”lerin

118 * bir dizinden okunacağını belirtmek için

119 * DirectoryCatalog sınıfı kullanıyoruz.

120 */

121 DirectoryCatalog dirCat = new DirectoryCatalog(Environment.CurrentDirectory + “\\plugin\\”);

122 /*

123 * “MEF” ile beraber gelen “CompositionBatch” ve

124 * “CompositionContainer” sınıfı ile “is a part of”

125 * ilişkisini kurabileceğimiz yapıyı yaratıyoruz.

126 * “is a part of” ilişkisi derken ne demek istediğimi

127 * açıklamamda fayda var sanırım.”plug-in”‘ler ana

128 * uygulamamızın bir parçası olacak.Ve bu parçalar

129 * takıp,çıkarılabilir özelliklere sahip olacak.

130 * Bütün bu ilişkiler “MEF” tarafından yönetiliyor olacak.

131 * “CompositionBatch” parçaların(Composable Parts)

132 * tutulduğu,”CompositionContainer” ise

133 * parçaların(Composable Parts)’ın metodlarının ve

134 * özelliklerinin sunulmasını sağlayan yapı olarak

135 * tanımlanabilir.

136 */

137 CompositionBatch batch = new CompositionBatch();

138 CompositionContainer container = new CompositionContainer(dirCat);

139

140 //Parçalarımızı ekliyoruz

141 batch.AddPart(this);

142

143 //Parçalarımızı ana uygulama ve birbirleri ile

144 //ilişkilendiriyoruz

145 container.Compose(batch);

146

147 //Parçalarımızı çalıştırıyoruz.Bu noktada

148 //arayüzün(IDisplayAdapter) sunduğu

149 //metodları sunabiliyoruz sadece.

150 DisplayAdapter.Display();

151 }

152 catch (Exception)

153 {

154

155 throw;

156 }

157

158

159 }

160

161

162 }

163

164

165 }

Ve işte bu kadar…Hemen özetliyelim ne yaptık. Bir tane “plug-in” desteği olan ana uygulama yaptık(CustomPC) ve bu ana uygulama üstünde çalışacak bir tane “plug-in” yaptık. Yaptığımız “plug-in” benzeri başka “plug-in”ler yaparak, uygulamamızı çeşitlendirebiliriz. Mesela XTI diye yeni bir ekran kartı, pardon “plug-in”….(:

Yukarıdaki CustomPC projesini çalıştırdığımız zaman hata alıyor olacağız. Peki ama neden? Bunun nedeni “XVidia” projesinin çıktısı olan *.Dll’in ana uygulama tarafına yüklenmemiş olması. CustomPC projesinin dizininde, “debug” klasörü altında “plugin” diye bir klasör açıp, “XVidia” projesinin çıktısı olan *.Dll’i kopyaladığımız zaman uygulamamızın sorunsuz bir şekilde çalıştığını göreceğiz.

“XVidia” projesinin çıktısı olan *.Dll yerine, benzer bir yapı ile oluşturulmuş bir *.Dll koyduğumuz zaman uygulamanın o *.Dll’i çalıştıracağını görüyor olacağız.

Çok basit ve güzel değil mi… .NET 4.0′ın bence en güzel yeniliği “MEF” ile entegre olması…İlerleyen yazılarda “MEF”in çok daha derinlerine dalıyor olacağım…Şimdilik bu kadar…