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.

Hiç yorum yok: