
Bağımlılık İletimi(Dependency Injection); bir bağımlılık enjektörü tarafından önceden belirtilen bir modelin yaratılıp, bu yaratılan modelin hizmet ihtiyacı duyan nesneye enjekte edilmesidir. Genellikle iki şekilde yapılır. Bunlardan ilki; yapıcı metodun parametresinde tanımlanan arayüze uygun modelin yaratılıp enjekte edilmesi. Bir diğeri ise bir özelliğe(property) tanımlandığı tipteki nesnenin enjekte edilmesidir. Biz Callisto ile ilk yöntemi kullanarak bu enjekte işlemini gerçekleştirebiliyoruz. Callisto geliştirilmeye devam ettikçe diğer yöntemide bu framework içine dahil etmeye çalışacağız.
Şimdi isterseniz bir örnekle MVC içerisinde yapıcı metot enjektör özelliğini kullanalım.
Şırıngamdaki Veriler, Seni Beni Ebeler
Öncelikle Linq2Sql ya da Entity Data Model ile active record’umuzu oluşturalım. Ben bunun için AdventureWorks ve Northwind veri tabanlarını kullanıcam. Sonrasında Repository (türkçeye çevirmek gerekirse Veri Ambarı. Bu veri ambarını ‘data warehouse’ ile karıştırmayalım.) kısmını oluşturalım. Yani Sql için SqlCategoryRepository, Oracle için OracleCategoryRepository gibi.
Repository : Farklı veritabanlarını bölümlemek ve sorguları tek bir çatı altında toplamak için oluşturduğumuz nesneler.

Repository için
Callisto.Data isminde bir proje oluşturuyorum. Şimdi bu projeyi belli parçalara ayıralım.
O halde bu kısıma başlamadan evvel kullanacağımız modellerimizi belirleyelim. Örneğin bir Ürün tablomuz var. Tablomuzda şu kolonlardan oluşuyor;
- ID
- Ad
- Aciklama
- UrunuEkleyenKullaniciID
- UrununEklendigiTarih
Şimdi bir de Category tablomuz olsun. Bu tablomuzda da
- ID
- Name
- Description
- InsertUser
- InsertDate
kolonlarından oluşsun.
Bu iki tabloda da gördüğünüz ID, UrunEkleyenKullaniciID ve InsertUser, UrununEklendigiTarih ve InsertDate kolonları aynı göreve hizmet etmektedir. Model tablomuzu oluşturduğumuzda aynı özellikleri(property) tekrar tekrar tanımlamaktansa, bunun için bir ata arayüz tanımlarsak herşey daha kolay ilerler sanırım. Böylece yarın bir gün bu alanlardan birinde değişiklik yapıldığında da, sadece bu arayüzdeki ismi değiştiririz olur biter.
O halde IModelBase arayüzümüzü şu şekilde yazıyorum;
using System;
namespace Callisto.Data.Interfaces
{
public interface IModelBase
{
int ID { get; set; }
int IUser { get; set; }
DateTime IDate { get; set; }
int UUser { get; set; }
DateTime UDate { get; set; }
}
}
Buraya eklediğim diğer iki özellik ise güncelleme yapan kullanıcıyı belirtmek için UUser(UpdateUser), güncelleme yapılan tarih için ise UDate.
Şimdi ICategory arayüzümüzü oluştururken sadece Name ve Description alanlarını ekleyeceğiz o kadar.
namespace Callisto.Data.Interfaces
{
public interface ICategory : IModelBase
{
string Name { get; set; }
string Description { get; set; }
}
}
Hemen ardından Category sınıfımızı gerçekleştirelim.
using System;
using Callisto.Data.Interfaces;
namespace Callisto.Data.Models
{
public class Category : ICategory
{
public int ID { get; set; }
public int IUser { get; set; }
public DateTime IDate { get; set; }
public int UUser { get; set; }
public DateTime UDate { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
}
Modelimizi oluşturduktan sonra Repository’mizi oluşturabiliriz. ICategoryRepository arayüzünü oluşturalım
using System.Collections.Generic;
using Callisto.Data.Models;
namespace Callisto.Data.DataAccess.Interfaces
{
public interface ICategoryRepository
{
IList<Category> GetAll();
Category GetById(int ID);
void AddEntity(Category entity);
void DeleteEntity(Category entity);
}
}
Burada kullanacağımız metotları tanımlıyoruz ve tanımlama işlemi bittikten sonra bu arayüzden CategoryRepository’mizi gerçekleştiriyoruz. Yalnız CategoryRepository sınıfımızı ben hem LinqToSql için hemde Entity Framework için yaptım. Sırasıyla sınıflar şu şekilde
Linq2Sql için
using System;
using System.Collections.Generic;
using System.Linq;
using Callisto.Data.DataAccess.ActiveRecord;
using Callisto.Data.DataAccess.Interfaces;
using Callisto.Data.Helper.Extensions;
using Category=Callisto.Data.Models.Category;
namespace Callisto.Data.DataAccess.Repository
{
public class CategoryRepository : ICategoryRepository
{
private readonly NorthwindDataContext dataContext;
public CategoryRepository(NorthwindDataContext _CallistoDataContext)
{
dataContext = _CallistoDataContext;
}
public IList<Category> GetAll()
{
return (from category in dataContext.Categories
select new Category
{
ID = category.CategoryID,
Name = category.CategoryName,
Description = category.Description
}).ToList();
}
public Category GetById(int ID)
{
return (from category in dataContext.Categories
where category.CategoryID == ID
select new Category
{
ID = category.CategoryID,
Name = category.CategoryName,
Description = category.Description
}).FirstOrDefault();
}
public void AddEntity(Category entity)
{
//Bu methodu şimdilik boş bırakabiliriz
throw new System.NotImplementedException();
}
public void DeleteEntity(Category entity)
{
//Bu methodu şimdilik boş bırakabiliriz
throw new System.NotImplementedException();
}
}
}
Entity Framework için
using System;
using System.Collections.Generic;
using System.Data.Objects;
using System.Linq;
using Callisto.Data.DataAccess.ActiveRecord;
using Callisto.Data.DataAccess.Interfaces;
using Callisto.Data.Helper.Extensions;
using Category = Callisto.Data.Models.Category;
namespace Callisto.Data.DataAccess.Repository
{
public class CategoryRepository : ICategoryRepository
{
private readonly NorthwindEntities dataContext;
public CategoryRepository(NorthwindEntities _dataContext)
{
dataContext = _dataContext;
}
private ObjectQuery<Category> Categories
{
get
{
var categories = dataContext.Category;
return (from category in categories
select new Category()
{
ID = category.ID,
Name = category.Name,
Description = category.Description
})
;
}
}
public IList<Category> GetAll()
{
return Categories.ToList();
}
public Category GetById(int ID)
{
return Categories.Where(category => category.ID == ID).FirstOrDefault();
}
public void AddEntity(Category entity)
{
//Bu methodu şimdilik boş bırakabiliriz
throw new System.NotImplementedException();
}
public void DeleteEntity(Category entity)
{
//Bu methodu şimdilik boş bırakabiliriz
throw new System.NotImplementedException();
}
}
}

Peki
sunum katmanı(Presentation Layer) ile
veri erişim katmanı(Data Access Layer) arasındaki iletişimi nasıl sağlayacağız.Bunun için araya bir
veri yorumlama katmanı (Business Logic Layer) soksak sanırım hiç fena olmaz. O halde
Callisto.Service projemizi yaratıp içine ilk servis tanımlamamızı yapalım. Bunun için aşağıdaki gibi
ICategoryService arayüzünü ve bu arayüzden gerçekleştirilen
CategoryService nesnemizi oluşturuyoruz
ICategoryService Arayüzü
using System.Collections.Generic;
using Callisto.Data.Models;
namespace Callisto.Service.Interfaces
{
public interface ICategoryService
{
IList<category> GetAllCategories();
Category GetCategory(int CategoryId);
}
}
CategoryService Sınıfı
using System;
using System.Collections.Generic;
using Callisto.Data.DataAccess.Interfaces;
using Callisto.Data.Models;
using Callisto.Service.Interfaces;
namespace Callisto.Service.Models
{
public class CategoryService : ICategoryService
{
private readonly ICategoryRepository repository;
public CategoryService(ICategoryRepository _repository)
{
repository = _repository;
if (_repository == null)
throw new InvalidOperationException("Repository cannot be null");
}
#region ICategoryService Members
public IList<Category> GetAllCategories()
{
return repository.GetAll();
}
public Category GetCategory(int CategoryId)
{
return repository.GetById(CategoryId);
}
#endregion
}
}
Buraya kadar yaptığımız kısımda veriyi kullanılabilir hale getirdik. Şimdi kullanmaya başlayalım. Callisto.Web isimli Mvc uygulamasınıda solution içine ekleyelim. Buradan HomeController’a gecelim ve ilk veriyi göstermeye çalışalım.
HomeController.cs
using System.Web.Mvc;
using Callisto.Service.Interfaces;
namespace Callisto.Web.Controllers
{
[HandleError]
public class HomeController : Controller
{
private readonly ICategoryService categoryService;
public HomeController(ICategoryService _categoryService)
{
categoryService = _categoryService;
}
public ActionResult Index()
{
ViewData["Message"] = "Welcome to ASP.NET MVC!";
var categories = categoryService.GetAllCategories();
return View(categories);
}
public ActionResult About()
{
return View();
}
}
}
Index.aspx
<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %>
<%@ Import Namespace="Callisto.Data.Models"%>
<asp:Content ID="indexTitle" ContentPlaceHolderID="TitleContent" runat="server">
Home Page
</asp:Content>
<asp:Content ID="indexContent" ContentPlaceHolderID="MainContent" runat="server">
<h2>
<%= Html.Encode(ViewData["Message"]) %></h2>
<p>
To learn more about ASP.NET MVC visit <a href="http://asp.net/mvc" title="ASP.NET MVC Website">
http://asp.net/mvc</a>.
</p>
<p>
<ul>
<%
foreach (var item in (IEnumerable)ViewData.Model)
{%>
<li>
<%= Html.Encode((item as Category).Name) %>
</li>
<% } %>
</ul>
</p>
</asp:Content>
Evet artık veriyi göstermek için herşey hazır gibi. Fakat projeyi bu şekilde çalıştırdıktan sonra bir hata mesajı alacağız
Eğer dikkat ettiyseniz CategoryRepository, CategoryService ve HomeController sınıflarının birer yapıcı metodu var ve bu yapıcı metotlar birer parametre alıyor. Peki bu yapıcı metotların parametrelerini kim set edecek? Hangi repository hangi ORM ile çalışacak? Repository, Service ve Controller yapıcı metotları nasıl set edilecek? İşte tüm bu soruların cevabını Callisto bize veriyor, hatta cevaplamakla kalmayıp bu işleride o yapıyor.
Callisto ile Başlangıç ve Ayarlar
O zaman Callisto isimli kütüphanemizi (DLL dosyamızı) projelerimize ekliyoruz. Ekleme işlemlerini gerçekleştirdikten sonra Callisto.Data projemize geçip burada DbRegister isimli bir sınıf yaratıp, bu sınıfı Callisto’nun Register isimli sınıfından türetiyoruz. Türetme işleminden sonra kayıt işlemlerini yapmak için Repository isimli sanal metodumuzu eziyoruz.
using Callisto.Configuration;
using Callisto.Data.DataAccess.ActiveRecord;
namespace Callisto.Data.Helper.DI
{
[Mark(ContextType.DbRegistry)]
public class DBRegistry : Registry
{
public override void Repositories()
{
TakeActiveRecord<NorthwindDataContext>(StorageType.Singleton);
}
}
}
Ha en önemliside Active Record’umuzu kayıt ettiğimiz Register sınıfımızı Mark niteliği ile imzalıyoruz. Ardınan Repository metodunun içinde TakeActiveRecord generic metodunu oluşturup kullanmak istediğimiz depolama tipini seçiyoruz. Eğer multi thread bir uygulama var ise StorageType isimli sıralamadan(enumeration) Thread isimli sabiti(constant) seçebiliriz.
Callisto.Service projemize dönelim ve buraya da Repository ve Servis nesnelerimizin kayıt işlemlerini gerçekleştirmek için CallistoRegistry sınıfımızı oluşturalım. Bu sınıfımızıda aynı şekilde Registry sınıfından türetelim.
using Callisto.Configuration;
using Callisto.Data.DataAccess.Interfaces;
using Callisto.Data.DataAccess.Repository;
using Callisto.Service.Interfaces;
using Callisto.Service.Models;
namespace Callisto.Service.Component
{
public class CallistoRegistry : Registry
{
public override void Repositories()
{
//Önce repository kayıt ediliyor.
TakeAbstractModel<ICategoryRepository>().TakeConcreteModel<CategoryRepository>();
//Her iki kod satırında da öncelikle soyut nesnenin kayıt edildiğine dikkat diyoruz
TakeAbstractModel<ICategoryService>().TakeConcreteModel<CategoryService>();
}
}
}
Burada dikkat etmemiz gereken kısım ise; eğer bir repository’miz varsa önce bunu Callisto’ya kayıt edip sonrasında servisi kayıt ediyoruz. Yine buna benzer şekilde bir sıralama daha var. Bu sıralama ise TakeAbstractModel ve TakeConcreteModel metotları. Bu metotların kullanımı sırasında öncelikle soyut(abstract) nesneyi yani arayüzü kayıt edip, ardından somut(concrete) modeli kayıt ediyoruz.
Sonraki adımda PreLoadCallistoDI isminde bir statik sınıf yaratıyoruz. Bu sınıfımızın içine yine statik olan bir metot yaratıp, bu metot içinde register bileşenlerini sisteme kayıt ettiriyoruz. Bu kayıt işlemi için ise CallistoConfiguration sınıfı içinde yer alan AddComponent metodunu kullanıyoruz.
using Callisto.Data.Helper.DI;
namespace Callisto.Service.Component
{
public static class PreLoadCallistoDI
{
public static void Load()
{
CallistoConfiguration.AddComponent(new DBRegistry());
CallistoConfiguration.AddComponent(new CallistoRegistry());
CallistoConfiguration.AllComponentsRegister();
}
}
}
Peki PreLoadCallistoDI içinde Load metodu nasıl tetiklenecek? Onun içinde Global.asax isimli dosyaya çift tıklıyoruz. Burada yer alan Application_Start isimli metodu bulup aşağıdaki değişikliği yapıyoruz
protected void Application_Start()
{
//Uygulama başladığında kayıt işlemleri yapılır
PreLoadCallistoDI.Load();
RegisterRoutes(RouteTable.Routes);
}
Fakat buraya kadar yaptığımız değişiklikler Controller içindeki yapıcı metotları pek etkilemeyecek. Yani Callisto hala hangi controller’ın hangi parametresi set edilecek sorusunun yanıtı bilmiyor. Bunun için bize Controller sınıflarının üretildiği bir fabrika sınıfı lazım. Hemen sınıfımızı yaratalım o zaman
using System;
using System.Web.Mvc;
namespace Callisto.Service.Component
{
public class CallistoControllerFactory : DefaultControllerFactory
{
protected override IController GetControllerInstance(Type controllerType)
{
IController result = null;
try
{
/* Controller'ın tipi Fabrikaya gönderilir ve enjekte işlemi gerçekleştirilerek yeni
* Controller'lar oluşturulur */
if (controllerType != null)
result = CallistoFactory.GetController(controllerType) as Controller;
}
catch (CallistoException e)
{
System.Diagnostics.Debug.WriteLine(e.Message);
throw;
}
return result;
}
}
}
İşte enjekte işleminin gerçekleştirildiği son kısım burada yapılıyor. CallistoFactory içindeki GetController metodu bize aldığı controller tipindeki nesne örneğini geri döndürüyor. Hemde servis enjekte edilmiş olarak. Ama MVC’ye bir fabrika kullandığımız bildirmemiz gerekiyor. Bunun için yine global .asax dosyasını açıyoruz ve Application_Start metoduna şu satırıda ekliyoruz
//Kayıt edilen nesneler Controller'lara enjekte edilir.
ControllerBuilder.Current.SetControllerFactory(new CallistoControllerFactory());
Application_Start metodunun son hali
protected void Application_Start()
{
//Uygulama başladığında kayıt işlemleri yapılır
PreLoadCallistoDI.Load();
//Kayıt edilen nesneler Controller'lara enjekte edilir.
ControllerBuilder.Current.SetControllerFactory(new CallistoControllerFactory());
RegisterRoutes(RouteTable.Routes);
}
Tamamdır. Artık endişelenmenize gerek yok. Bir hata ile karşılaşmayacaksınız. Proje çalışır durumda. Tek yapmanız gereken şey klavyeden F5 tuşuna basarak projeyi build etmek ve başlatmak.
Final
MVC’ye geçiş yaptığımız şu günlerde projeler içinde ne kadar zorluk çekildiğinin farkındayım. Bu örnekteki Data, Service ve Web ayrıştırması, projemizi komplike olmaktan çıkarıp aksine derli toplu bir hale getiriyor. Dependency Injection’la ise bu düzeni korumakla kalmıyor, bununla birlikte zamandan da kazanç sağlamış oluyoruz.
Umarım yararlı ve faydalı bir yazı olmuştur. Bir sonraki yazımıda görüşmek üzere esen kalın...