Arda Çetinkaya Yazılım ve arada kendim ile ilgili karaladıklarım…

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…

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.

,

Ö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.

, ,

Geçtiğimiz günlerde Team Foundation Server 2010’nun çıkması ile geliştirme süreçlerinde ki değişikliklere yeni yeni adapte olmaya çalışırken, bu adaptasyon sürecini hızlandıracak ve daha fazla katma değer katacak “Power Tools”‘un TFS 2010 versiyonu çıkmış bulunmakta. Buradan indirebilirsiniz. En büyük geliştirme “Process Template Editor”ünde yapılmış. “Process Template Editor”deki bu geliştirmeler, yeni “şablon”lar oluşturup, geliştirme süreçini yönetmek, mevcut alışkanlıklarınızı TFS 2010 üzerine taşınmak adına güzel bir yenilik olmuş. Öte yandan “Build Extensions”lar ise ANT gibi farklı “build” süreçlerini TFS 2010 üstünden yönetmek adına güzel bir yenilik.

Şiddetle bir göz atmanızı tavsiye ederim…

Cidden çok hoşuma gitti…Resmin adı: Dolmabahçe, yapan 4 yaşında bir çocuk…