SİTE
İÇİ ARAMA |
|
Blogroll |
|
|
|
Entity Modeli ile Veri Erişim Bileşeni Hazırlamak |
|
Gönderiliyor lütfen bekleyin... |
|
|
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
|
Not : Yazıda
anlatılan bütün kaynak kodları indirmek için tıklayınız.
.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?
Ö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.
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; |
Ş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; |
Değ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; |
|
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 kütüphanesini kullanarak nesnelerin üyelerinin bilgilerine,
niteliklerine erişerek veritabanı işlemlerinin otomasyonunun nasıl yapılabileceği
hakkında bilgi vermeye çalıştım.
Yazıda
anlatılan bütün kaynak kodları indirmek için tıklayınız.
Umarım yararlı olmuştur.
Hepinize mutlu günler dilerim.
Makale:
Entity Modeli ile Veri Erişim Bileşeni Hazırlamak ADO.NET ve SQL Levent Yıldız
|
|
|
-
-
Eklenen Son 10
-
Bu Konuda Geçmiş 10
Bu Konuda Yazılmış Yazılmış 10 Makale Yükleniyor
Son Eklenen 10 Makale Yükleniyor
Bu Konuda Yazılmış Geçmiş Makaleler Yükleniyor
|
|
|