4 Kasım 2011 Cuma

MVC için SEO - 301 Redirect

Problem

Arama motorları web üzerinden ki aynı içeriğe birden fazla url ile ulaşılması durumunda tekrarlanan içerik olarak algılamaktadır. Arama motorları sitenizi tararken www'li e www'siz iki ayrı içeriği algılarlar. Yani http://www.siteniz.com/sayfa1.html ve http://siteniz.com/sayfa1.html  arama motorları için iki farklı tekrar edilen içeriktir. Bu durumda arama motorları tüm sitenizi tekrar eden içerik olarak algılar ve sitenizin bulunabilirliği azalır.

301 Redirect

Yapmanız gereken sitenizin www'li veya www'siz versiyonundan birini seçip gelen istekleri  diğer versiyona yönlendirmektir. Genellikle www'li versiyon kullanılmaktadır. Bende örnek olarak www'siz versiyondan gelen istekleri www'li versiyona yönlendireceğim. Yapmamız gereken gelen tüm istekleri kontrol edecek bir MvcHandler yazmak. MvcHandler'ımız gelen istekleri kontrol edecek ve istek adresinde www yoksa isteği www'li versiyona yönlendirecek.

Mvc 301 Redirect

   MvcHandler'lar Global.asax içinde yaptığımız Route tanımlarına eklenen nesnelerdir. MvcHandler bir Rooute işlemi çalışırken hangi Controller'ın seçilmesi gerektiğine karar verirler. Bu işlemi yapabilmek için tüm isteklerin başında çalışırlar. Mvc gelen isteğe göre önce hangi Route tanımının çalışması gerektiğine karar verir daha sonra Route içinde tanımlı Handler'larda hangi Controller sınıfının çalışması gerektiğine karar verir. Böyle her hangi bir Router için tüm istekler "Handle" edebileçeğiniz yer MvcHandler'dır.

Bizim problemimizde www'siz gelen tüm istekleri www'li versiyona çevirecek bir MvcHandler yazmamız gerekmektedir.

[cs]

public class RedirectHandler : MvcHandler
    {
        public RedirectHandler(RequestContext requestContext)
            : base(requestContext) { }

        protected override IAsyncResult BeginProcessRequest(
            HttpContext httpContext,
            AsyncCallback callback, object state)
        {

            if (!httpContext.Request.Url.AbsoluteUri.Contains("://www."))
            {

                httpContext.Response.Status = "301 Moved Permanently";
                httpContext.Response.StatusCode = 301;
                httpContext.Response.AppendHeader(
                    "Location",
                    httpContext.Request.Url.AbsoluteUri
                        .Replace("://", "://www.")
                    );
            }

            return base.BeginProcessRequest(
                httpContext, callback, state);
        }
    }

Mvc 301 Redirect

Yukarıda RedirectHandler içinde istek olarak gelen adresde www yoksa www'li versiyonuna yönlendirme yaptık. Fakat yönlendirmeden MVC sitemizde ki Route tanımlarının haberi bulunmamaktadır. Tüm Route tanımlarımıza RedirectHandler'ı kullanmaları gerektiğini söylememiz gerekmektedir. Yani tüm Route tanımlarımıza yazdığımız RedirectHandler ile çalışarak gerekli Controller'ı seçmesini söylemeliyiz.

[cs]

routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
            routes.IgnoreRoute("Content/{*pathInfo}");

            routes.MapRoute(
                "DefaultController",
                "{controller}/{action}/{id}",
                new {controller = "Home", action = "Index",id=UrlParameter.Optional});

            RouteHandler<RedirectHandler>.Assign(RouteTable.Routes);

Global.asax içinde sitemizde bulunan tüm Route tanımlarına RedirectHandler ekledik. Burada kullandığım RouteHandler sınıfı verilen MvcHandler tipini tüm Route tanımlarına eklemekte sorumludur. Yani RouteHandler sınıfı MvcHandler sınıflarını Route listelerine ekleyebilecek yardımcı bir sınıfdır.

[cs]

public class RouteHandler<THttpHandler> : MvcRouteHandler
    where THttpHandler : MvcHandler
    {
        public RouteHandler(IControllerFactory controllerFactory)
            : base(controllerFactory) { }
        public static void Assign(RouteCollection routes)
        {
            using (routes.GetReadLock())
            {

                var routeHandler
                    = new RouteHandler<THttpHandler>(
                        ControllerBuilder.Current.GetControllerFactory());

                foreach (var route in routes
                    .OfType<Route>()
                    .Where(r => (r.RouteHandler is MvcRouteHandler)))
                {

                    route.RouteHandler = routeHandler;
                }
            }
        }
    }

Sonuç

SEO için aynı içeriğin tek bir adresi olması gerekmektedir. Sitemizin www'siz adreslerini www'li adrese yönlendirmemiz gerekiyor. Bir MvcHandler yazarak bu işlemi gerçekleştirdik. Mvc sitemizde bulunan tüm Router tanımlarına bu handler'ı ekledik. Bu örnekte tüm isteklerde çalışacak bir MvcHandler yazmayı ve sitemizin Router tanımlarına eklemeyi gördük. Ayrıca SEO için önemli bir problemide çözmüş olduk. Ayrıca 301 Redirect mekanizması sadece adrese www eklemek için kullanılmaz. Eski adresin yeni adrese yönlendirilmesi işlemindede kullanılır.

2 Kasım 2011 Çarşamba

MVC için SEO - RSS

Kullanıcılar site içinde ilgilendikleri içerikler güncellendikce haberdar olmak isterler. Sitede yayınlanan beslemelere (RSS  - feed) abone olarak site içeriğini takip ederler. Arama motorlarıda benzer şekilde site değişikliklerini takip etmek için site haritaları ve site beslemelerini kullanırlar. Peki MVC ile geliştirdiğimiz sitemize web içerik besleme özelliğini nasıl entegre edebiliriz?

RSS Nedir ?

RSS (Really Simple Syndication) web içeriği abonelik formatıdır. RSS içeriği sabit bir xml yapısıdır. <rss> tagları arasında sitede yer tüm içeriği sunabilmektesiniz. Site haritası'na (sitemap.xml) göre çok daha gelişmiş ve detaylı bir veri yapısına sahiptir. Yorumlar, resimler, yayın tarihi web master bilgisine kadar geniş bir veri yapısı ile besleme oluşturabilirsiniz.

RSS ve .NET

Bu kadar gelişmiş bir veri yapısını .NET ortamında nasıl yöneteceksiniz. Üstelik veri yapısı benzerde olsa atom gibi farklı bir  abonelik formatı daha  vardır. Tüm bu farklı besleme formatlarını destekleyen bir besleme kaynağı oluşturma araçına ihtiyaçımız var. .NET yer alan SyndicationFeed sınıfı bu ihtiyacımızı karşılamaktadır.  SyndicationFeed nesnesi oluşturup site içeriğinizi feed.items altına doldurarak kendi besleme kaynağınızı oluşturabilirsiniz.

[cs]

public SyndicationFeed CreateFeed()
        {
            var pages = _pageRepository.Query();
            
            var feed = new SyndicationFeed( 
                    _settingRepository[SiteSettingKey.SiteTitle],
                    _settingRepository[SiteSettingKey.SiteSlogan],
                    new Uri(_settingRepository[SiteSettingKey.SiteUrl]));
            feed.Categories.Add(new SyndicationCategory("Blog"));
            feed.Language = "tr-TR";
            feed.Copyright = new TextSyndicationContent(
                string.Format("{0} {1}", DateTime.Now.Year, _settingRepository[SiteSettingKey.SiteTitle]));
            feed.LastUpdatedTime = DateTime.Now;
            feed.Authors.Add(
                    new SyndicationPerson
                        {
                            Name = _settingRepository[SiteSettingKey.SiteTitle], 
                            Email = _settingRepository[SiteSettingKey.Email]
                        });
            var feedItems = new List<SyndicationItem>();
            foreach (var item in pages)
            {
                var sItem = 
                    new SyndicationItem(
                        item.Title,
                        null,  /*content*/
                        new Uri(string.Format("{0}/{1}/{2}/{3}", 
                                _settingRepository[SiteSettingKey.SiteUrl], 
                                item.Published.Year, 
                                item.Published.Month, 
                                item.Slug)))
                        {
                            Summary = new TextSyndicationContent(item.Summary),
                            Id = item.Slug,
                            PublishDate = item.Created
                        };
                sItem.Links.Add(
                    new SyndicationLink
                        {
                            Title = item.Title,
                            Uri =
                                new Uri(string.Format("{0}/{1}/{2}/{3}", 
                                    _settingRepository[SiteSettingKey.SiteUrl], 
                                    item.Published.Year, 
                                    item.Published.Month, 
                                    item.Slug)),
                            Length = item.ContentHtml.Length,
                            MediaType = "html"
                        });
                feedItems.Add(sItem);
            }
            feed.Items = feedItems;
            return feed;
        }

Yukarıdaki örnekte bir besleme kaynağı (feed) oluşturulmuştur. Besleme kaynağı içerisine sitenin başlığı, url bilgisi, dil bilgisi, yazar bilgisi gibi bir çok bilgi eklenmiştir. Daha sonra sitede bulunan sayfalar feed içerisine eklenmiştir. Bu örnekte sayfa içeriğini olduğu gibi besleme kaynağına (feed) gömmek yerine sadece özet bilgisi göndermeyi tercih ettim. Beslemem içerisine hem içeriğin özetini hemde içeriğin linkini ekledim. Böylece sitemize ait içeriği webde paylaşabilecek duruma geldik.

RSS ve MVC

Rss içeriğimizi oluşturduk. Şimdi bu içeriği MVC sitemizde yayınlamamız gerekiyor. Oluşturduğumuz Rss içeriği kendi formatına sahip.  Web browser uygulamasına geri dönen değerin Rss formatına sahip olduğunu söylememiz gerekiyor. Böylece kullanıcı Rss içeriğini düzgün şekilde görüntüleyebilsin.

[cs]

public class SyndicationFeedResult : ContentResult
    {
        public SyndicationFeedResult(SyndicationFeed feed)
        {
            using (var memstream = new MemoryStream())
            using (var writer = new XmlTextWriter(memstream, System.Text.Encoding.UTF8))
            {
                feed.SaveAsRss20(writer);
                writer.Flush();
                memstream.Position = 0;
                Content = new StreamReader(memstream).ReadToEnd();
                ContentType = "application/rss+xml";
            }
        }
    }

Yukarıdaki kod bloğunda MVC ContentResult sınıfından türettiğimiz SyndicationFeedResult sınıfı  vardır. SyndicationFeedResult sınıfı içerik olarak .NET'e ait SyndicationFeed nesnesini Rss  formatında serialize etmektedir. Veri içeriğinin Rss formatında olduğunu söylemektedir.  Tüm Rss içeriğini CreadFeed() ile oluşturduk ve Rss içeriğini istemciye uygun formatta ulaştıracak SyndicationFeedResult sınıfnı yazdık. Bir action metot içinde site beslememizi paylaşabiliriz.

[cs]

public SyndicationFeedResult Feed()
        {
            var feed = CreateFeed(); 
            return new SyndicationFeedResult(feed);
        }

mvc feed görüntüsü

Sonuç

Rss ve Atom gibi web içerik besleme yapıları gelişmiş veri yapıları ile çok detaylı bir şekilde içerik paylaşmamıza olanak sağlamaktadır. Bu gelişmiş veri yapısı ve farklı veri formatları ile uğraşmadan .NET SyndicationFeed sınıfını kullanarak içerik paylaşımı yapabilirsiniz. MVC ile SyndicationFeed sınıfını entegre etmek için yeni bir ActionResult sınıfnı yazmalısınız. Özelleşmiş ActionResult sınıfınız içerik tipini rss olarak ayarlayıp SyndicationFeed içeriğini döndürmelidir.

1 Kasım 2011 Salı

MVC için SEO - Sitemap

MVC ile site geliştrdiniz ve dünyanın bundan haberdar olması istiyorsunuz. Yapmanız gerek sitenizi arama motorlarına kaydetmek. Arama motorlarının sitenizde dolaşabileceği ve sitenizin listelerde üst
sıralarda çıkarabilecek bir yol sunmalısınız. MVC ile geliştirilen bir sitede SEO nasıl yapılır? Burada her yerde bulunabilecek (saylarda özel başlıklar kullanın, meta etiketleri ile sayfa içeri betimleyin, okunabilir linkler verin gibi) SEO önerileri yerine kodlama yapılarak sitenize kazandırmanız gereken özellikleri anlatacağım.
İlk olarak insanların ve arama motorlarının sitenizde rahatça dolaşabilmesi için site haritaları hazırlamanız gerekmektedir. Sitenizde ziyaretcileriniz için mutlaka bir site haritası sayfası veya arşiv sayfası oluşturunuz.

SiteMap.xml

Site Haritaları, web yöneticilerinin arama motorlarını sitelerindeki taranabilir

sayfalar hakkında bilgilendirmeleri için kolay bir yoldur.En basit biçimiyle, bir

Site Haritası, arama motorlarının siteyi daha akıllıca tarayabilmeleri için, her

bir URL'yle ilgili ek meta verilerle (son güncellenme zamanı, genellikle ne sıklıkta

değiştiği ve sitedeki diğer URL'lere göre ne kadar önemli olduğu) birlikte, bir

siteye ilişkin URL'leri listeleyen bir XML dosyasıdır. (sitemaps.org adresinden alıntıdır)
SiteMap.xml dosyası bir çok arama motoru tarafından desteklenen bir endüstri standartıdır. Sitenizin SiteMap.xml protokolüne uygun site haritası sağlaması gerekmektedir.

MVC sitemap.xml

Eğer siteniz için dinamik bir sitemap.xml oluşturmak istiyorsanız ne yapmalısınız ? İlk olarak yapmamız gereken sitemize gelen sitemap.xml dosya isteğini yakalamamız ve bir action'a yönlendirmemiz gerekmektedir. Uygulamanın başlangıçında (Global.asax Application_Start()) içinde sitemap.xml yönlendirmeyi yapalım:
[cs]
public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("robots.txt");
            routes.MapRoute("sitemap", "sitemap.xml", new { controller = "Rss", action = "SiteMap" });
Sitemize gelen sitemap.xml dosyası isteği RssController.SiteMap() action'a yönlendirdik. RssController.SiteMap() action metotdu stansart bir html yerine sitemap.xml protokolüne uygun çıktı üretmelidir. O halde ilk olarak sitemap.xml protokolüne uygun özelleşmiş bir ActionResult oluşturamamız gerekmektedir. Böylece arama motorlarının anlayabileceği bir içerik oluşturmuş olacağız. Sonuç olarak bir xml döndüreceğimiz için XmlWriter sınıfnı çıktı üretmek için kullanabiliriz.
[cs]
public class SitemapActionResult : ActionResult
    {
        private readonly string _siteUrl;
        private readonly List<Page> _pages;

        public SitemapActionResult(string siteUrl, List<Page> pages)
        {
            _siteUrl = siteUrl;
            _pages = pages;
        }

        public override void ExecuteResult(ControllerContext context)
        {
            context.HttpContext.Response.ContentType = "text/xsl";
            using (XmlWriter writer = XmlWriter.Create(context.HttpContext.Response.Output))
            {
                writer.WriteStartElement("urlset", "http://www.sitemaps.org/schemas/sitemap/0.9");
                writer.WriteStartElement("url");
                writer.WriteElementString("loc", _siteUrl);
                writer.WriteElementString("lastmod", DateTime.Now.ToString("yyyy-MM-dd"));
                writer.WriteElementString("changefreq", "daily");
                writer.WriteElementString("priority", "1.0");
                writer.WriteEndElement();

                foreach (var page in _pages)
                {
                    writer.WriteStartElement("url");
                    writer.WriteElementString("loc",
                                              string.Format("{0}/{1}/{2}/{3}", _siteUrl, page.Published.Year,
                                                            page.Published.Month, page.Slug)); 
                    writer.WriteElementString("lastmod", page.LastChanged.ToString("yyyy-MM-dd")); 
                    writer.WriteElementString("changefreq", "daily");
                    writer.WriteElementString("priority", "0.5");
                    writer.WriteEndElement();
                } 
                writer.WriteEndElement(); 
                writer.Flush();
                writer.Close();
            }
        }
    }
Böylece protokole uygun çıktı üretip xml forrmatında HttpContext.Response üzerine yazmış olduk. Benim örneğimde sayfaların değişim sıklığı ve önceliklerinin aynı olduğunu kabul edilmiştir. Her bir sayfaya ulaşabilmek için okunabilir bir link verilmiştir. Kendiniz için yukarıdaki ActionResult sınıfnı özelleştirebilirsiniz. Son olarak satimap.xml isteklerini yönlendirdiğimiz RssController.SiteMap() action metodunun SitemapActionResult ile isteklere cevap vermesi gerekmektedir.
[cs]
public ActionResult SiteMap()
        {
            return new SitemapActionResult(
                _settingRepository[SiteSettingKey.SiteUrl],
                _pageRepository.Query()
                    .Where(c => c.IsPublish && c.Published < DateTime.Now)
                    .OrderByDescending(c => c.Published).ToList());
        }
Böylelikle yayında olan sayfaları listeleyip sitemap.xml protokolüne uygun olarak cevap dönmüş olduk.
sitemap.xml görüntüsü

Sonuç

Arama motorlarına sitede bulunan sayfaları listemek ve sayfalar hakkında bilgi vermek için sitemap.xml dosyaları kullanılmaktadır. Buradaki çalışma ile MVC sitenize ait sitemap.xml dosyanızı dinamik olarak üretip kullanabileceğiniz bir yol önerilmiştir. SEO için yapılması gerekenler listesinde önemli bir madde olan sitemap.xml dosyası oluşturmayı çözümlemiş bulunmaktayız.

31 Ekim 2011 Pazartesi

MVC için Elmah ile hata loglama

Problem

MVC uygulamanızda oluşan tüm hataları loglamak ve gerektiğinde listelemek istiyorsunuz. Bunun için tüm muhtemel yerlere try{}catch() blokları yerleştirmeli ve hatayı bir yerlere yazmalısınız. ActionFilter yazarak action'lar içindeki hatayı filter seviyesinde loglayabilirsiniz. Fakat action'lar dışındaki alanlarda oluşan beklenmedik hataları nasıl elde edeceksiniz.

ELMAH Nedir?

ELMAH ( Error Logging Modules And Handlers) çok fazla bilinmeyen açık kaynak kodlu bir ASP.NET projesidir. Çalışma anında beklenmeyen bir durum oluştuğunda loglama için tek satır kodu değiştirmeden tamemen web.config ayaları ile ELMAH sizin yerinize aşağıdaki işlemleri yapacaktır.

  • Bütün beklenmeyen hataları loglama
  • Uzaktan erişilebilir bir web sayfası ile tüm hataları listeleme
  • Uzaktan erişilebilir bir web sayfası ile hata detaylarını gösterme
  • Asp.Net'in sarı hata sayfası yerine özelleştirilmiş hata sayfalarına yönlendirebilme
  • Hata oluştuğunda mail ile bilgilendirme
  • Hata listeleme için RSS bağlantısı
  • Hataları sql server gibi farklı veri kaynaklarında saklayabilme

Nasıl Kullanılır ?

Öncelikle ELMAH'ı kendi projenize kurmanız gerekmektedir. Nuget ile aşağıdaki komutu çalıştırarak kendi projenize ekleyebilirsiniz.

PM> Install-Package elmah

Kurulumdan sonra Nuget gerekli tüm web.config ayarlarını yapacaktır. MVC uygulamanızda elmah.axd sayfasına giderek hata listeleme sayfasına ulaşabilirsiniz.

elmah.axd sayfasının görünümü

ELMAH bu işleri nasıl yapmaktadır. web.config dosyası üzerinde takip edelim.

[xml]

<httpHandlers>
      <add verb="POST,GET,HEAD" path="elmah.axd" type="Elmah.ErrorLogPageFactory, Elmah" />
    </httpHandlers>
    <httpModules>
      <add name="ErrorLog" type="Elmah.ErrorLogModule, Elmah" />
      <add name="ErrorMail" type="Elmah.ErrorMailModule, Elmah" />
      <add name="ErrorFilter" type="Elmah.ErrorFilterModule, Elmah" />
    </httpModules>

Elmah.ErrorLogModule ile Asp.Net işlemi içinde oluşan her beklenmeyen hata kayıt etmektedir. Loglama, mail gönderme ve filtreleme özelliklerini gene web.config üzerinden değiştirebilirsiniz. Oluşan beklenmedik hatalar varsayılan olarak xml dosyalarda tutulmaktadır. Ben örnek olması açısından hataların bir SQL CE veri tabanında tutulmasını göstericeğim. Yapmamız gereken web.config üzerinde ELMAH'a veri saklama yerini değiştirmesini söylemek.

[xml]

<connectionStrings>
    <add name="elmah-sqlservercompact" connectionString="Data Source=|DataDirectory|\Elmah.sdf" />
  </connectionStrings>
  <elmah> 
    <errorLog type="Elmah.SqlServerCompactErrorLog, Elmah" connectionStringName="elmah-sqlservercompact" />
  </elmah>

MVC Entegrasyonu

Hiç bir ekstra geliştirme yapmadan ELMAH çalışacaktır. Fakat MVC ile gelen beklenmeyen hataları yönetem bir HandleErrorAttribute ActionFilter nesmemiz var. Bu attribute MVC uygulamamızda GlobalFilter olarak kayıt edilmektedir. Bunun anlamı MVC ile gelen HandleErrorAttribute tüm Action metotlarımızın için bir merkezi hata yönetim noktasıdır.(Daha fazlası için Aspect Programing, Policy Injection) Action içinde oluşan hatalar HandleErrorAttribute içinde yakalanacağı için ELMAH tarafından listelenemeyecektir. Yapmamız gereken HandleErrorAttribute ile ELMAH'ı konuşturmaktır. Kendi HandleErrorAttribute sınıfmızı yazıp oluşan hatayı ELMAH'a bildirmemiz gerekmektedir.

[cs]

[Export]
    public class HandleErrorAttribute : System.Web.Mvc.HandleErrorAttribute
    {
        public override void OnException(ExceptionContext context)
        {
            base.OnException(context);
            if (context.ExceptionHandled)
                ErrorSignal.FromCurrentContext().Raise(context.Exception);
        }
    }

Son olaral Global.asax içinde kendi HandleErrorAttibute sınıfımızı kayıt etmeliyiz.


[cs]

public static void RegisterGlobalFilters(GlobalFilterCollection filters)
        {
            filters.Add(new Filters.HandleErrorAttribute()); 
        }

Sonuç

ELMAH beklenmeyen hataları loglamak ve yönetmek için açık kaynak kodlu güçlü bir araçtır. Muhtemelen sonraki Asp.Net versiyonlarında framework içinde geleçek bir uygulamadır. MVC ile entegrasyonu oldukça kolaydır. Buradaki örnekte olduğu gibi hata loglarını ayrı bir SQL CE veri tabanında tutmanızı öneriyorum. Zira ELMAH hataların stack bilgisinide sakladığı için hata kayıtları çok fazla yer işgal etmektedir. Ayrı bir SQL CE veri tabanı kullanarak gerektiğinde hata kayıtlarını doğrudann silebilirsiniz.

30 Ekim 2011 Pazar

MVC için Dependency Injection

Problem

MVC mimarisi gereği çok parçalı bir yapıdır. View, controller, filter, model gibi bir çok parçanın bulunduğu ortamda nesneler arasında ki ilişkiyi takip etmek ve performansı korumak bir sorundur. Yani bir DbContext nesnenize her hangi bir view, controller veya filter üzerinden erişebilmek isteriz.

MVC'nin bu karmaşasından arınmanın en iyi yöntemi MVC uygulamıza bir DI mekanizması eklemektir. Dependency Injection(DI) bir ilke veya bir mimari desendir. DI epeyce farklı tanımları vardır. Genel olarak DI uygulama nesneleri arasında nesne bağımlılılarını ve yaşan döngülerini yönetmektedir.

MVC'ye DI nasıl entegre edilir?

MVC çalışma anında ihtiyaç duyduğu tüm nesneleri elde etmek için formal bir yöntem ile geliştirmiştir. MVC IDepenedcyResolver interface uygulaması olan DependencyResolver nesnesi ile birlikte gelmektedir. MVC uygulamanızda oluşturulacak tüm nesneler IDependencyResolver interface ile oluşturulmaktadır. Yani aslında MVC doğrudan DI esteği ile gelmektedir. MVC Uygulamamıza DI entegre etmek için yapmamız gereken IDepenedcyResolver interface uygulayan bir sınıf oluşturmak ve bunu MVC uygulamıza söylemektir.

IDependecyResolver

IDependecyResolver interface'i iki fonksiyona sahiptir.

[cs]

public interface IDependencyResolver
    {
        object GetService(Type serviceType);
        IEnumerable<object> GetServices(Type serviceType);
    }
  • GetService(servicType): Bu fonksiyon bir servis tipini alır ve çözümlemeye çalışır. Eğer istenen servis tipine bağlı servis tanımları varsa uygun servisi üretir ve döndürür. Eğer uygun bir bağımlılık tanımı bulamazsa null döner bu durumda MVC varsayılan davranışına devam eder.
  • GetServices(servicType): Bu mehod servis tipine uygun tüm servis çözümlerini bir koleksiyon (IEnumerable<>) içinde döner. Benzer şekilde eğer servis tipi çözümlenemezse null döner ve MVC'nin varsayılan şekilde çalışmaya devam etmesini sağlar.

DependencyResolver, özel IDependencyResolver nesnemizi kaydetmek için kullandığınız statik bir sınıftır. DependencyResolver.SetResolver ( IDependecyResolver) yöntemini kullanarak uygulamanız için özelleştirdiğiniz IDependencyResolver nesnenizi kurabilirsiniz. Artık MVC uygulamızda üretilen tüm MVC nesneleri özelleştirdiğiniz IDependencyResolver nesnesi üzerinden sağlanacaktır.

CompositionDepencyResolver Oluşturmak

Şimdi basit bir IDependecyResolver örneği yapalım. Tüm MVC nesnelerinin üretileceği Composition kullanan özelleşmiş CompositionDependecyResolver sınıfmız. :

[cs]

[Export(typeof (IDependencyResolver))]
    public class CompositionDependencyResolver
        : IDependencyResolver
    {
        #region IDependencyResolver Members

        public object GetService(Type serviceType)
        {
            try
            {
                IEnumerable<Lazy<object, object>> exports = _container.GetExports(serviceType, null, null);
                if (exports.Any())
                    return exports.First().Value;

                object result = _defaultDependencyResolver.GetService(serviceType);
                if (result != null) _container.ComposeParts(result);
                return result;
            }
            catch (Exception)
            {
                return null;
            }
        }

        public IEnumerable<object> GetServices(Type serviceType)
        {
            try
            {
                IEnumerable<Lazy<object, object>> exports = _container.GetExports(serviceType, null, null);
                if (exports.Any())
                    return exports.Select(e => e.Value).AsEnumerable();
                IEnumerable<object> result = _defaultDependencyResolver.GetServices(serviceType);
                _container.ComposeParts(result);
                return result;
            }
            catch (Exception)
            {
                return null;
            }
        }

        #endregion
    }


Burada basitçe servis tipi önce katolog üzerinde aranmaktadır. Eğer katalog üzerinde servis tipine ait bir çözüm varsa bu döndürülmektedir. Eğer katalog üzerinde servis tipine ait bir çözüm yoksa MVC'nin varsayılan IDependencyResolver uygulamasına çözüm sorulmaktadır. Varsayılan IDependencyResolver  çözümü bulursa dönen nesne üzerinde tekrar DI işlemi yapılmaktadır.

Şimdi Global.asax içinde özelleştirdiğimiz IDependencyResolver sınıfımızı kayıt edelim:

[cs]

protected void Application_Start()
        {
            var catalog = new AggregateCatalog(new AssemblyCatalog(typeof(PageRepository).Assembly));
            var container = new CompositionDependencyResolver(catalog, DependencyResolver.Current);

            DependencyResolver.SetResolver(container);

Böylelikle tüm MVC nesnelerinizin bağımlılıklarını tek bir sınıf ile çözebilirsiniz.Artık istediğiniz MVC nesnesi ile DI kullanabilirsiniz.

[cs]

[Export, PartCreationPolicy(CreationPolicy.NonShared)]
    public class BlogController : Controller
    {
        private readonly IPageRepository _pageRepository;
        private readonly ILayoutRepository _layoutRepository;
        private readonly ITagRepository _tagRepository;
        private readonly ICommentRepository _commentRepository;
        private readonly ISettingRepository _settingRepository;

        [ImportingConstructor]
        public BlogController(IPageRepository pageRepository, ILayoutRepository layoutRepository, 
            ITagRepository tagRepository, ICommentRepository commentRepository, ISettingRepository settingRepository)
        {
            _pageRepository = pageRepository;
            _layoutRepository = layoutRepository;
            _tagRepository = tagRepository;
            _commentRepository = commentRepository;
            _settingRepository = settingRepository;
        }

Sonuç

MVC  uygulamaları daha fazla DI desteğine ihtiyaç duyduğu için framework IDependecyResolver interface sunmaktadır. Burada özelliştirilmiş bir IDependecyResolver nasıl yazılaçağını inceledik. DI ihtiyaçımızı gene .NET içinde sunulan Composition isim uzayı ile karşıladık.