Makale Özeti

Bu makalemde hepimizin veritabanı uygulaması geliştirirken karşılaştığı genel bir soruna çözüm sunmaya çalışacağım.Aslında buna işin angaryasını ortadan kaldırmak diyebiliriz.Geliştirdiğimiz veritabanı uygulamalarında en çok tekrarladığımız şey veritabanında bulunan bir tablo'ya veri göndermek ve okumak.Baz anlamda bu işleri sıralarsak;

Makale

Öncelikle normalde yaptığımız işleri bir inceleyelim.

  • Veritabanına bağlanmak
  • Ekrandaki bilginin işlenmesi için uygun SQL cümlesini
    oluşturmak veya Stored Procedure'leri kullanmak
  • Bu SQL cümlelerini veya SP leri çalıştırmak

Veritabanına bağlanma ve komutların çalıştırılması işlemlerini kısaltmamız mümkün değil.Ancak veri işlediğimiz tabloya ait SQL cümlelerini veya SP nesnelerini çalıştırmak için yazdığımız kod tablo kolon bilgilerine göre oluştuğu için burda bir otomasyon işlemi yapıp aradaki angarya işlemleri kaldırabiliriz.

Bunu gerçekleştirmemiz için nelere ihtiyacımız var?
Tablonun alanlarına?
Tablonun alanlarının özelliklerine?

Peki tablo alanlarının bilgilerine nasıl erişebiliriz?Gelin her tablo için projemizde bir sınıf yaratalım.
Yaratacağımız sınıf aşağıdaki Table isimli sınıftan türeyecektir.Bu sınıfın ne işe yaradığını ilerleyen bölümlerde anlatacağım, dolayısı ile şimdilik sadece Tablo sınıflarımızın aşağıdaki sınıftan türeyeceğini bilin yeterli.

public class Table
{
    public Table()
    {
        object[] o = this.GetType().GetCustomAttributes(typeof(AttribTableObject), true);
        foreach (object m in o)
        {
            AttribTableObject ma = (AttribTableObject)m;
            this.TableName = ma.TableName;
        }
    }

    private string mTableName="";        
    public string TableName
    {
        get { return mTableName; }
        set { mTableName = value; }
    }
}

Örneğin kullanıcı kimlik bilgileri için aşağıdaki sınıfı yaratabiliriz.

using System;
using System.Collections.Generic;
using System.Text;
using System.Data;

namespace DataAccessComponents
{
    class User:Table
    {
        private int mUSR_ID;
        private string mUSR_NAME;
        private string mUSR_SURNAME;
        private string mUSR_PASS;

        public int USR_ID
        {
            get { return mUSR_ID; }
            set { mUSR_ID = value; }
        }
        public string USR_NAME
        {
            get { return mUSR_NAME; }
            set { mUSR_NAME = value; }
        }
        public string USR_SURNAME
        {
            get { return mUSR_SURNAME; }
            set { mUSR_SURNAME = value; }
        }
        public string USR_PASS
        {
            get { return mUSR_PASS; }
            set { mUSR_PASS = value; }
        }
    }
}   
Gördüğünüz gibi tablonun her alanı
sınıfımızda bir üye.Basit anlamda
tablonun blue-print'ini çıkartmış
durumdayız.

Artık elimizde tablonun yapısı bir sınıf şeklinde bulunduğuna göre reflection sınıfını kullanarak bu sınıfın üye bilgilerini okuyarak tablonun alan isimlerine ulaşabiliriz.

Şimdi bir mola verelim ve düşünelim.User sınıfının üye bilgilerini okuyarak tablo kolon isimlerine ulaşabiliriz fakat SQL sorgusunu otomasyona almadan önce bazı hususlar hakkında fikir yürütmemiz gerekecektir.Örneğin tüm alanları sorguda kullanacak mıyız?, SQL sorgusunda FROM kısmında diğer tablolarla ilişki kurmamız gerekebilir?, Bazı üyelerin where koşuluna girmesi aldığı değere göre değişebilir? bazı koşullar wildchar '*' içeriyorsa where de like operatörünü kullanmamız gerekebilir?

Örnek:
USR_ID 0 değerini alıyorsa bu where koşuluna girmemeli.
USR_NAME değeri '*even*' değerini alıyorsa where koşulunda like operatörü kullanılmalı.
...

Bunlara ek olarak Tablo ismini almamız gerekecek.

Çok karıştı değil mi? Hepinizin -"sen simdi angaryayı kaldıracam dedin ama işin gidişatı daha kötü gibi gözüküyor" dediğinizi duyar gibiyim.Telaşa gerek yok.

Belkide çoğu zaman kulağınızda çınlayan  -"acaba ben bu attribute olayını nerde kullanabilirim" sorusuna cevap getiriyoruz.

Tablonun özelliklerini (Tablo ismi,Tablonun saklama/güncelleme işlemi için Stored Proc ismi,Tablonun delete işlemi için Stored Proc ismi) ve Tablo kolon özelliklerini (sql veri tipi, wildchar değeri, kolonun null değeri, select delete de kullanılıp kullanılmayacağı) kendi geliştireceğimiz bir attribute sınıfında tutacağız.

Sınıfımızı yazmadan tam olarak ihtiyaçlarımızı listeleyelim;

Tabloya yani Sınıfa atayacağımız attrib değerleri
Tablo ismi string TableName [Ör: USR_TBL]
Save komutunun tipi CommandType TypeofSaveCommand [Ör: CommandType.StoredProcedure]
Delete komutunun tipi CommandType TypeofDeleteCommand [Ör: CommandType.StoredProcedure]
Save komutunun ismi string SPNameForSave [Ör: SP_USR_SAVE]
Delete komutunun ismi  string SPNameForDelete [Ör: SP_USR_DELETE]
Kolonlara yani Sınıfın üyelerine atayacağımız attrib değerleri (AttribDataAccess)
Select komutuna dahil mi?  bool IsUsedInSelect
Save komutuna dahil mi? bool IsUsedInSave
Delete komutuna dahil mi?  bool IsUsedInDelete
Kolon ismi string DatabaseColumnName
Sql veri tipi SqlDbType DatabaseType
Wildchar kullanılacak mı? bool IsWildCharImplemented
Null değeri object NullValue
Parametre yönü ParameterDirection ParamaterDirection
Parametre boyutu int ParameterSize

System.Attribute sınıfından yukarıda listelediğimiz bilgileri içerecek iki attrib sınıfı türetelim.

AttribDataAccess
    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Data;
    
    [AttributeUsage(AttributeTargets.All , AllowMultiple = false)]
    public class AttribDataAccess:System.Attribute
    {
        #region Variables        
        protected bool mIsUsedInSelect=true;
        protected bool mIsUsedInSave=true;
        protected bool mIsUsedInDelete = false;
        protected string mDatabaseColumnName="";
        protected SqlDbType mDatabaseType;
        protected bool mIsWildCharImplemented;
        protected object mNullValue;
	protected ParameterDirection mParameterDirection=ParameterDirection.Input;
	protected int mParameterSize=0;         
        #endregion

        public AttribDataAccess()
        {
        }
        #region GetSets
        public bool IsUsedInSelect
        {
            get { return mIsUsedInSelect; }
            set { mIsUsedInSelect = value; }
        }
        public bool IsUsedInSave
        {
            get { return mIsUsedInSave; }
            set { mIsUsedInSave = value; }
        }
        public bool IsUsedInDelete
        {
            get { return mIsUsedInDelete; }
            set { mIsUsedInDelete = value; }
        }
        public string DatabaseColumnName
        {
            get { return mDatabaseColumnName; }
            set { mDatabaseColumnName = value; }
        }
        public SqlDbType DatabaseType
        {
            get { return mDatabaseType; }
            set { mDatabaseType = value; }
        }
        public bool IsWildCharImplemented
        {
            get { return mIsWildCharImplemented; }
            set { mIsWildCharImplemented = value; }
        }
        public object NullValue
        {
            get { return mNullValue; }
            set { mNullValue = value; }
        }
	public ParameterDirection ParameterDirection
        {
            get { return mParameterDirection; }
            set { mParameterDirection = value; }
        }
        public int ParameterSize
        {
            get { return mParameterSize; }
            set { mParameterSize = value; }
        }
        #endregion
    }    

AttribTableObject
    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Data;
    
    [AttributeUsage(AttributeTargets.All, AllowMultiple = false)]
    public class AttribTableObject : System.Attribute
    {
        #region Variables
        protected string mTableName;
        protected CommandType mTypeofSaveCommand=CommandType.StoredProcedure;
        protected CommandType mTypeofDeleteCommand = CommandType.StoredProcedure;
        protected string mSPNameforSave;
        protected string mSPNameforDelete;
        #endregion

        public AttribTableObject()
        {
        }
        #region GetSets
        public string TableName
        {
            get { return mTableName; }
            set { mTableName = value; }
        }
        public CommandType TypeofSaveCommand
        {
            get { return mTypeofSaveCommand; }
            set { mTypeofSaveCommand = value; }
        }
        public CommandType TypeofDeleteCommand
        {
            get { return mTypeofDeleteCommand; }
            set { mTypeofDeleteCommand = value; }
        }
        public string SPNameforSave
        {
            get { return mSPNameforSave; }
            set { mSPNameforSave = value; }
        }
        public string SPNameforDelete
        {
            get { return mSPNameforDelete; }
            set { mSPNameforDelete= value; }
        }
        #endregion
    }

Şimdi geliştirdiğimiz bu iki attribute sınıfını kullanıcı kimlik bilgileri tablomuz USR_TBL ye ait User sınıfımıza uygulayalım.

using System;
using System.Collections.Generic;
using System.Text;
using System.Data;

namespace DataAccessComponents
{
    [AttribTableObject(TableName = "USR_TBL",
    SPNameforSave = "SP_USR_SAVE",
    SPNameforDelete = "SP_USR_DELETE")]
    class User:Table
    {
        private int mUSR_ID;
        private string mUSR_NAME;
        private string mUSR_SURNAME;
        private string mUSR_PASS;

	[AttribDataAccess(DatabaseType = SqlDbType.Int,
        IsWildCharImplemented = false,NullValue = 0,IsUsedInDelete=true)]
        public int USR_ID
        {
            get { return mUSR_ID; }
            set { mUSR_ID = value; }
        }
	[AttribDataAccess(DatabaseType = SqlDbType.VarChar,
            IsWildCharImplemented = true, NullValue = "")]
        public string USR_NAME
        {
            get { return mUSR_NAME; }
            set { mUSR_NAME = value; }
        }
	[AttribDataAccess(DatabaseType = SqlDbType.VarChar,
            IsWildCharImplemented = true, NullValue = "")]
        public string USR_SURNAME
        {
            get { return mUSR_SURNAME; }
            set { mUSR_SURNAME = value; }
        }
	[AttribDataAccess(DatabaseType = SqlDbType.VarChar,
            IsWildCharImplemented = false, NullValue = "",
	    ParameterDirection=ParameterDirection.InputOutput,
            ParameterSize=50)]
        public string USR_PASS
        {
            get { return mUSR_PASS; }
            set { mUSR_PASS = value; }
        }
    }
}   

Evet artık tablonun güncellenmesini otomasyonla yapabilmemiz için sadece bu bilgileri ve nitelikleri okuyan ve gerekli SQL cümlesini veya SP leri çalıştırmak için komut nesnelerini oluşturan bir sınıfa ihtiyacımız kaldı.Bu sınıf reflection sınıfını kullanarak Tablo nesnelerinin veritabanı ile bağlantısını sağlayacak.

Levent YILDIZ
theone@leventyildiz.net
msmoracle@hotmail.com