.NetGurus

Diğer Yazılar

Google a Ekle

Add to Google



NHibernate İle Veritabanı İşlemlerini Loglama

Please Click Here to see the this article in English.

Özellikle yoğun veritabanı işlemleri gerçekleştirilen uygulamalarda veritabanındaki kayıtlarda hangi değişiklik, ne zaman ve hangi kullanıcı tarafından yapılmış gibi bilgilerin loglanmasına genellikle ihtiyaç duyulmaktadır. Kullanılan veri erişim katmanına göre farklı seçenekleri bulunan loglama işleminin, NHibernate ile kullanılabilecek bir yönteminden bahsedeceğim.

.NET ortamında kullanılan açık kaynak kodlu bir O/R M (Object/Relational Mapping) aracı olan NHibernate, Java dünyasında uzun süredir kullanılan Hibernate’in .NET dünyasına taşınmış halidir. O/R M araçları ve NHibernate’in kullanımına ilişkin konular bu yazımızın kapsamında olmayıp okuyucunun bu konularda bilgi sahibi olduğu varsayılmaktadır.

NHibernate ile veritabanı işlemleri yaparken, arka taraftaki veri kaynağının ne olduğuna bakılmaksızın ve genellikle SQL cümleleri kullanılmadan doğrudan uygulama katmanındaki nesneler üzerinde çalışılmaktadır. Yeni oluşturduğumuz, değiştirdiğimiz veya sildiğimiz nesnelerin veritabanına aktarılması işlemlerinden tamamen NHibernate Session’ı sorumludur.

Örneğin uygulamamızda Musteri ve Sehir adında iki adet sınıfımız olsun

Uygulama katmanında “Musteri” sınıfı tipinde bir nesnemizi yeni oluşturup veritabanına kaydetmek için aşağıdaki kod parçacığına benzer bir kod kullanmamız muhtemeldir.

 
Musteri musteri=new Musteri(); 
musteri.MusteriAdi = ""; 
musteri.Adresi = ""; 
musteri.BulunduguSehir=sehir; 
nhSession.Save(musteri);

NHibernate Session’ının Save metodu çağrıldığında, “musteri” nesnesi veritabanına kaydedilmiş olacaktır.

Buna benzer işlemler sırasında, NHibernate’te yer alan IInterceptor arayüzü, NHibernate Session’ı nesneyi kaydetmeden, güncellemeden veya silmeden önce uygulamanın bu nesnelere erişebilmesini ve isterse bu nesneler üzerinde değişiklik yapabilmesini sağlayan bir arayüzdür. Konumuz olan loglama işlemi için de, IInterceptor arayüzünü gerçekleyerek oluşturacağımız bir sınıf ile, NHibernate veritabanı işlemlerini yapmadan hemen önce araya girerek işlem yapılan nesnelerin ilgili özelliklerini alıp bunları istediğimiz log kaynağına yazmamız mümkündür.

IInterceptor arayüzü, kullanabileceğimiz bir çok metod imzası sağlamaktadır. Bu metodlar loglama işlemlerinin yanında bir çok amaç için kullanılabilir. Loglama konusuyla ilgili kullanabileceğimiz metotlara kısaca göz atacak olursak:

OnLoad : Nesneler veritabanından getirilmeden önce çağrılır
OnFlushDirty : Varolan bir nesne güncellenmeden önce çağrılır
OnSave : Yeni bir nesne veritabanına eklenmeden önce çağrılır
OnDelete : Varolan bir nesne veritabanından silinmeden önce çağrılır

Bu kadar teorik anlatımdan sonra IInterceptor arayüzünün kullanımını örnek bir sınıf ile görelim.

Öncelikle LoggingInterceptor sınıfımızın IInterceptor arayüzünü gerçekleyeceğini belirtiyoruz.

 
public class LoggingInterceptor : IInterceptor

Bunu yazdığımızda IInterceptor arayüzünde bulunan 14 metotun da imzalarının LoggingInterceptor sınıfımızda yer alması gerekmektedir. Bunlardan sadece bize lazım olanların içini doldurmamız yeterli olacaktır. İhtiyacımız olmayan diğer metotlardan dönüş tipi nesne (object) olanlar için sadece

  return null;


    ve dönüş tipi boolean olanlar için de

 return false;


dememiz yeterli olacaktır.

Veritabanına kayıt işlemlerini loglamak için, LoggingInterceptor sınıfımızdaki OnSave metodunu kullanmamız gerekmektedir.

 
public bool OnSave(object entity, object id, object[] state, string[] propertyNames, IType[] types){}

OnSave metodunu kullanabilmek için argümanlarının ne anlama geldiğini ve nasıl kullanabileceğimizi bilmemiz gerekir.
entity: Kaydedilecek olan nesne
id : Kaydedilecek olan nesnenin birincil anahtar değeri
state: Kaydedilecek olan nesnenin özelliklerinin değerleri
propertyNames: Kaydedilecek olan nesneye ait özelliklerin isimleri
types: Kaydedilecek olan nesneye ait özelliklerin veri tipleri

entity.GetType().Name ile kaydedilecek nesnenin tipinin adını (kaydedilecek nesnenin sınıfının adını) elde edebiliriz.
propertyNames ve state nesne dizilerinde oluşturacağımız bir döngü ile nesnenin her bir özelliğinin değerini tespit edebiliriz.
types nesne dizisinde oluşturacağımız bir döngü ile nesnenin her bir özelliğinin veri tipini tespit edebiliriz.

Bunlara ek olarak, örneğin bir web uygulaması yapıyorsak,
HttpContext.Current.Request.UserHostAddress ile kullanıcının IP Adresini
HttpContext.Current.User.Identity.Name ile kullanıcı adını elde edebiliriz.

Loglama işlemi için nesnenin özelliklerine ait değerleri state dizisinden elde edebileceğimizi söylemiştik. Bunu yaparen özelliklerin veri tiplerini de mutlaka dikkate almamız gerekmektedir. Veri tipi string, DateTime, int, decimal vb. olan özelliklerin değerlerini doğrudan state dizisinden elde edebiliriz. Fakat, özelliğin veri tipi Collection veya Entity ise, bu durumda state[i].ToString() bize o Collection veya Entity'nin adını döndürecektir.

Örneğin yukarıdaki örnekte bahsettiğimiz musteri nesnesinin kaydedilmesi sırasında,

 
for (int i = 0; i < propertyNames.Length; i++){
     stringBuilder.Append(propertyNames[i]);
     sb.Append(" : ");
     sb.Append(currentState[i].ToString()); 
}

şeklinde özellik değerlerini aldığımızı düşünürsek, musteri nesnesinin BulunduguSehir özelliğinin değeri "Sehir" olarak elde edilmiş olacaktır. Oysa bizim ihtiyacımız olan, musteri nesnesinin BulunduguSehir özelliğinin SehirAdi ve SehirId değerleridir. Bunları elde etmek için ise, Reflection kullanarak BulunduguSehir özelliğinin özelliklerine erişip bunların o anki değerlerini almamız gerekir.

 
for (int i = 0; i < propertyNames.Length; i++)
{
     if (!types[i].IsAssociationType && !types[i].IsCollectionType && !types[i].IsComponentType && !types[i].IsEntityType && !types[i].IsMutable)
     {
          sb.Append(propertyNames[i]);
          sb.Append(" : "); 
          if (state[i] != null)
               sb.Append(state[i].ToString());
          else
               sb.Append("NULL"); 
          sb.AppendLine();
     }
     else if (types[i].IsEntityType)
     {
         sb.Append(propertyNames[i]);
         sb.Append(" : ");
         if (state[i] != null)
              sb.Append(types[i].Name);

         sb.AppendLine();

         PropertyDescriptorCollection pdc = TypeDescriptor.GetProperties(types[i].ReturnedClass);
         for (int j = 0; j < pdc.Count; j++)
         {
              if (pdc[j].PropertyType.IsValueType || pdc[j].PropertyType==typeof(string))
              {
                   sb.Append(pdc[j].Name + " : " + pdc[j].GetValue(state[i]));
                   sb.AppendLine();
              }
         }
     }
     else
     {
          sb.Append("NULL");
          sb.AppendLine();
     }
}

İhtiyacımız olan bilgileri OnSave metodunun argümanları aracılığıyla aldıktan sonra ise, bu bilgileri saklamak istediğimiz log kaynağına yazmamız yeterli olacaktır. Örneğin sunucunun EventLog'una yazmak için

 
EventLog.WriteEntry("MusteriUygulamasi", sb.ToString());
yazmamız yeterli olacaktır.

LoggingInterceptor sınıfımızı nasıl oluşturacağımızı ve loglamak istediğimiz bilgileri nasıl elde edebileceğimizi inceledik. Peki bu LoggingInterceptor sınıfından NHibernate'in haberdar olmasını nasıl sağlayacağız?

NHibernate'te tüm veritabanı işlemlerimizi NHibernate Session'ı aracılığıyla yaptığımızı söylemiştik. NHibernate Session'ını oluşturmak için ise NHibernate SessionFactory'yi kullanarak genellikle şu şekilde oluşturuyoruz.

 
NHibernate.ISessionFactory sessionFactory = new NHibernate.Cfg.Configuration().BuildSessionFactory();
NHibernate.ISession nhSession = sessionFactory.OpenSession();

İşte bu işlem esnasında NHibernate Session'ının LoggingInterceptor sınıfımızdan haberdar olmasını ve yukarıda yazdığımız loglama işlemlerini yapmasını sağlayabiliriz. Bunun için Session oluşturmak için kullandığımız bu iki satırı şu şekilde değiştirmemiz gerekecektir.
 
NHibernate.ISessionFactory sessionFactory = new NHibernate.Cfg.Configuration().BuildSessionFactory();
NHibernate.ISession nhSession = sessionFactory.OpenSession(new LoggingInterceptor());

Yukarıdaki örneğimizde sadece yeni bir nesnenin veritabanına kaydedilmesi ile ilgili loglama işleminden bahsettik. Buna benzer olarak, varolan bir nesnenin güncellenmesi veya silinmesi gibi işlemler için de loglama işlemini uygulayabiliriz. Ekteki projede LoggingInterceptor sınıfının kullanımına ilişkin örnek bir uygulama verilmiştir.  Bu projede OnSave metodunun yanında OnFlushDirty ve OnDelete metodları da kullanılmıştır. 

LoggingWithNHibernate.zip (724,51 kb)


Categories: ASP.NET | C# | Database
Posted by omer on 25 Nisan 2008 Cuma 14:35
Permalink | Yorumlar (1) | Post RSSRSS comment feed