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");
        }
    }

}


Export ve Metadata kavramlarını önceki yazılardan tanıyoruz zaten. Aslında çok da farklı bir şey yok, şu aşamaya kadar…Burda altını çizmek istediğim nokta, bu “Controller”ı IController tipinde bir arayüz ile “export” ediyor olmamız. Bu arayüz Asp.Net MVC Framework içerisinde bulunan bir yapı ve MVC Framework’ünde ki tüm “Controller”lar bu arayüzden türüyor. Dolayısıyla, Asp.Net MVC uygulamamızın bu geliştirdiğimiz “Controller”ı algılayabilmesi için bu arayüz tipiyle “export” etmemiz çok önemli.

Bu projenin için de, artık nasıl bir operasyon gerçekleştirmek istiyorsan onları yapmamız yeterli olacaktır.

Şimdi sıra Asp.Net MVC uygulamamızda…

Bunun için Asp.Net MVC uygulamamızda kendi geliştirdiğimiz bu yapıdaki “Controller”ları yükleyecek yapıyı oluşturmamız lazım. Bir önceki yazımda bahsettiğim Controller Factory yaklaşımını, gerçekleştiriyor olacağız ve kendi “Controller” fabrikamızı basitçe yazıyor olacağız.

Asp.Net MVC uygulamamızda da PluginControllerFactory sınıfını yaratmamız lazım. Bu kodu daha iyi anlayabilmek için MEF ile ilgili daha önceki yazılara bakmakta fayda var.

using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.ComponentModel.Composition.Primitives;
using System.Globalization;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using Common;

namespace MvcApplication2
{

    //IControllerFactory arayüzü MVC Framework'de Controller' yaratılmasında
    //gerekli olan bir arayüz. Geliştirilen tüm Controll fabrikalarının bu
    //arayüzden türemesi gerekiyor.
    public class PluginControllerFactory : IControllerFactory
    {
        private string _pluginPath;
        private DirectoryCatalog _catalog;
        private CompositionContainer _container;
        private DefaultControllerFactory _defaultControllerFactory;

        [ImportMany("MVCController", typeof(IController))]
        private IEnumerable<Lazy<IController, IPluginMetaData>> _plugins;

        public PluginControllerFactory(string pluginPath)
        {
            this._pluginPath = pluginPath;
            this._catalog = new DirectoryCatalog(_pluginPath);
            this._container = new CompositionContainer(_catalog);
            this._defaultControllerFactory = new DefaultControllerFactory();

        }
        //IControllerFactory arayüzünün iki tane metodu var.
        //Bunlardan biri CreateController metodu.
        //Bu metod gelen Routing talebini ve hangi
        //controller'ın yaratılması gerektiği bilgisi alıp,
        //"Controller"ın yaratılmasını sağlar. Dolayısıyla ne yapacaksak
        //bu metod içerisinde yapıyoruz. MVC uygulamamız için kendi
        //geliştirdiğimiz "Controller"ı bulup bu "Controller"ı MVC
        //uygulamıza gönderiyoruz.
        #region IControllerFactory Members
        public IController CreateController(System.Web.Routing.RequestContext requestContext, string controllerName)
        {
            _container.ComposeParts(this);
            if (_plugins != null || _plugins.Count() != 0)
            {
                foreach (var item in _plugins)
                {
                    if (item.Metadata is IPluginMetaData)
                    {
                        IPluginMetaData metadata = item.Metadata as IPluginMetaData;
                        if (metadata.Name == controllerName)
                        {
                            IController controller = item.Value as IController;
                            return controller;
                        }
                    }

                }
            }

            return this._defaultControllerFactory.CreateController(requestContext, controllerName);
        }

        //Bu metod da IControllerFactory arayüzünden
        //gelen bir metod.Bu metod yaratılan "Controller"ların
        //yok edilmesini sağlıyor.
        public void ReleaseController(IController controller)
        {
            IDisposable disposable = controller as IDisposable;
            disposable.Dispose();
        }
        #endregion
    }
}

Şimdi tek yapmamız gereken Asp.Net MVC uygulamızın “Controller” yaratırken kendi “Controller” fabrikamızı kullanmasını söylemek. Bunun için global.asax dosyasında aşağıdaki gibi bir değişiklik yapmamız yeterli olacaktır.

        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            RegisterRoutes(RouteTable.Routes);

            //Asp.Net MVC uygulamasının kendi yarattığımız "Controller"
            //fabrikasını kullanmasını ControllerBuilder sınıfının
            //Current özelliğinin SetControllerFactory metodu ile sağlıyoruz
            //"Plugins" parametresi ile de kendi yarattığımız "Controller"ların
            //hangi dizinde olacağını belirtiyoruz.
            ControllerBuilder.Current.SetControllerFactory(new PluginControllerFactory(
                Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Plugins")));
        }

Artık Asp.Net MVC uygulamamız, kendi yarattığımız “Controller” fabrikası sayesinde, farklı “Controller”ların çok rahat bir şekilde sisteme dahil olmasını sağlıyor olacaktır.

Oldukça basit bir şekilde Asp.Net MVC Framework’de MEF’i nasıl uygulayabiliriz bunu anlatmaya çalıştım. Umarım biraz olsa da faydalı olmuştur.

Not: Yazıda geçen kodları ve projeyi buradan indirebilirsiniz.