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

Evet, artık select işlemlerini bitirdikten sonra, saklama prosedürüne geçebiliriz.
Yazdığımız SP_USR_SAVE stored procedure'ünü hatırlayalım.
Tablonun bütün kolonlarına ait parametreler mevcut.
Yapmamız gereken select işleminde yaptığımız işlerden farklı değil.Gene aynı şekilde sınıfın üyelerini ve niteliklerini okuyup gerekli parametreleri hazırlamak.

Save metodunu inceleyelim.

public void Save()
{
    //Connection nesnemizi ve command nesnemizi belirliyoruz.
    SqlConnection conSQL = new SqlConnection(mConnectionString);
    SqlCommand comSQL = new SqlCommand();

    comSQL.CommandType = mTypeofSaveCommand;
    switch (mTypeofSaveCommand)
    {
        //burada sadece Stored Procedure desteği mevcut.
        //isterseniz CommandType.Text case'i ekleyerek
        //kendiniz insert ve update SQL cümlelerini oluşturabilirsiniz.
        case CommandType.StoredProcedure:
	   //Stored Proc ismi atanıyor
            comSQL.CommandText = mSPNameforSave;

	   //üyelerin attribute bilgileri okunarak sqlparameter nesneleri oluşturulup
	   //comSQL command nesnesine ekleniyor.
            Type t = mTableObject.GetType();
            MemberInfo[] mi = t.GetMembers();
            foreach (MemberInfo m in mi)
            {
                object[] o = m.GetCustomAttributes(typeof(AttribDataAccess), true);
                foreach (object ma in o)
                {
                    AttribDataAccess d = (AttribDataAccess)ma;

		  //Eğer Üye Save işleminde kullanılmak üzere işaretlenmişse gerekli
		  //parametre yaratılıyor.
                    if (d.IsUsedInSave)
                    {
		      //TableObject nesnesinin üye değeri type sınıfının invokemember 
		      //metodu kullanılarak okunuyor.
                        object returnValue = null;
                        returnValue = t.InvokeMember(m.Name,
                            BindingFlags.GetProperty,
                            null, mTableObject, new object[] { });

                        SqlParameter arrprmSQL = new SqlParameter("@" + m.Name, d.DatabaseType);
                        arrprmSQL.Direction = d.ParameterDirection;
                        if (d.ParameterSize > 0)
                            arrprmSQL.Size = d.ParameterSize;
                        arrprmSQL.Value = returnValue;

                        comSQL.Parameters.Add(arrprmSQL);
                    }
                }
            }
            break;
    }
    //connection açılıp sorgu çalıştırılıyor.
    try
    {
        conSQL.Open();
        comSQL.Connection = conSQL;
        comSQL.ExecuteNonQuery();
    }
    catch (Exception excpSQL)
    {                
        throw excpSQL;
    }

    //Eğer geri dönüş parametresi mevcutsa geri dönüş değeri,
    //TableObject nesnesinin ilgili üyesine atanıyor.
    Type tParam = mTableObject.GetType();
    MemberInfo[] miParam = tParam.GetMembers();
    foreach (MemberInfo m in miParam)
    {
        object[] o = m.GetCustomAttributes(typeof(AttribDataAccess), true);
        foreach (object ma in o)
        {
            AttribDataAccess d = (AttribDataAccess)ma;

            if (d.ParameterDirection!=ParameterDirection.Input)
            {     
		try
		{                   
                tParam.InvokeMember(m.Name,
                    BindingFlags.SetProperty,
                    null, mTableObject, new object[] {comSQL.Parameters["@" + m.Name].Value});
		}
		catch{}
            }
        }
    }
}

Bir örnekle Save metodunu bitirelim.

User u =new User();
TableAccess ta = new TableAccess(u);
ta.ConnectionString = @"Data Source=IT001\SQLEXPRESS;Initial Catalog=TestDatabase;Integrated Security=True";
u.USR_ID=0;
u.USR_NAME="Serkan";
u.USR_SURNAME="YILMAZ";
u.USR_PASS="123";
ta.Save();

Gördüğünüz gibi aynı sınıfı ve üyeleri kullanarak save veya select işlemlerini gerçekleştirebiliyoruz.

Aynı işlemleri Delete için yazalım.

public void Delete()
{
    SqlConnection conSQL = new SqlConnection(mConnectionString);
    SqlCommand comSQL = new SqlCommand();

    comSQL.CommandType = mTypeofDeleteCommand;
    switch (mTypeofDeleteCommand)
    {
        case CommandType.StoredProcedure:
            comSQL.CommandText = mSPNameforDelete;

            Type t = mTableObject.GetType();
            MemberInfo[] mi = t.GetMembers();
            foreach (MemberInfo m in mi)
            {
                object[] o = m.GetCustomAttributes(typeof(AttribDataAccess), true);
                foreach (object ma in o)
                {
                    AttribDataAccess d = (AttribDataAccess)ma;

                    if (d.IsUsedInDelete)
                    {
                        object returnValue = null;
                        returnValue = t.InvokeMember(m.Name,
                            BindingFlags.GetProperty,
                            null, mTableObject, new object[] { });

                        SqlParameter arrprmSQL = new SqlParameter("@" + m.Name, d.DatabaseType);
			arrprmSQL.Direction = d.ParameterDirection;                                
                        if (d.ParameterSize > 0)
                            arrprmSQL.Size = d.ParameterSize;
                        arrprmSQL.Value = returnValue;

                        comSQL.Parameters.Add(arrprmSQL);
                    }
                }
            }
            break;
    }

    try
    {
        conSQL.Open();
        comSQL.Connection = conSQL;
        comSQL.ExecuteNonQuery();
    }
    catch (Exception excpSQL)
    {                
        throw excpSQL;
    }

    //Eğer geri dönüş parametresi mevcutsa geri dönüş değeri,
    //TableObject nesnesinin ilgili üyesine atanıyor.
    Type tParam = mTableObject.GetType();
    MemberInfo[] miParam = tParam.GetMembers();
    foreach (MemberInfo m in miParam)
    {
        object[] o = m.GetCustomAttributes(typeof(AttribDataAccess), true);
        foreach (object ma in o)
        {
            AttribDataAccess d = (AttribDataAccess)ma;

            if (d.ParameterDirection!=ParameterDirection.Input)
            {
		try
		{                        
                tParam.InvokeMember(m.Name,
                    BindingFlags.SetProperty,
                    null, mTableObject, new object[] {comSQL.Parameters["@" + m.Name].Value});
		}
		catch{}
            }
        }
    }
}

ve test edelim.

User u =new User();
TableAccess ta = new TableAccess(u);
ta.ConnectionString = @"Data Source=IT001\SQLEXPRESS;Initial Catalog=TestDatabase;Integrated Security=True";
u.USR_ID=3;
ta.Delete();

Artık yapmamız gereken sadece User sınıfının üyelerine değer atamak ve ilgili komutu çağırmak.

Makalenin başında değindiğim Table sınıfına geri dönmek istiyorum.Save,Select,Delete metodlarını incelerseniz orda yoğun olarak tablo ismi kullanılmaktadır.Her kullanımda attribute okumak yerine nesne oluştuğunda attrib lerin okunmasi ve yerel alanlara atanması hem fazla kod yazmamızı engelleyecektir, hem de yaratacağımız TableObject sınıflarının sade gözükmesini sağlayacaktır.

Peki Görev Tablosu?
Hemen yazalım.

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

namespace DataAccessComponents
{
    [AttribTableObject(TableName = "TSK_TBL",
    SPNameforSave = "SP_TSK_SAVE",
    SPNameforDelete = "SP_TSK_DELETE")]
    class Task:Table
    {
        private int mTSK_ID;
        private int mTSK_USRID;
        private string mTSK_SUBJECT;
        private string mTSK_DESC;
        private datetime mTSK_DATE;

	[AttribDataAccess(DatabaseType = SqlDbType.Int,
        IsWildCharImplemented = false,NullValue = 0,IsUsedInDelete=true)]
        public int TSK_ID
        {
            get { return mTSK_ID; }
            set { mTSK_ID = value; }
        }
	[AttribDataAccess(DatabaseType = SqlDbType.Int,
            IsWildCharImplemented = false, NullValue = 0)]
        public string TSK_USRID
        {
            get { return mTSK_USRID; }
            set { mTSK_USRID = value; }
        }
	[AttribDataAccess(DatabaseType = SqlDbType.VarChar,
            IsWildCharImplemented = true, NullValue = "")]
        public string TSK_SUBJECT
        {
            get { return mTSK_SUBJECT; }
            set { mTSK_SUBJECT = value; }
        }
	[AttribDataAccess(DatabaseType = SqlDbType.VarChar,
            IsWildCharImplemented = false, NullValue = "")]
        public string TSK_DESC
        {
            get { return mTSK_DESC; }
            set { mTSK_DESC = value; }
        }
	[AttribDataAccess(DatabaseType = SqlDbType.DateTime,
            IsWildCharImplemented = false)]
        public string TSK_DATE
        {
            get { return mTSK_DATE; }
            set { mTSK_DATE = value; }
        }
    }
}   

Sadece TSK_TBL 'ye ait yukarıdaki sınıfı yazarak artık bu tabloya tüm veri işlemlerini gerçekleştirebilir duruma geldik.Tabiki her proje gibi bu da bitmeyecek bir projedir.T-SQL kodları kullanılarak tabloların sınıflarını yaratacak bir tool geliştirebiliriz veya AttribDataAccess niteliklerini daha da kısaltmak için SqlDbType türleri ile System.Type türleri arasında bağlantı kurabiliriz, sonuç olarak geliştirilmeye açık ve hayal gücümüzle sınırlı.

Bu makalemde Reflection sınıfını kullanarak nesnelerin üyelerinin bilgilerine, niteliklerine erişerek veritabanı işlemlerinin otomasyonunun nasıl yapılabileceği hakkında bilgi vermeye çalıştım.

Umarım yararlı olmuştur.
Hepinize mutlu günler dilerim. 

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