Application Repository Ninject Modülü

29 May

Bu yazımızda, veri merkezli(data centric) uygulamalarda alışılagelmiş bir yöntem olan Repository kullanımının, birden fazla veri kaynağının bulunduğu durumlarda nasıl yapılması gerektiği konusunu incelemeye çalışacağız.

Repository mantığında öncelikle tüm alt sınıfları hiyerarşik bir düzene sokmak adına tanımlanan bir interface vardır. Örnek olarak basit bir interface tanımlamasını şu şekilde ifade ediyor olalım.


public interface IRepository<T>
{
    void Add(T entity);
    IList<T> GetAll();
}

Bu interface altında veritabanına ekleme ve veritabanından veri getirme gibi iki temel işlem vardır. Eğer bir SQL veri tabanına veri yazmayı amaçlıyorsak bu işlemi gerçekleştiren somut sınıfın oluşturulması gerekmektedir.


 public class SqlRepository<T> : IRepository<T>
 {
   public void Add(T entity)
   {
       // Veritabanına ekle..
   }

   public IList<T> GetAll()
   {
       // Veritananından oku..
   }
 }

Buraya kadar işler normal akışında seyrediyor. Ancak SQL veri tabanına yazılamadığı durumlarda ne olacağı bir soru işareti. Ya da başka bir sebepten dolayı ilişkisel veri tabanına yazmak yerine kayıtları XML bir dosyaya yazmak gerektiğinde XML dosya ile çalışabilen somut bir repository oluşturulması gerekmektedir. Bu durumda yeni bir sınıfa ihtiyaç duyulmaktadır.


 public class XmlRepository<T> : IRepository<T>
 {
    public void Add(T entity)
    {
       // XML dosyaya ekle...
    }

    public IList<T> GetAll()
    {
       // XML dosyadan oku...
    }
 }

Ancak bu iki farklı Repository tipinin, çalışma zamanında seçilebilir ve değiştirilebilir olması gerekmektedir. Aksi taktirde kodun değiştirilebilir olması istenmeyen bir durumdur. Bu değişime ayak uydurabilmek adına, değişikliği otomatik olarak gerçekleştirebilecek bir sınıfa ihtiyaç duyulmaktadır. Bu sınıf, çalışma zamanında dependency injection mekanizması ile yüklenebilen bir modül halinde tasarlanabilir.


 public class ApplicationRepositorySelectionModule : NinjectModule
 {
    public override void Load()
    {
        switch (ApplicationConfiguration.StorageType)
        {
           case PersistenceStorageType.SqlRepository:
              Bind(typeof(IRepository<>)).To(typeof(SqlRepository<>));
              break;
          case PersistenceStorageType.XmlRepository:
              Bind(typeof(IRepository<>)).To(typeof(SqlRepository<>));
              break;
         default:
              throw new ApplicationException("Not supported storage type!");

       }
    }
 }

NinjectModule sınıfı, Ninject dependenjy injector aracına ait bir sınıftır. Yaptığı işlem ise  Interface türlerine karşılık hangi somut sınıfın oluşturulacağını belirlemektir. Zamanı geldiğinde ilgili nesneyi oluşturup programa sağlamak yine Ninject tarafından gerçekleştirilmektedir.

Modül içerisinde kullanılan PersistenceStorageType ve ApplicationConfiguration sınıfları ise şu şekildedir.


 public class ApplicationConfiguration
 {
     public static PersistenceStorageType StorageType { get; set; }
 }

 public enum PersistenceStorageType
 {
     SqlRepository = 0,
     XmlRepository = 1
 }

Bu şekilde birden fazla repository sınıfı ile konfigurasyonu sağlababilir şekilde çalışmak mümkün olmaktadır. Yazılımda çevikliği yani yazılımın değişime ayak uydurabilme yeteneğini sağlayabilmek açısından uygulanabilecek olan küçük bir detaydan bahsetmiş olduk.

Bir sonraki yazıda görüşmek dileğiyle.

Asp.Net MVC Web API Projesinde Ninject ile Dependency Injection

5 Tem

Bir önceki yazılarımdan biri olan MVC Projesinde Dependency Injection başlıklı yazıda MVC uygulamasında Controller sınıfı içerisine dışarıdan bağımlılıkların enjekte edilmesini incelemeye çalışmıştık. Bu yazımızda ise Asp.Net MVC Web Api uygulaması üzerinde Dependency Injection işlemini uygulamaya çalışacağız.

MVC uygulamasında Controller sınıfları System.Web.Mvc.Controller tipinden türetilirken API Controller sınıfları System.Web.Http.ApiController tipinden türetilmiştir. Bu nedenle Web API üzerinde Dependency Injection uygularken bir önceki örnek uygulamadaki yöntemden daha farklı bir yöntem kullanacağız.

public class ValuesController : ApiController
{
     readonly IProductRepository productRepository;
     public ValuesController(IProductRepository productRepository)
     {
          this.productRepository = productRepository;
     }

     public IEnumerable<string> Get()
     {
         return productRepository.GetValues();
     }
}

API içerisine bağımlılıkları enjekte ederken IProductRepository interface tipini uygulayan(implemente eden) sınıfları göndermeye dikkat edeceğiz.

WEB API içerisinde Dependency Injection işlemini gerçekleştirmek için yine MVC’de olduğu gibi IoC container kütüphanelerinden faydalanmamız gerekiyor.

WEB API projelerinde Controller sınıflarının parametreli şekilde çalışabilmesi için bazı ayarlamaları yapmamız gerekecek. Bu ayarlamalardan biri, System.Web.Http alanında bulunan GlobalConfiguration statik sınıfındaki DependencyResolver özelliğinin düzenlenmesi olacaktır. Bu sayede WEB API, resolver mekanizmasını tanıyacaktır. Bu ayarlama Global.asax.cs dosyası içerisinde yapılmaktadır. Tanımlama:

public class WebApiApplication : HttpApplication
{
     protected void Application_Start()
     {
         // Diğer kodlar...
         GlobalConfiguration.Configuration.DependencyResolver = new NinjectDependencyResolver(CreateKernel());
     }

     private IKernel CreateKernel()
     {
         IKernel kernel = new StandardKernel();

         kernel.Bind<IProductRepository>().To<ProductRepository>();

         return kernel;
     }

}

NinjectDependencyResolver adında bir sınıf hazırlıyoruz ve yukarıdaki gibi DependencyResolver özelliğine atıyoruz. DependencyResolver özelliği IDependencyResolver tipindedir. Bu sebeple NinjectDependencyResolver sınıfı da IDependencyReolver tipini uygulamalıdır.

public class NinjectDependencyResolver : NinjectDependencyScope, IDependencyResolver
{
      private readonly IKernel kernel;

      public NinjectDependencyResolver(IKernel kernel): base(kernel)
      {
          this.kernel = kernel;
      }

      public IDependencyScope BeginScope()
      {
          return new NinjectDependencyScope(kernel.BeginBlock());
      }
}

NinjectDependencyResolver sınıfını aynı zamanda NinjectDependencyScope sınıfından türetiyoruz. Bunun sebebi ise IDependencyResolver interface tipinin barındırdığı BeginScope() metodudur. Bu metodun bir şekilde doldurulması gerekmektedir. NinjectDependencyScope tipi de kendi oluşturmuş olduğumuz bir sınıf olacaktır. Bu sınıfı de şu şekilde oluşturabiliriz.

public class NinjectDependencyScope : IDependencyScope
{
    IResolutionRoot resolver;

    public NinjectDependencyScope( IResolutionRoot resolver)
    {
        this.resolver = resolver;
    }

     public void Dispose()
     {
        IDisposable disposable = resolver as IDisposable;

        if (disposable!=null)
        {
            disposable.Dispose();
        }

        resolver = null;
     }

     public object GetService(Type serviceType)
     {
         if (resolver==null)
         {
             throw new ObjectDisposedException("this", "This scope has been disposed");
         }

         return resolver.TryGet(serviceType);
     }

     public IEnumerable<object> GetServices(Type serviceType)
     {
         if (resolver == null)
         {

              throw new ObjectDisposedException("this", "This scope has been disposed");
         }

         return resolver.GetAll(serviceType);
     }

}

Son aşamayı da bu şekilde tamamladıktan sonra projemiz çalışabilir hale gelmiş demektir.

WEB API Sonuç
WEB API Sonuç

Projenin örnek kodlarını SkyDrive üzerinden paylaşıyorum.

Kaynak Kod: Burada

ASP.NET MVC Projesinde Dependency Injection Uygulanması

30 Haz

Daha önce hazırladığım Dependency Injection Tasarım Deseni başlıklı yazıda, yazılım tasarımında sınıfların birbirine olan bağımlılıklarını esnetmeyi ve bağımlılıkların sınıf dışından enjekte edilmesini incelemiştik. Bu yazımızda ise konunun bir örnek uygulaması niteliğinde olan ASP.NET MVC uygulamalarında Dependency Injection uygulamasını inceleyeceğiz.

Bu yazımızdaki örnek uygulamamızı geliştirirken, soyut sınıflar ile somut sınıflar arasındaki bağlantıyı kurarken ve somut nesne oluştururken IoC Container denen yardımcı kütüphanelerden faydalanacağız. Yine bu örnek uygulamada IoC Container kütüphanesi ile  somut nesnelerin kullanıcı(client) sınıf içerisinde “new” anahtar sözcüğü ile oluşturmak yerine, IoC Container tarafından oluşturulduğunu göreceğiz.

Örnek Uygulama

Örnek uygulamamızda ASP.NET MVC controller sınıfına Dependency Injection tarasım deseni ile bağımlılıkları dışarıdan soyut nesneler(interface veya abstract) yardımıyla vererek uygulamayı çalıştırmayı deneyeceğiz.

Öncelikle Controller sınıfımızı tanımlayarak işe başlayabiliriz.

public class HomeController : Controller
{
    private readonly IProductService productService;

    public HomeController(IProductService productService)
    {
        this.productService = productService;
    }
    public JsonResult Index()
    {
        IEnumerable<Product> products = productService.GetBestSellers();

        return Json(products, JsonRequestBehavior.AllowGet);
    }

}

Gördüğünüz üzere, Controller sınıfında hiç bir şekilde nesne oluşturma işlemi gerçekleştirilmiyor. Dışarıda oluşturulan ProductService nesneleri Controller sınıfı içerisine enjekte ediliyor. IProductService tipi bir interface’dir. Bu interface tipini uygulayan sınıflar HomeController içerisinde çalışabilir. IProductController tipindeki nesneler constructor metodu yardımıyla sınıf içerisine alınıyor.

ASP.NET MVC Controller sınıflarının varsayılan(default) halleri çalışma zamanında parametresiz constructor metodlarını ararlar. Bu tanımlama ASP.NET DefaultControllerFactory içerisinde belirlenmiştir. ASP.NET MVC open source olduğundan isteyen MVC kodlarını açıp inceleyebilir. Biz constructor seviyesinde injection işlemi yapacağımızdan DefaultControllerFactory sınıfının GetControllerInstence metodunu ezerek yani yeniden tanımlayarak istediğimiz formata getirmeliyiz.

Bu tanımlamayı yapmadan önce uygulamada kullanacağımız NInject kütüphanesinden bir iki cümle ile bahsedelim. Ninject kitüphanesi bir konteynır olarak çalışır. Soyut nesneler ile somut nesnelerin eşleştirildiği ve istendiğinde somut nesnelerin(new) oluşturulduğu kütüphanelerdir.

public class NinjectControllerFactory : DefaultControllerFactory
{
     private readonly IKernel kernel;

     public NinjectControllerFactory()
     {
         kernel = new StandardKernel(new NinjectBindingModule());
     }

     protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
     {
         return controllerType == null ? null : (IController)kernel.Get(controllerType);
     }
}

public class NinjectBindingModule : NinjectModule
{
     public override void Load()
     {
         Kernel.Bind<IProductService>().To<ProductService>();
     }
}

Burada tanımlanan kod parçasında anlatılmak istenen şey DefaultControllerFactory metodunun Controller Instance oluştururken karşılaşacağı parametresiz constructor metodu oluşturma hatasını engellemektir. Ninject aracılığı ile Controller tipine göre bir nesne oluşturuyor. Örneğin HomeController için controllerType HomeController olacaktır ve constructor metodunda IProductService interface tipini parametre olarak alacaktır. HomeController tipinde bir nesne oluştururken constructor metodda karşılaştığı parametreyi Kernel bildiği için hemen nesnesini oluşturarak Controller sınıfına vermektedir.

Artık Global.asax sınıfı içerisine bu yapıyı tanıtma işlemi kaldı. Onu da şu şeklide gerçekleştirebiliriz.

public class MvcApplication : System.Web.HttpApplication
{
     protected void Application_Start()
     {
        //diğer tanımlamalar...

        ControllerBuilder.Current.SetControllerFactory(new NinjectControllerFactory());
     }
}

Uygulamayı çalıştırdığımızda çıktı olarak şu şekilde bir JSON veri dönecek:

[{"Name":"Notebook"},{"Name":"PC"}]

Örnek uygulamanın kodlarını SkyDrive üzerinden paylaştım. Bir sonraki yazıda görüşmek üzere.

Kaynak Kodlar: Burada

Dependency Injection Tasarım Deseni

4 Nis

Yazılım dünyasında ürünlerden daha çok prensiplere ve ilkelere önem verenlerdenim. Java veya C# dilinde yazmışım çokta dert değil benim için. Bir dilin yazım kurallarını ve ürün geliştirme araçlarını öğrenmek çokta uzun zaman almaz. Yazılım geliştirdiğimiz ortamların (IDE) pratiklerini anlamak çok önemlidir elbette. Ama yazılım prensipleri ve ilkelerini benimsemek, o ilkelere sadık kalarak yazılım geliştirmek bana göre daha önemlidir. Bu yazımızda da yazılım geliştiricilerin zamanla edindiği tecrübelereden biri olan Dependency Injection tasarım deseni üzerinde duracağız.

Resimde ne görüyorsunuz diye bir soru sorsam, süpheli süpheli bakmaya mı başlarsınız, yoksa USB anahtarlıklar ve iki anahtar mı dersiniz bilmiyorum. Ama sistematik bir görüntü var aslında resimde. Canı isteyen sistemdeki anahtarlıkları halkadan boşandırıp değiştirebilir. Çünkü halka esnek bir yapıda yani birbirine sabitlenmemiş. Aynı şekilde isteyen, ipliğin ucundaki USB aygıtlarını da istediği renkle değiştirebilir. Çünkü iplikte basit bir düğüm, dolayısıyla anahtarlık ve anahtarlar arasında gevşek bir bağ mevcut. Aynı şekilde USB aygıtlarının kapakları da birbirine uyumlu olduklarından  istenen renkte anahtarlık veya kapak seçilebilir ve ikili oluşturulabilir. Bu durum sistemin esnek olduğunu ve tüm bileşenler arasında gevşek bir bağlantı söz konusu olduğunu göstermekdir.

Yazılım tasarım desenlerinde de sıklıkla karşılaştığım “gevşek bağlılık” deyiminin de ifade etmek istediği şey yukarıdaki şekilde anlatıldığı gibidir. Nesnelerin birbirine uyumlu ve birbirini etkilemeyecek şeklide olmasıdır.

Yazılım tasarım desenlerinden biri olan, Dependency Injection (Bağımlılıkların Dışarıdan Enjekte Edilmesi) deseni de bileşenlerin(components) birbirine olan sıkı bağlarının gevşetilmesini ve yazılımın test edilebilir bir hale getirilmesini amaçlamaktadır. Test edilebilir tasarım konusuna daha sonraki yazılarda değinmek istiyorum. Şu anda ilgileneceğimiz konu tasarımdaki gevşek bağlılık(Loosely Coupled) üzerinden ilerleyecektir.

Bir örnek uygulama üzerinden devam etmelim. Senaryomuz bir şirketin çalışanlarının aylık ve günlük satış istatistiklerini veren bir sınıfın tasarlanması şeklinde olacaktır.

public interface IEmployeeStatistics
{
       decimal GetDailySales(int employeeId);
       decimal GetMonthlySales(int employeeId);
}

Yukarıdaki arayüz(interface), diğer bir deyişle sözleşme, employeeId bilgisi verilen çalışanın günlük ve aylık satış tutarlarını verecek şekilde hazırlanmıştır.

Aşağıdaki EmployeeStatistics sınıfı ise IEmployeeStatistics arayüzünden türetilmiş sınıfların getireceği istatistikleri dış dünyaya sunmaktan sorumlu olacaktır.

public class EmployeeStatistics
{
       private IEmployeeStatistics employeeStatistics;

       public EmployeeStatistics(IEmployeeStatistics employeeStatistics)
       {
              this.employeeStatistics = employeeStatistics;
       }

       public decimal GiveMeDailyReports(int employeeId)
       {
              return employeeStatistics.GetDailySales(employeeId);
       }

       public decimal GiveMeMonthlyReports(int employeeId)
       {
              return employeeStatistics.GetMonthlySales(employeeId);
       }

}

EmployeeStatistics sınıfının kurucu metodundaki bağımlılık, IEmployeeStatistics arayüzü tarafından sağlanmıştır.

Dış dünyaya sunum yapan sınıf olan EmployeeStatistics sınıfı, eline gelen istatistiklerin hangi veritabanından kullandığını, hangi aşamalardan geçtiğini, ne zorluklar çektiğini bilmez. Hatta sınıfın adını bile bilmez. EmployeeStatistics sınıfı için önemli olan sadece constructor metoduna gelecek olan tipin IEmploeyeeStatistics arayüzünü uygulamış (implement) olmasıdır. Aynen yukardaki resimde anahtarlığa bağlanan USB anahtarlıkların ne şekilde olduğunun önemsiz olduğu gibi.

Şimdi de MSSQL veritabanından istatistik çeken sınıfı oluşturalım.

public class EmoleeStatisticsFromMSSQL : IEmployeeStatistics
{
       public decimal GetDailySales(int employeeId)
       {
              //.....
              //.....
              // MSSQ'den getirme işlemleri

              decimal result = 5214M;

              return result;
       }

       public decimal GetMonthlySales(int employeeId)
       {
              //.....
              //.....
              // MSSQ'den getirme işlemleri

              decimal result = 23514M;

              return result;
        }
}

Eğer EmployeeStatistics sınıfı içerisinde IEmployeeStatistics tipini değilde şu anda oluşturduğumuz EmployeeStatisticsFromMSSQL tipini direk kullansaydık iki sınıf arasında (EmployeeStatistics ve EmployeeStatisticsFromMSSQL) sıkı bir bağ oluşacaktı. Bu durumda, sistemde çalışma zamanında(runtime) EmployeeStatisticsFromMSSQL sınıfı yerine EmployeeStatisticsFromOracle gibi bir değişimi imkansız hale getirecekti. Ancak bu sıkı bağı IEmployeeStatistics arayüzü ortadan kaldırdı ve esnek bir bağ oluşturdu. Artık çalışma zamanında istenen sınıf devreye alınabilir. İstediğimiz sınıfı nasıl devreye alacağımız ise EmployeeStatistics sınıfına baktığımızda anlaşılabilmektedir. Sınıf içerisine Constructor seviyesinde bir enjeksiyon yapılmaktadır. Bağımlılıklar dışarıdan sınıf içerisine enjekte edilmektedir.

Bir süre sonra şirketiniz veritabanı yazılımını MSSQL yerine Oracle tarafına geçirmek istediğinde yapmamız gereken sadece EmployeeStatisticsFromOracle sınıfını oluşturmak olacaktır.

public class EmoleeStatisticsFromOracle : IEmployeeStatistics
 {
       public decimal GetDailySales(int employeeId)
       {
              //.....
              //.....
              // Oracle'dan getirme işlemleri

              decimal result = 5214M;

              return result;
       }

       public decimal GetMonthlySales(int employeeId)
       {
               //.....
               //.....
               // Oracle'dan getirme işlemleri

               decimal result = 23514M;

               return result;
       }

}

Bu sayede yazılımımız değiştirilmeye kapalı , geliştirilmeye açık hale gelmiş olacaktır. Yani yazılım prensiplerinden biri olan Open Closed (Gelişime açık değişime kapalı) prensibine de uygun hale gelmiştir.

Sınıfın kullanımı ise şu şekilde olacaktır.

public class Run
{
       public void Raporla()
       {
             IEmployeeStatistics reportProvider = getRaporUretici();

             EmployeeStatistics statistics = new EmployeeStatistics(reportProvider);

             var daily = statistics.GiveMeDailyReports(1);
             var monthly = statistics.GiveMeMonthlyReports(1);
       }

       private IEmployeeStatistics getRaporUretici()
       {
             //Web.config dosyasından hangi veritabanından çalışılacak, öğren.
             //"OR" ise Oracle, "MS" ise MSSQL

             var db = getDBFromConfig();

             IEmployeeStatistics reportProvider;

             if(db == "MS")
                 reportProvider = new EmoleeStatisticsFromMSSQL();
             if(db == "OR")
                 reportProvider = new EmoleeStatisticsFromOracle();
             else 
                 reportProvider = new NullStatistics();

             return reportProvider;
        }
}

Artık kodumuz değiştirilmeden geliştirilebilir haldedir. Yani config dosyasından belirlenen bir flag ile çalışan sisteme müdahale edilebilmektedir. Bu imkanı kolaylaştıran yöntem ise bağımlılıkların dışarıdan alınmasını sağlayan, Dependency Injection tasarım desenidir.

Kullanıma hazır bir yapıyı oluşturabildik. Tekrar görüşmek ümidiyle.