Bu site emekli olmuştur. Arşiv amaçlı olarak BT AKADEMİ sponsorluğunda yayın hayatına devam etmektedir.




C#nedir?com
 
YAZAR HAKKINDA
Levent Yıldız
Levent Yıldız
http://www.csharpnedir.com/
İletişme geçmek için tıklayın.
3 Makalesi yayınlanmakta.
Yazar hakkında detaylı bilgi için tıklayın.
Yayınlanan diğer makaleleri için tıklayın.
İlgili etiketler: #endregion #region dataset foreach nesnesinin object[] on go set private protected public return returnvalue string tablonun tutuldugu ADO.NET/SQL Levent Yıldız
 
YAZI HAKKINDA
Türü : Makale
Serbest Köşede C#nedir?com üyelerinin hazırladıkları yazılar yayınlanır. Bu yazılar editör incelemesine girmeden yayınlanır.
Seviyesi : Orta
Kategori : ADO.NET/SQL
Yayınlanma Tarihi : 10.9.2006
Okunma Sayısı : 38816
Yorum Sayısı : 4     yorum yaz
Site İçi AramaSİTE İÇİ ARAMA
Üye Girişini AçÜye GİRİŞİ
Üye girişi için tıklayın.
Kullanıcı Adı
Şifre
 
Beni her zaman hatırla
Bir hafta boyunca kullanıcı bilgilerinizi kullanıcı çıkışı yapana kadar hatırlar. (Paylaşılan bilgisayarlarda önerilmez.)
 
Şifremi / Kullanıcı Adımı unuttum.
 
.net TV RSS Serbest KÖŞE (?)
Serbest Köşede C#nedir?com üyelerinin hazırladıkları yazılar yayınlanır. Bu yazılar editör incelemesine girmeden yayınlanır.
emre TAŞ
Silindi
emre TAŞ
yazının devamı >
emre TAŞ
silindi
emre TAŞ
yazının devamı >
emre TAŞ
silindi
emre TAŞ
yazının devamı >
emre TAŞ
silindi
emre TAŞ
yazının devamı >
emre TAŞ
silindi
emre TAŞ
yazının devamı >
Makale Gönder Bende Yazmak İstiyorum
.net TV RSSBlogroll
Turhal Temizer
Conda install environment.yml Package 21.11.2024
Turhal Temizer
Mac OS/X Removing CUDA 21.11.2024
Burak Selim Şenyurt
Rust ile ECS Yaklaşımını Anlamak 21.11.2024
Burak Selim Şenyurt
Birlikte Rust Öğrenelim Serisi 21.11.2024
  Diğer Herşey
Sponsorlar
BT Akademi
Medya Portakal
Video Hosting Sponsoru
Csharpnedir.com bir Ineta üyesidir
Uzman Abi
Her Yönüyle C# - Sefer Algan
Entity Modeli ile Veri Erişim Bileşeni Hazırlamak
 
Kapat
Sayfayı Yazdır Sık Kullanılanlara Ekle Arkadaşıma Gönder MySpace Del.Ico.Us Digg Facebook Google Mixx Reddit StumbleUpon
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
  • Yazılan Yorumlar
  • Yorum Yaz
AĞU
9
2011
Yazınız için size teşekkür ederim. Gayet Başarılı...
KAS
20
2006
Teknik olarak çok etkilendim. Gerçekten tebrik ediyorum. Sadece anlatılan değil açtığı ufuklarda çok geniş.
EYL
12
2006
Üzerine emek verilmiş bir yazı. Teşekürler. Kaynak alınıp çok profesyonel noktalara götürülebilir. Bu konu üzerinde bende kafa yormuştum. Bir iki tercihimi belirtmek isterim Uzun uzadıya attribute kullanabilmek bana hep zor gelmiştir. O nedenle db değişken tipini reflection ile propertilerden anlasa daha kolay olabilir. Ayrıca entityden bahsettiğimiz anda bir db-katmanından bahsetmemiz gerekir diye düşünüyorum. Kısaca, db den bağımsız bir entity kavramını işaret ediyorum. İyi çalışmalar.
Sayfalar : 1 
Yorum yazabilmek için üye girişi yapmalısınız. Üye girişi için tıklayın.
Üye değilseniz Üyel Ol linkine tıklayarak üyeliğinizi hemen başlatabilirisniz.
 
  • Bu Konuda Son 10
  • 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