Autofac Nedir? IoC Container Olarak Nasıl Kullanılır?

Autofac Nedir? IoC Container Olarak Nasıl Kullanılır?

Autofac Nedir? IoC Container Olarak Nasıl Kullanılır?

Merhabalar,

Bu yazımızda Autofac üzerine konuşacağız. Autofac .Net için geliştirilmiş bir IoC container’dır. Autofac sınıflar arası bağımlılıkları yönetmimizi sağlar, uygulamamız genişleyip karmaşıklığı arttıkça Autofac ile yönetimi daha kolay bir hal alır. Peki IoC(Inversion of Control) container nedir?

Yazılım geliştirirken Coupling ve Cohesion kelimelerini duymuşuzdur. Coupling birden fazla sınıf yada nesne arasındaki ilişkiyi temsil ederken Cohesion ise bu sınıf ve nesnelerin içindekilerin birbirine olan bağlılığı, benzerliğidir. Biz yazılım geliştirirken High Cohesion isteriz yani sınıf ve nesnelerin içindeki bağıntıların, benzerliğin, birbirleriyle olan alakanın yüksek olmasını isteriz. Aslında bu SOLID prensiplerinden Single Responsibility Principle’ a denk gelmektedir. Yani bir sınıf bir işi yapmalıdır. O sınıfta bulunan elemanlar aynı işin sorumluluğunu taşımalıdırlar. Bununla beraber Loose Coupling yani gevşek bağlılık isteriz. Bunu istememizdeki amaç projenin ileriki safhalarında bir sınıf yada nesnede yapılacak değişikliklerin diğer sınıf yada nesneleri minimum düzeyde etkilemesidir böylece hem gelişim hem de bakım çok daha kolay olacaktır.

İşte IoC, projelerimizdeki bu bağımlılığı azaltmamızı sağlayan bir yazılım tasarım prensibidir. Nesnelerin üretimi ve yaşam döngülerinden sorumludur. Bu gibi işlemleri yerine getirmemizi sağlayan belli başlı IoC container dediğimiz yapılar mevcuttur. Autofac ise .Net için geliştirilmiş bir IoC container’dır, dependecy injection aracıdır da diyebiliriz. Dependency Injection’ı kısa olarak tarif etmek gerekirse SOLID prensiplerinin son harfi olan Dependency Inversion prensibi der ki üst seviye sınıflar alt seviye sınıflara doğrudan bağımlı olmamalıdır. Doğrudan alt seviye sınıfların somut hallerini kullanmak yerine interface gibi bir yapı ile soyut hali kullanılmalıdır. Böylece bağımlılık azalacaktır. İşte dependency injection bu prensibi uygulayıp bağımlılıkları ortadan kaldırırken kullandığımız bir tasarım desenidir. Autofac haricinde Castle Windsor, Unity, Ninject gibi araçlarda bulunmaktadır. Şimdi bir örnek üzerinden Autofac’i IoC container olarak nasıl kullanacağımızı inceleyelim.

Projede katmanlı mimari kullanacağız. Katmanlarımız Entities, DataAccess, Business ve WebAPI. Bu yazıda Autofac’in IoC container olarak nasıl kullanılacağını anlattıktan sonra diğer yazıda Autofac ile Aspect Oriented Programming nasıl yapılır konusuna da değinmeyi düşünüyorum bu yüzden proje ne kadar demo bir proje olsa da oluşturduğum sınıflar hakkında bilgi vermemin ve biraz açıklamamın doğru olacağını düşündüm. Visual studio ile yeni bir blank solution oluşturuyoruz.

Proje Oluşturma

Daha sonra açılan blank solution üzerine sağ tıklayıp add>new project diyoruz ve ismine WebAPI verdiğimiz bir API projesi oluşturuyoruz.

WebAPI Oluşturma

Burada Enable OpenAPI support seçeneğini seçtik böylece oluşturduğumuz projeyi swagger üzerinden test edebileceğiz. Bu işlemden sonra ise yine add>new project seçeneğini seçip 3 adet class library ekliyoruz. Bunlar Entities, DataAccess ve Business. İşlemlerimiz tamamlandıktan sonra solution explorer;

Solution Explorer

Sıra geldi ilgili sınıflarımızı oluşturmaya. Veritabanı tablolarına karşılık gelecek sınıflarımızı Entities katmanında oluşturucağız. İlk olarak tüm entity sınıflarımızın kullanacağı IEntity interface’ini oluşturuyoruz. Sadece veritabanı nesnelerimizle işlem yapmak istediğimiz zaman bu interface ile bu kontrolü yapabiliriz. Yani IEntity den türettiğimiz tüm sınıfların referansını IEntity ile tutabiliriz. Ayrıca burada türeyen sınıflarda bulunmasını istediğimiz nesneleri de oluşturabiliriz.

IEntity Interface’i

Daha sonra Product ve Category sınıflarımızı oluşturuyoruz. Yazının çok uzamaması adına örneklerde sadece product ile ilgili olan kısımları kullanacağım.

Product Sınıfı

Burada amacım Autofac’i anlatmak olduğu için şuanlık sınıflar arasındaki ilişkilere yani her ürünün kategorisi olduğu için burada bu sınıfı kategori ile ilişkilendirmeye aynı şekilde kategori sınıfınında ürünleri olduğu için bu sınıfıda ürün sınıfı ile ilişkilendirmeye girmeyeceğim. Bu ilişkileri attribute vasıtasıyla yada Fluent Api ile belirtebilmekteyiz. Bu işlemler sonrası sıra geldi veriye erişim katmanındaki interface ve sınıfları oluşturmaya. Ben burada repository pattern kullanacağım o yüzden ilk iş temel crud işlemlerini yapan interface’i oluşturup içini dolduruyorum.

IGenericRepository Interface’i

Burada temel işlemleri yapacak metot imzalarını bir interface de topluyoruz. Daha sonra bu interface’i generic yapıda tanımlıyoruz yani hangi entity sınıfı ile işlem yapacaksak o sınıfı gönderiyoruz böylece hem product hem de category için ayrı ayrı kod yazma zahmetinden kurtulmuş oluyoruz. Biz hangi sınıfı gönderirsek onun için crud işlemlerini gerçekleştirmiş olacağız. Ayrıca bu sınıfa generic olarak alacağı nesnenin de bir class olduğunu ve IEntity den türemesi gerektiğini söylüyoruz. IEntity den türemesini söylememizdeki amaç entity sınıflarımız haricinde başka sınıfı buraya vermeyi engellemektir. Son olarak new() ifadesini de ekleyerek IEntity gibi interface verilmesinin önüne geçiyoruz çünkü new() ifadesi nesnesi oluşturulabilecek bir generic tip vermemi sağlıyor. Interface’ler de new kullanamadığımız için buraya IEntity veremeyoruz. Daha sonra bu interface’i kullanacak sınıfı yazıyorum.

Generic Repository

Burada IGenericRepository de tanımladığım contractları implement ettik. Daha sonra aynı şekilde product ve category sınıfları içinde interface ve class tanımlamalarını yapıyoruz.

IProductDal
ProductDal

Yukarıda görüldüğü üzere product nesnesi için interface ve class tanımlamalarını yaptık. Burada repository sınıfları ile iskeleti olışturduğumuz için bu sınıflarda sadece oluşturduklarımızı kullanarak işlem yaptık ayrıca veritabanı bağlantısı için context nesnesini gönderdik. Burada veritabanı bağlantısı için pek çok yöntem bulunmaktadır.

ApplicationDbContext

Son olarak veritabanı işlemlerini gerçekleştirebilmek için context sınıfımızı oluşturduk. Şimdi gelelim bu oluşturduklarımızı kullanmaya.

Şimdi de business katmanına geçip servislerimizi oluşturuyoruz.

IProductService
ProductService

Business katmanındaki sınıflarımız ile data access katmanındaki sınıfları kullanarak işlemlerimizi gerçekleştiriyoruz.

WebAPI

Son olarak web api katmanı içinde bir controller oluşturarak burada da IProductService isimli interface’i çağırıyoruz. Şimdi kısaca bir özet geçelim. WebAPI den business katmanına istekte bulunuyoruz daha sonra isteği alan business katmanındaki ilgili sınıf kendi içinde data access katmanındaki ilgili sınıfa istekte bulunuyor oda repository sınıfına giderek tüm ürünleri getiriyor. Burada olabildiğince az bağımlılık oluşturmak için interface ler üzerinden işlem yaptık ama bizim asıl işlemlerimizi bu interface ler değil de bunları implement eden sınıflarımız yapıyor. Peki projemiz bizim aslında interface üzerinden asıl sınıfları çağırmak istediğimizi nasıl anlıyor? İşte burada biz bunu startup dosyası içerisinde belirtiyoruz. Yani ben sana bu IProductService interface ile gelirsem sen git bana onun somut hali olan ProductService’i ver diyoruz. Şimdi gelelim bu işlemi yapmaya.

Startup Sınıfı

İşte servislerimizi startup sınıfında yukarıdaki gibi kayıt ediyoruz. Burada interface ler çağrıldığı zaman hangi somut sınıfların bu isteği karşılaması gerektiğini belirtiyoruz. Yukarıdaki kullanım sonucunda proje çalışır ve istediğimizi verir ama burada belli başlı sorunlar göze çarpıyor. Çok küçük bir demo projede bile 4 satır kod yazıp servislerimizi kayıt ettik. ConfigureServices metodunda pek çok konfigurasyon da yapılabilir. Hal böyle olunca daha büyük projelerde bu metodun okunabilirliğinin ne kadar azaldığını görmek çok da zor değil aslında. Bu yüzden bunları burada tutmak yerine ayrı bir sınıfta tutmak kod okunabilirliğini arttırıp daha anlaşılır bir kod karşımıza çıkaracak böylece startup sınıfımız daha sade olacaktır. İşte bu noktada Autofac’i devreye alıyoruz. İlk önce aşağıdaki paketleri hem business katmanına hem de api katmanına ekliyoruz.

Gerekli Paketler

Daha sonra business katmanı içerisinde resolvers isminde bir klasör oluşturup onun içinde de Autofac adında bir klasör oluşturuyorum. Autofac klasörü içinde DataModule adında bir sınıf oluşturup tıpkı startup da yaptığım konfigurasyonu bu sınıfta yapıyorum.

Data Module

DataModule sınıfını Autofac’e ait olan Module sınıfından türetiyoruz. Daha sonra Module abstract sınıfı içerisinde bulunan virtual Load metodunu override edip sınıfımızda kullanıyoruz. Bu metot ContainerBuilder tipinde bir builder parametresi alıyor. Bu parametre ile servislerimizi projeye dahil ediyoruz. Builder nesnesi ile builder.RegisterType diyip somut nesnemi veriyorum daha sonra As ifadesi ile bu somut nesnenin interface halini veriyorum son olarak ise SingleInstance diyip tamamlıyorum. Burada eğer her servis çağrısında tek bir instance kullanılmasını istiyorsak SingleInstance ifadesini eğer her istek için bir instance kullanılmasını istiyorsak InstancePerLifetimeScope ifadesini eğer her servis çağrısında tek bir instance değilde yeni bir instance kullanmak istiyorsak ta InstancePerDependency ifadesini kullanabiliriz. Bu işlem sonrası artık startup içinde yaptığımız kayıt işlemlerini silebiliriz. Bu işlem sonrası artık yapmamız gereken son iş program.cs içerisine gelip burada uygulamaya ben kendi IoC container’ımı kullanacağım demektir.

Program.cs

Yukarıda UseServiceProviderFactory ile Autofac i kullanacağımızı belirtiyoruz. Daha sonra da DataModule içerisinde bu kayıt işlemini yaptığımızı söylüyoruz ve proje ayağa kalktığı zaman servisleri kayıt etmek için DataModule sınıfına bak diyoruz. Artık işlemler tamam. Projeyi ayağa kaldırıp swagger dan istek atıyoruz.

Products

İşte bu kadar. Şuana kadar Autofac ile kendi IoC container’ımızı oluşturduk ve bu sayede çok daha modüler bir yapıya kavuşup okunabilir bir kod yazdık ayrıca projemizi geliştireceğimiz zamanda neyin nerde olduğu ne yapılması gerektiği daha açık olduğu için yapılacak geliştirmelerde servislerimizi yönetmek çok daha kolay bir hal almış olacaktır. Kısacası bir IoC container kullanarak projemizde kullanılan objelerin daha kolay yönetilmesini sağlayıp bağımlılıkları azaltmış olduk.

Projenin Son Durumu

Yukarıda demo proje sonrası oluşan klasör ve sınıf yapısı solution explorer üzerinden gösterilmiştir.

Autofac’in IoC container olarak nasıl kullanıldığından bahsettiğim yazının sonuna gelmiş bulunuyoruz. Bir sonraki yazı da Autofac ile Aspect Oriented Programming nasıl yapılır konusuna değineceğim. Bir sonraki yazımızda görüşmek üzere.

Projenin kaynak kodlarına buradan ulaşabilirsiniz.

İyi çalışmalar.

Yorum bırakın