Jump to content

Kendi Dataadapter Nesnemizi Geliştirelim -1


wmismail

Recommended Posts

üm yazılımcı arkadaşlara selamlar,

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;

Okuma/Kriterlere göre arama

Saklama/Güncelleme

Silme

.Net ile bu işlemleri sqlconnection sqlcommand dataset vs. gibi nesnelerle gerçekleştirebiliyoruz.Asıl sorun, tablo sayısı çoğaldığında ,proje büyüdüğünde ve dağınık projelerde birden fazla form nesnesinde veritabanına erişmek zorunda kaldığımızda karşımıza çıkıyor.

Problemin detaylarına inmeden önce projemizde kullanacağımız örneğimizi hazırlayalım.

Bir senaryo yaratalım.Bir şirketin görev yönetimi sistemini geliştirdiğimizi düşünelim.Kullanıcı kimlik bilgilerinin ve görev bilgilerinin tutulduğu iki tablo yaratalım.Ayrıca bu tablolara ait bilgilerin kaydedilmesi ve silinmesi için Stored Procedure ler yaratalım.

USR_TBL (Kullanıcı kimlik bilgilerinin tutulduğu tablo)

USE [TestDatabase]

GO

/****** Object: Table [dbo].[uSR_TBL] Script Date: 09/08/2006 23:45:56 ******/

SET ANSI_NULLS ON

GO

SET QUOTED_IDENTIFIER ON

GO

SET ANSI_PADDING ON

GO

CREATE TABLE [dbo].[uSR_TBL](

[uSR_ID] [int] IDENTITY(1,1) NOT NULL,

[uSR_NAME] [varchar](50) COLLATE Turkish_CI_AS NOT NULL,

[uSR_SURNAME] [varchar](50) COLLATE Turkish_CI_AS NOT NULL,

[uSR_PASS] [varchar](50) COLLATE Turkish_CI_AS NOT NULL,

CONSTRAINT [PK_USR_TBL] PRIMARY KEY CLUSTERED

(

[uSR_ID] ASC

)WITH (PAD_INDEX = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY]

) ON [PRIMARY]

GO

SET ANSI_PADDING OFF

TSK_TBL (Görev bilgilerinin tutulduğu tablo)

USE [TestDatabase]

GO

/****** Object: Table [dbo].[TSK_TBL] Script Date: 09/08/2006 23:53:35 ******/

SET ANSI_NULLS ON

GO

SET QUOTED_IDENTIFIER ON

GO

SET ANSI_PADDING ON

GO

CREATE TABLE [dbo].[TSK_TBL](

[TSK_ID] [int] IDENTITY(1,1) NOT NULL,

[TSK_USRID] [varchar](50) COLLATE Turkish_CI_AS NOT NULL,

[TSK_SUBJECT] [varchar](50) COLLATE Turkish_CI_AS NOT NULL,

[TSK_DESC] [text] COLLATE Turkish_CI_AS NOT NULL,

[TSK_DATE] [datetime] NOT NULL,

CONSTRAINT [PK_TSK_TBL] PRIMARY KEY CLUSTERED

(

[TSK_ID] ASC

)WITH (PAD_INDEX = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY]

) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]

GO

SET ANSI_PADDING OFF

SP_USR_SAVE/DELETE (USR_TBL tablosuna ait saklama ve silme SP leri)

SET ANSI_NULLS ON

GO

SET QUOTED_IDENTIFIER ON

GO

CREATE PROCEDURE SP_USR_SAVE

@USR_ID INT,

@USR_NAME VARCHAR(50),

@USR_SURNAME VARCHAR(50),

@USR_PASS VARCHAR(50) OUTPUT

AS

BEGIN

SET NOCOUNT ON;

IF @USR_ID=0

BEGIN

INSERT

USR_TBL

VALUES

(

@USR_NAME,

@USR_SURNAME,

@USR_PASS

)

END

ELSE

BEGIN

UPDATE

USR_TBL

SET

USR_NAME=@USR_NAME,

USR_SURNAME=@USR_SURNAME,

USR_PASS=@USR_PASS

WHERE

USR_ID=@USR_ID

END

END

GO

//---------------------------------------------

SET ANSI_NULLS ON

GO

SET QUOTED_IDENTIFIER ON

GO

CREATE PROCEDURE SP_USR_DELETE

@USR_ID INT

AS

BEGIN

SET NOCOUNT ON;

DELETE FROM

USR_TBL

WHERE

USR_ID=@USR_ID

END

GO

SP_TSK_SAVE/DELETE (TSK_TBL tablosuna ait saklama ve silme SP leri)

SET ANSI_NULLS ON

GO

SET QUOTED_IDENTIFIER ON

GO

CREATE PROCEDURE SP_TSK_SAVE

@TSK_ID INT,

@TSK_USRID INT,

@TSK_SUBJECT VARCHAR(50),

@TSK_DESC TEXT,

@TSK_DATE DATETIME

AS

BEGIN

SET NOCOUNT ON;

IF @TSK_ID=0

BEGIN

INSERT

TSK_TBL

VALUES

(

@TSK_USRID,

@TSK_SUBJECT,

@TSK_DESC,

@TSK_DATE

)

END

ELSE

BEGIN

UPDATE

TSK_TBL

SET

TSK_USRID=@TSK_USRID,

TSK_SUBJECT=@TSK_SUBJECT,

TSK_DESC=@TSK_DESC,

TSK_DATE=@TSK_DATE

WHERE

TSK_ID=@TSK_ID

END

END

GO

//---------------------------------------------

SET ANSI_NULLS ON

GO

SET QUOTED_IDENTIFIER ON

GO

CREATE PROCEDURE SP_TSK_DELETE

@TSK_ID INT

AS

BEGIN

SET NOCOUNT ON;

DELETE FROM

TSK_TBL

WHERE

TSK_ID=@TSK_ID

END

GO

Sonuç itibariyle tablolarımız aşağıdaki gibi gözükecektir.

Resim 1 - Kullanıcı kimlik ve Görev bilgilerinin tutulduğu tablolar.

Yukarıdaki yapıya ulaşıp kullanıcı kayıtlarını veritabanından okumak istediğimizde aşağıdaki kodu yazıyoruz.

SqlConnection conSQL=new SqlConnection(@"Data Source=IT001\SQLEXPRESS;Initial Catalog=TestDatabase;Integrated Security=True");

SqlCommand comSQL = new SqlCommand("Select * from USR_TBL", conSQL);

conSQL.Open();

SqlDataReader drdSQL = comSQL.ExecuteReader(CommandBehavior.CloseConnection);

DataSet dsSQL = new DataSet();

dsSQL.Load(drdSQL, LoadOption.OverwriteChanges, new string[] { "USR_TBL" });

dataGridView1.DataSource = dsSQL;

dataGridView1.DataMember = "USR_TBL";

Fakat projenin diğer kısımlarında da aynı şekilde veri okumamız gerektiğinde aynı kodu kullanmak zorunda kalıyoruz.Tablo yapısının değişmesi gibi durumlarda ise bütün projeyi araştırıp güncelleme yapmamız gerekiyor ki bu geliştirme zamanını artırıyor ayrıca hata riskini de yükseltiyor.

Çözüm olarak n-tier modelini uygulamamız bizi veriye ulaşım'ın tek noktadan yapılmasını sağlar fakat büyük çaplı projelerde modüllerin veya yeni tabloların eklenmesi tekrar bizim her tablo için ayrıca kod yazmamızı gerektirir.

Bu noktada çözüm üretebilmemiz için standardizasyona gitmemiz gerekmektedir.Yeni oluşturulan tablolara kayıt eklemek için minimum kod yazımını gerçekleştirmemiz gerekecektir.

Peki bunu nasıl yapacağız?

Link to comment
Share on other sites

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

  1. Veritabanına bağlanmak
  2. Ekrandaki bilginin işlenmesi için uygun SQL cümlesini
  3. oluşturmak veya Stored Procedure'leri kullanmak
  4. 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; }

}

}

}

Link to comment
Share on other sites

Yeni oluşturacağımız bu sınıfın ismi TableAccess olsun.

İlk olarak select SQL cümlesinin otomasyonunu , TableAccess e gönderilecek Table nesnesini ve bu nesnenin attibute değerlerini barındıracak üyeleri yazalım.

using System;

using System.Collections.Generic;

using System.Text;

using System.Data;

using System.Data.SqlClient;

using System.Diagnostics;

using System.Reflection;

using System.Collections;

using System.ComponentModel;

public class TableAccess

{

public TableAccess()

{

}

public TableAccess(object tableobject)

{

this.TableObject = tableobject;

}

#region Variables

private string mConnectionString="";//veritabanı ile kurulacak bağlantının tutulduğu alan.

private object mTableObject; //Table nesnesini tutan alan. (Ör: User nesnesi)

private string mTableName; //TableObject nesnesinin değerlerinin tutulduğu alan.

private CommandType mTypeofSaveCommand; //TableObject nesnesinin değerlerinin tutulduğu alan.

private CommandType mTypeofDeleteCommand; //TableObject nesnesinin değerlerinin tutulduğu alan.

private string mSPNameforSave; //TableObject nesnesinin değerlerinin tutulduğu alan.

private string mSPNameforDelete; //TableObject nesnesinin değerlerinin tutulduğu alan.

#endregion Variables

#region GetSets

public string ConnectionString

{

get { return mConnectionString; }

set { mConnectionString = value; }

}

public object TableObject

{

get { return mTableObject; }

set

{

//bir tableobject nesnesi atandığında sınıfın attrib değerleri okunarak

//değerler yerel alanlara aktarılıyor.

mTableObject = value;

object[] o = mTableObject.GetType().GetCustomAttributes(typeof(AttribTableObject), false);

foreach (object m in o)

{

AttribTableObject ma = (AttribTableObject)m;

mTableName = ma.TableName;

mSPNameforSave = ma.SPNameforSave;

mTypeofSaveCommand = ma.TypeofSaveCommand;

mTypeofDeleteCommand = ma.TypeofDeleteCommand;

mSPNameforDelete = ma.SPNameforDelete;

}

}

}

#endregion GetSets

public DataSet Select(string SelectColumns,string SQLFromPart,string SQLWherePart,string SQLOrderPart)

{

StringBuilder mSQL = new StringBuilder();

SqlCommand comSQL = new SqlCommand();

comSQL.CommandType = CommandType.Text;

if (SelectColumns.Trim().Length==0)

SelectColumns="*";

//SQLFromPart parametresi boş değere sahipse standart işlem uygulanıp

//Attribute dan okunan değer ile SQL cümlesinin from kısmı oluşturuluyor.

//Eğer SQLFromPart parametresi gönderilmişse bu değer kullanılıyor.

if(SQLFromPart.Trim().Length==0)

mSQL.Append("SELECT " + SelectColumns + " FROM " + mTableName + " WHERE ");

else

mSQL.Append("SELECT " + SelectColumns + " FROM " + SQLFromPart + " WHERE ");

//reflection sınıfı kullanılarak nesne özelliklerine erişiliyor.

//her üyenin AttribDataAccess türünden nitelikleri okunuyor.

#region ReadAssemblyInformation

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 alan select işleminde kullanılmak üzere işaretlenmişse

if (d.IsUsedInSelect)

{

//TableObject nesnesinin üye değeri returnValue değerine aktarılıyor.

object returnValue = null;

returnValue = t.InvokeMember(m.Name,

BindingFlags.GetProperty,

null, mTableObject, new object[] { });

//Üyenin değeri NullValue nesnesine eşitse returnValue değeri null olarak atanıyor.

if (returnValue != null && d.NullValue != null)

if (returnValue.ToString()==d.NullValue.ToString())

returnValue = null;

//SqlDbType enum'unda bulunan DateTime türüne select cümlesi yazarken

//gerekli ayrıştırmalar yapılıyor.Saat bilgisi tutuluyorsa parametre olarak

//ekleniyor.

switch (d.DatabaseType)

{

case SqlDbType.DateTime:

if (returnValue.ToString() == DateTime.MinValue.ToString())

returnValue = null;

if (returnValue != null)

{

SqlParameter paramSQL;

string strDbColumnName = "";

if (d.DatabaseColumnName.Length == 0)

strDbColumnName = m.Name;

else

strDbColumnName = d.DatabaseColumnName;

mSQL.Append("Day(" + strDbColumnName + ")=@Day_" + strDbColumnName + " AND ");

paramSQL = new SqlParameter("@Day_" + strDbColumnName, Convert.ToDateTime(returnValue).Day);

comSQL.Parameters.Add(paramSQL);

mSQL.Append("Month(" + strDbColumnName + ")=@Month_" + strDbColumnName + " AND ");

paramSQL = new SqlParameter("@Month_" + strDbColumnName, Convert.ToDateTime(returnValue).Month);

comSQL.Parameters.Add(paramSQL);

mSQL.Append("Year(" + strDbColumnName + ")=@Year_" + strDbColumnName + " AND ");

paramSQL = new SqlParameter("@Year_" + strDbColumnName, Convert.ToDateTime(returnValue).Year);

comSQL.Parameters.Add(paramSQL);

//Eğer saat bilgisi 00:00:00 dan farklıysa gerekli parametreler

//yaratılıyor.

if ((Convert.ToDateTime(returnValue).Hour +

Convert.ToDateTime(returnValue).Minute +

Convert.ToDateTime(returnValue).Second) > 0)

{

mSQL.Append("Datepart(hh," + strDbColumnName + ")=@Hour_" + strDbColumnName + " AND ");

paramSQL = new SqlParameter("@Hour_" + strDbColumnName, Convert.ToDateTime(returnValue).Hour);

comSQL.Parameters.Add(paramSQL);

mSQL.Append("Datepart(mi," + strDbColumnName + ")=@Minute_" + strDbColumnName + " AND ");

paramSQL = new SqlParameter("@Minute_" + strDbColumnName, Convert.ToDateTime(returnValue).Minute);

comSQL.Parameters.Add(paramSQL);

mSQL.Append("Datepart(ss," + strDbColumnName + ")=@Second_" + strDbColumnName + " AND ");

paramSQL = new SqlParameter("@Second_" + strDbColumnName, Convert.ToDateTime(returnValue).Second);

comSQL.Parameters.Add(paramSQL);

}

returnValue = null;

}

break;

}

//Eğer üye bilgisi null değerinden farklı bir değere sahipse parametre

//nesnesi yaratılıyor.

if (returnValue != null)

{

SqlParameter paramSQL;

//Üye WildChar uygulanmak üzere işaretlenmişse SQL cümlesi

//LIKE ile oluşturuluyor.

string strEqualsSign;

if (d.IsWildCharImplemented)

{

returnValue = (object)returnValue.ToString().Replace("*", "%");

strEqualsSign = " LIKE ";

}

else

{

strEqualsSign = "=";

}

//Eğer DatabaseColumnName değeri boş ise reflection ile okunan

//değer atanıyor ve parametre yaratılıyor.

if (d.DatabaseColumnName.Length == 0)

{

mSQL.Append(m.Name + strEqualsSign + "@" + m.Name + " AND ");

paramSQL=new SqlParameter("@" + m.Name,returnValue);

}

else

{

mSQL.Append(d.DatabaseColumnName + strEqualsSign + "@" + d.DatabaseColumnName + " AND ");

paramSQL=new SqlParameter("@" + d.DatabaseColumnName,returnValue);

}

//Sonuç olarak parametre ekleniyor.

comSQL.Parameters.Add(paramSQL);

}

}

}

}

#endregion ReadAssemblyInformation

//hiçbir üyeye değer atanmamışsa SQL cümlesinin sonu WHERE ile biter

//Ayrıca her koşuldan sonra bir sonraki koşul için AND sözcüğü cümlede

//bırakılmıştır.Bu kontroller yapılıp SQL cümlesinin son halini alması

//sağlanıyor.

#region TrimSQL

if (SQLWherePart.Trim().Length > 0)

mSQL.Append(SQLWherePart);

string mSQLRelease = mSQL.ToString();

if (mSQLRelease.Substring(mSQLRelease.Length - 7, 7) == " WHERE ")

mSQLRelease = mSQLRelease.Replace(" WHERE ", "");

if (mSQLRelease.Substring(mSQLRelease.Length - 5, 5) == " AND ")

mSQLRelease = mSQLRelease.Substring(0, mSQLRelease.Length - 5);

if (SQLOrderPart.Trim().Length > 0)

mSQLRelease+=" ORDER BY " + SQLOrderPart;

#endregion TrimSQL

//Son olarak SQL cümlemiz çalıştırılıyor

#region DoConnectionAndAction

SqlConnection conSQL = new SqlConnection(mConnectionString);

comSQL.CommandText = mSQLRelease;

DataSet dsSQL = new DataSet();

try

{

conSQL.Open();

comSQL.Connection = conSQL;

SqlDataReader drdSQL = comSQL.ExecuteReader();

dsSQL.Load(drdSQL,LoadOption.OverwriteChanges,mTableName);

return dsSQL;

}

catch (Exception excpSQL)

{

throw excpSQL;

}

#endregion DoConnectionAndAction

}

//Aşırı yüklemeler

public DataSet Select(string SelectColumns, string SQLFromPart,string SQLWherePart)

{

return Select(SelectColumns, SQLFromPart, SQLWherePart, "");

}

public DataSet Select(string SelectColumns, string SQLFromPart)

{

return Select(SelectColumns, SQLFromPart, "", "");

}

public DataSet Select(string SelectColumns)

{

return Select(SelectColumns, "", "", "");

}

public DataSet Select()

{

return Select("*", "","","");

}

}

Kodu elimden geldiği kadar açıklayıcı yazmaya çalıştım.Umarım anlaşılmayan yer yoktur.

Artık kodumuz kullanıma hazır.Isterseniz bir select denemesi yapalım.

User u =new User();

TableAccess ta = new TableAccess(u);

ta.ConnectionString = @"Data Source=IT001\SQLEXPRESS;Initial Catalog=TestDatabase;Integrated Security=True";

DataSet ds = ta.Select();

dataGridView1.DataSource = ds;

dataGridView1.DataMember = u.TableName;

1000001028_resim002.JPGŞimdi olayı biraz daha karmaşıklaştıralım ve bir koşul belirtelim.Kullanıcı ismi (USR_NAME) '%eve%' koşuluna uyan kayıtları sorgulayalım.Dikkat edin bu işlem için ne sql sorgusu yazıyoruz ne de yeni parametreler oluşturuyoruz.

User u =new User();

u.USR_NAME="*eve*";

TableAccess ta = new TableAccess(u);

ta.ConnectionString = @"Data Source=IT001\SQLEXPRESS;Initial Catalog=TestDatabase;Integrated Security=True";

DataSet ds = ta.Select();

dataGridView1.DataSource = ds;

dataGridView1.DataMember = u.TableName;

1000001028_resim003.JPGDeğişiklik yaptığımız sadece 1 satır mevcut.Ayrıca user sınıfındaki birden fazla üyeyi kullanarak sorgulamamızın kriterlerlerini oluşturabiliriz veya sıralama koşulu belirtebiliriz.

User u =new User();

TableAccess ta = new TableAccess(u);

ta.ConnectionString = @"Data Source=IT001\SQLEXPRESS;Initial Catalog=TestDatabase;Integrated Security=True";

DataSet ds = ta.Select("", "", "", "USR_NAME DESC");

dataGridView1.DataSource = ds;

dataGridView1.DataMember = u.TableName;

1000001028_resim004.JPG

Link to comment
Share on other sites

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();

1000001029_resim005.JPG

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(); 1000001029_resim006.JPG

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.

Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.

  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...