C# - Nibernate Mapping (Nesne Haritalama)

Yeni bir projeye başladığımı daha önceki Android yazımda belirtmiştim. Projenin iki ayağından sunucu için olan kısımı .NET web uygulaması olarak yapmaya çalışıyorum. Benim gibi datatable, datarow, sql string kullanmayı alışkanlık haline getirmiş birisi için yeni sayılabilecek MVC ve ORM (Object Relational Mapping) kavramlarını kullanmayı  planladım. Bu bağlamda projeye asp.net MVC3 ile NHibernate kullanarak başladım.

Öncelikli olarak şunu belirtmek isterim ki, kendi adınıza veya küçük ölçekli bir işletme için geliştireceğiniz uygulamalarda bağımlı olduğunuz geliştirici sayısı üç-beş kişiyi geçmiyor, yazacağınız uygulama eklentilerle ve kullanım oranıyla hayal edemeyeceğiniz büyüklüklere ulaşmayacak ise dilediğiniz şekilde kodlayabilirsiniz.

Ama uygulamada geliştirici sayısı artacak, uygulama eklentilerle ve uygulama alanı ile genişleyecekse nesneye dayalı uygulama geliştirme ve onun getirisi olan patternleri ve araçları kullanmak gerekiyor. ORM kavramı da ilk olarak bu kısımda devreye giriyor.

.NET MVC ile uygulama geliştirmeye başlayınca view, controller ile veri katmanını oluşturacak model kavramına giriş yapıyorsunuz. Ben NHibernate kullanmayı tercih ettim. Siz FluentNHibernate, Entity Framework gibi başka ORM araçlarını kullanabilirsiniz.

Bu açıklamalar ne kadar uzarsa uzasın örnek olmadan, kod görmeden kolay anlamayan biri olarak sizi de daha fazla sıkmadan örneğe geçelim. Örnek olarak bir kategori, ürün, sipariş üçlemesinden oluşan veritabanı üzerinden yola çıkalım.


 Veri tabanında tabloları oluşturduktan sonra genel görünüm soldaki şekilde olacaktır.Tabloları oluşturduktan sonra uygulama tarafında tabloları işaret edecek sınıfları proje içerisinde oluşturarak sağdaki resmi elde etmiş olacağız. Uygulama tarafında sınıf şemasını oluşturduktan sonra aşağıdaki gibi bir görüntü elde edeceğiz.
Sınıf şemasını incelediğimizde tüm sınıflarda ortak özellik olarak "ID" üyesini (kolonunu) görmekteyiz. NHibernate ile çalışırken her tabloya ait birincil anahtarı kesin olarak belirlememiz gerekir. Bu uygulamada ise tüm sınıflar için otomatik sayı (int / identity) türünden üyeyi anahtar olarak belirlemiş oldum.

Veri tabanı -> Nesne eşlemesinde veri tabanı üzerinde bulunan her kolonu tablo sınıfı üzerinde bir üyeye denk getirmekteyiz.

Sınıf tarafından bakıldığında veri tabanı kolonundan ve yapısından farklı üyelerin (Örn; Product->Category) de sınıf içerisinde yer aldığını görebiliriz.

Hazırladığımız sınıfları nesne yönelimli programlamaya (OOP) daha uygun hale getirmek için ortak üyeleri bir sınıf altında toplayıp diğer sınıflarda bu ana sınıfı miras almamız gerekir. Bu sayede kod yazımı ve kontrolü daha kolay sağlanmış olur. En azından bundan sonra üreteceğiniz diğer sınıflar için "ID" kolonunu yazmak zorunda kalmazsınız. Sınıfları "_Base" ve "_BaseCaption" adlı iki sınıftan türettikten sonra aşağıdaki şemayı elde etmiş oluruz.


Veri tabanı, tablolar ve sınıfları oluşturduktan sonra asıl konumuz olan eşleşmeyi sağlayacak ilişkileri belirlemeye geldi. NHibernate kullandığınızda bu ilişkileri XML dosyasında oluşturmanız gerekecektir. Aynı şekilde FluentNhibernate kullansaydık .cs uzantılı dosyada kodlama yaparak veri tabanı haritalamasını gerçekleştirmiş olacaktık.

Projenize düzen hakim olması açısından sınıflarınızı ve harita dosyalarınızı ayrı bir klasörde tutmanızı öneririm. Hatta her sınıf için ayrı bir .cs ve .xml dosyası projenin anlaşılması bakımından oldukça faydalı olacaktır. En üst proje resminde haritalar için ayırdığım "Map" klasörü görünmektedir.

Her sınıf için teker teker eşleşme dosyalarını oluşturduk. Dikkat ederseniz dosyaların ismi sınıf isminden sonra ".hbm" takısı alıyor. Uygulama çalıştıktan sonra NHibernate eşleşme dosyalarını bu uzantıyı kullanarak arıyor ve bulduktan sonra eşleşmeyi gerçekleştiriyor.

Burada önemli olan bir konu da bu eşleşme dosyalarının assembly içerisine yerleşmesidir. Her harita dosyasının üzerine sağ tıkladıktan sonra özelliklerden "Build Action" özelliğini "Embedded Resource" yaparak hazırladığımız dosyaların derleme sonucu oluşacak DLL içerisine yerleşmesini sağlayabiliriz.

Harita dosyalarının içerisine gelecek olursak, sizlere iki farklı örnek üzerinde detayları verebilirim. Sipariş ürünleri (OrderProduct) tablosundan gidecek olursak resimde xml içeriği, sınıf şeması, tablosu ve sınıf kodu aşağıdaki şekilde olacaktır.


Görüldüğü gibi xml içerisindeki ikinci satırda NHibernate için haritalama kuralı yazılmaktadır. Burada dikkat edilecek kısım kırmızı çizgi ile belirtilmektedir. Oluşturacağımız namespace ve assembly bu kısımda mutlaka belirtilmelidir.

Tablo ismi ve sınıf ismi belirtildikten sonra "ID" kolonunun belirtilmesi gerekir. Burada birincil anahtar "ID" kolon değerinin otomatik olacağı "<generator class="native"/>" ibaresiyle belirtilmektedir.

Birincil anahtar sonrasında her bir üye tek tek "property" olarak tanımlanır. Üyelerin tanımının peşine gelen satırlarda ilişki tanımı yer alıyor. Tablomuzda bulunmayan fakat sınıfımızda tanımladığımız nesneleri burada ilişkili hale getiriyoruz.

Üyeleri (kolon bazında) tanımladıktan sonra sınıf içerisinde belirlediğimiz ilişkili üyeleri tanımlamamız gerekir. Bu kısımda "many-to-one , one-to-one" ilişkilerini tanımlayabilirsiniz. İlişki tanımlamada "name, class, column" üçlüsü yani yukarı mavi renklerle belirttiğim kısımlara dikkat etmemiz gerekir. Aynı veri tabanı tablolarında olduğu gibi ilişkinin artması performansı çekilecek sorgunun büyüklüğünü etkilediği gibi nesne sınıflarında ilişki tanımının artması da aynı etkileri yapar.

Performans açısından ilk dikkat etmemiz gereken kısım ilişkilerin nesne yaratıldığında yüklenip yüklenmemesini "lazy" özelliği belirlemektir. Üç tip olarak nesne yüklemesini aşağıdaki gibi yapabiliriz.

  • Nesne yüklendiğinde ilişkili tablodaki veri de yüklensin lazy="false"
  • Sadece nesne yüklensin ilişkili tablodaki veri kullanmak istediğim zaman yüklensin lazy="proxy"
  • Sadece nesne yüklensin ilişkiyi gösteren nesne bile yüklenmesin lazy="no-proxy"

Veri tabanında ilişkili tabloların ilişkileri üzerinden birbirlerini etkilmesi "cascade" olayı da haritalamada tanımlanmalıdır. Eğer nesnelerin "insert, update, delete" işlemlerinde ilişkili tabloyu etkilemesini istiyorsanız cascade özelliğine ilgili değeri belirlemeniz gerekir. Oluşturma, silme, güncelleme işleminde ilişkili tablo önemli değilse benim gibi cascade="none" kullanabilirsiniz.


İkinci örneğimizde ilk örnekten farklı olarak "bag" kavramına değinmek isterim. Bir kaydın diğer bir tabloda birden fazla alt kayıt içermesi durumunda liste tipi verileri kullanmamız gerekecektir. NHibernate ile üç liste  tipi kullanabiliriz.

  • "Bag" sırasız liste tipidir. Çift kayıtları (duplicate) içerebilir. .NET karşılığı olarak IList kullanılır.
  • "List" sıralı liste tipidir. Çift kayıtlara (duplicate) izin verilir. İndeks (Index) kolonunun belirtilmesi gerekir..NET karşılığı olarak IList kullanılır.
  • "Set" sırasız liste tipidir. Çift kayıtlara izin verilmez. Her kayıt eşsiz (unique) olmalıdır.
Yukarıdaki örnekte ilişkili alt verileri liste şeklinde elde etmek için "Bag" özelliğini kullandım. Bu özelliği kullanırken anahtar kolonu ve ilişki tipini de belirlemek gerekir. Anahtar (<key/>) kolon, ilişkili tablo üzerindeki kolondur. Ana tablo üzerinde varsayılan olarak id yani ID kolonu ilişkiyi belirler. Biraz karışık olduysa örnek üzerinden açıklayalım.


Ana Tablo
İd
İlişki Tablosu
Anahtar
Product
ID
OrderProduct
ProductID
Order
ID
OrderProduct
OrderID


İlişkiyi belirlemede id kolonunu kullanmak istemiyorsanız, ana tablo üzerinde ilişkiyi belirleyen farklı bir kolon varsa "bag" özelliğinde bunu da belirtebiliriz.

      <!--property-ref ana tablodaki, column ise diger tablo -->
      <key column="OrderID" property-ref="xID"></key>

Bu yazımızda Nhibernate ile kısaca temel tanımlamalara değinmiş olduk. Temel diyorum çünkü  NHibernate kullanmaya başladığınız zaman kullanacağınız parametrelerin bu kısa yazıya sığmayacak kadar çok olduğunu görünce hak vereceksiniz.

Yorum Gönder