Ninject – Yaşam Döngüsü Yönetimi

   Dependecy Injection kütüphanelerini incelerken öncelikle türleri nasıl ele aldıkları, nasıl oluşturdukları konusuna eğildik. Dependecy Injection kütüphaneleri bu görevlerini yerine getirirken kaçınılmaz olarak oluşturdukları nesnelerin ne zaman yeni bir örneğinin oluşturulması gerektiğine, ne zaman mevcut bir örneğini kullanılması gerektiğine karar vermelidirler. Hatta bundan da öte; oluşturulan nesne örneklerinin ne zaman hafızadan atılması gerektiğine de karar vermelidirler. Bu bakış açısıyla, Dependecy Injection kütüphaneleri nesnelerin yaşam döngüsünü de yönetebilme yeteneğine sahiptirler. Ninject, yaşam döngüsü yönetimini bir adım ileri taşıyarak geliştiricinin de bu döngüde karar verebilmesine imkan sunmaktadır.

   Önceki makalelerimde paylaştığım örneklere dikkat edecek olursanız hepsinde her talepte bulunduğumuzda ilgili tür’ün yeni bir örneği verilmekteydi. Üstelik bir geliştirici olarak biz nesnelerin yaşam döngüleri konusunda Ninject’e hiçbir bildirimde bulunmamışken… Bu noktada Ninject’in kullanım kolaylığı ön plana çıkarak bizlerin aksini belirtmemiz durumunda varsayılan değerlerle kendisini yapılandırması önem kazanıyor. Ninject, bu varsayılan yaşam döngüsü kapsamı dışında bize farklı seçenekler de sunarak türlerin bağlanması yanında ne kadar süre ile aynı tür örneğinin kullanılacağına da karar verebilmemize olanak sağlamakta. Aşağıda, Ninject tarafından varsayılan olarak desteklenmekte olan nesne yaşam döngüsü kapsamlarını bulabilirsiniz;

Transient

   Ninject’in varsayılan yaşam döngüsü kapsamı olan Transient ile birlikte tür örnekleri her talepte bir öncekinden bağımsız olarak yeniden oluşturulmaktadır. Ninject, Transient olarak bağlaması yapılan bir türün yaşam döngüsünü hiçbir zaman için kontrol etmeyeceği için özellikle IDisposable arayüzüne sahip nesnelerin uygulamanız içerisinde dispose edildiğine emin olmalısınız. Öte yandan bu yaklaşım C#’ın nesne yaşam döngüsü yaklaşımı ile paralellik göstermesi nedeniyle pek çok uygulamada bir probleme neden olmamaktadır.

   Transient kapsamı, varsayılan olması nedeniyle önceki makalelerimde örneklediğim şekilde tanımlanabilir;

kernel.Bind<IGunlukDeposu>().To<TextGunlukDeposu>();

   Alternatif olarak, aşağıdaki şekilde InTransientScope fonksiyon çağrısı ile de tanımlama yapılabilmesi mümkündür;

kernel.Bind<IGunlukDeposu>().To<TextGunlukDeposu>().InTransientScope();

   Hatırlayacak olursanız örneği oluşturulabilir (Interface, abstract sınıf v.b. olmayan) sınıflarda bir binding tanımı yapılmaması durumunda varsayılan olarak aynı türe bağlama yapılıyordu. İstenirse ToSelf fonksiyon çağrısıyla da belirtilebilen bu tür bağlamaları için aynı zamanda Transient kapsamı tanımlanmış olacaktır;

kernel.Bind<OgrenciIslemleri>().ToSelf();

Singleton

   Adından da tahmin edebileceğiniz üzere nesne örneği uygulama yaşamı boyunca sadece bir defa oluşturulur ve yapılan tüm taleplerde aynı nesne örneği paylaşılır. Transient kapsamından farklı olarak Singleton kapsamında nesnelerin dispose edilmesi Ninject çekirdeği tarafından takip edilmektedir. Bu sebeple Singleton kapsamı ile oluşturulmuş bir nesne Ninject çekirdeği ile birlikte dispose edilecektir.

   Singleton kapsam tanımlaması için tür bağlamasında InSingletonScope fonksiyon çağrısı yapılmalıdır;

kernel.Bind<IGunlukDeposu>().To<TextGunlukDeposu>().InSingletonScope();
kernel.Bind<OgrenciIslemleri>().ToSelf().InSingletonScope();

   Alternatif olarak, ToConstant fonksiyon çağrısı ile tür bağlaması türün doğrudan bir örneği için yapılmaktadır. Dolayısıyla da ToConstant fonksiyonu doğrudan bir Singleton kapsamı oluşturacaktır.

Thread

   Bu kapsamda, talep edilen nesneler bağlı oldukları thread içerisinde bir defa oluşturularak bu thread’den gelen takip eden taleplerde aynı örnek paylaşılmaktadır. Singleton kapsamından farklı olarak Thread kapsamında nesnelerin tekilliği thread içerisindedir. Nesnenin yaşam ömrü talep edildiği thread ile sınırlıdır. Ninject çekirdeği ilgili thread’in sonlanması sırasında nesneyi dispose edecektir.

   Thread kapsam tanımlaması için tür bağlamasında InThreadScope fonksiyon çağrısı yapılmalıdır;

kernel.Bind<IGunlukDeposu>().To<TextGunlukDeposu>().InThreadScope();
kernel.Bind<OgrenciIslemleri>().ToSelf().InThreadScope();

Request

   Web uygulamaları için kullanılabilen bu kapsam, her bir web isteği için nesnenin sadece bir defa oluşturulmaktadır. Web request’in sonlanması ile birlikte nesne Ninject çekirdeği tarafından dispose edilecektir.

   Request kapsam tanımlaması için tür bağlamasında InRequestScope fonksiyon çağrısı yapılmalıdır;

kernel.Bind<IVeriDeposu>().To<XmlDosyaVeriDesposu>().InRequestScope();
kernel.Bind<IGunlukDeposu>().To<TextGunlukDeposu>().InRequestScope();
kernel..Bind<OgrenciIslemleri>().ToSelf().InRequestScope();

   Bu fonksiyonun Ninject.Web.Common Nuget paketi altında tanımlı olması nedeniyle öncelik NuGet paketini yüklemelisiniz;

Ninject.Web.Common NuGet paketi yüklenmesi

Named, Call,Parent

   ninject.extensions.namedscope eklentisi tarafından sunulan named, call ve parent kapsamlarında, nesne bağlı olduğu scope nesnesi .Net Garbage Collection tarafından toplandığında ya da Kernel.Release çağrısı yapıldığında dispose edilecektir.

Özel

   Yukarıda sıralanan yaşam döngüsü kapsamları dışında geliştiricilerin uygulamalarına özgü kendi yaşam döngüsü kapsamlarını tanımlayabilmelerine olanak vermektedir. Özel kapsam sayesinde geliştiriciler enjekte edilecek nesnelerin hangi sınırlar içerisinde yaşayabileceklerine karar verebilmekteler. Özel kapsam tanımlaması için tür bağlamasında InScope fonksiyon çağrısında yapılmalıdır. Bu yaklaşım temelde kapsam tanımı için aşağıdaki şekilde bir delegate kullanımına dayanmaktadır;

Func<IContext, object> kapsam

   Ninject, belirtilen delegate’ten aynı nesne döndüğü sürece talep edilen türün aynı örneği dönecektir. Oluşturulan nesneler daha sonradan talep edilebileceği göz önüne alınarak bir önbellekte tutulacaktır. Delegate’den dönen nesnenin garbage collector tarafından toplanması ile birlikte önbellekte tutulan bu nesneler serbest bırakılarak silinecektir.

   Özel kapsam kullanılırken dikkat edilmesi bazı noktaların altını çizmek gerekli;

  • InScope fonksiyonundan dönen nesne, bağlaması yapılan türler için oluşturulacak tüm nesnelerin yaşam döngüsünde belirleyici olacaktır. Bu sebeple, InScope fonksiyonundan dönen nesne garbage collector tarafından toplanarak hafızadan atılırsa, Ninject bu nesne kapsamında oluşturulmuş tüm nesne örneklerini temizleyecektir (IDisposable ise Dispose edecektir).
  • Bir tür için yapılan InScope çağrısından aynı değerin dönmesi durumunda Ninject bu tür için yeni bir nesne oluşturmak yerine daha önceden oluşturduğu nesneyi verecektir.

   Request kapsamı için paylaştığım örneği özel kapsam tanımı ile aşağıdaki şekilde yeniden yazabilmeniz mümkün;

kernel.Bind<OgrenciIslemleri>().ToSelf().InScope(ctx => HttpContext.Current);

   Bu örnekte Ninject çekirdeğinde her OgrenciIslemleri sınıfı talebinde çekirdek InScope fonksiyonuna geçilmiş olan delegate’i çağıracaktır. Tanımladığımız delegate bir web request’i boyunca her zaman aynı HttpContext nesnesini döneceği için Ninject çekirdeği OgrenciIslemleri sınıfını her bir web request’i için sadece bir defa oluşturacaktır. Web request’in sonlanması ile birlikte HttpContext nesnesi’de garbage collector tarafından toplanacağı için bu kapsamda oluşturulmuş tüm nesne örnekleri de pasif olacaktır.

   Ninject, bağlama tanımları sayesinde oluşturduğu nesnelerin ilgili kapsamları sonlandığında etkin şekilde temizlenmesi adına INotifyWhenDisposed arayüzünü tanımlamıştır. InScope fonksiyonuna geçilen delegate’in INotifyWhenDisposed arayüzüne sahip olması durumunda Ninject çekirdeği kapsamın sonlandığından daha etkin şekilde haberdar olması nedeniyle oluşturulmuş nesnelerin daha etkin temizlenmesi yapılabilecektir.

   Thread kapsamı için paylaştığım örneği özel kapsam tanımı ile aşağıdaki şekilde yeniden yazabilmeniz mümkün;

kernel.Bind<OgrenciIslemleri>().ToSelf().InScope(ctx => System.Threading.Thread.CurrentThread);

   Transient kapsamı için paylaştığım örneği özel kapsam tanımı ile aşağıdaki şekilde yeniden yazabilmeniz mümkün;

kernel.Bind<OgrenciIslemleri>().ToSelf().InScope(ctx => null);

   Singleton kapsamı için paylaştığım örneği özel kapsam tanımı ile aşağıdaki şekilde yeniden yazabilmeniz mümkün;

kernel.Bind<OgrenciIslemleri>().ToSelf().InScope(ctx => ctx.Kernel)

Fatih Boy

Ankara'da yaşayan Fatih, bir kamu kurumunda danışman olarak çalışmaktadır. ALM süreçleri, kurumsal veri yolu sistemleri, kurumsal altyapı ve yazılım geliştirme konularında destek vermektedir. Boş zamanlarında açık kaynak kodlu projeler geliştirmeyi ve bilgisini yazdığı makalelerle paylaşmayı seven Fatih, aynı zamanda Visual C# ve Visual Studio teknolojileri konusundan Microsoft tarafından altı yıl üst üste MVP (En Değerli Profesyonel) ödülüne layık görülmüştür. İş hayatı boyunca masaüstü uygulamaları, web teknolojileri, akıllı istemciler gibi konularda Asp.Net, Php, C#, Java programlama dilleri ve MySql, MsSql ve Oracle gibi veritabanı yönetim yazılımları ile çalışmıştır. İngilizce ve Türkçe olarak yayınlanan makalelerini gerek İngilizce bloğunda, gerekse de Türkçe bloğunda bulabileceğiniz gibi web sitesinden de açık kaynak kodlu geliştirdiği yazılımlarına ulaşabilirsiniz. vCard - Twitter - Facebook - Google+

Bir Cevap Yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir