(Disconnected Database Operations with C#)
ADO.NET’e Genel Bir Bakış
Microsoft VS.NET’te veritabanı işlemleri ADO.NET ile gerçekleştirilmektedir. ADO.NET yazılımcının çeşitli veritabanlarıyla ilgili akla gelebilecek bütün işlemlere müdahale edebilmesi için geliştirilmiş .NET dillerinin hepsiyle birlikte kullanılabilen ortak bir yapıdır. Bu yapı içerisinde çeşitli veritabanı işlemlerini gerçekleştirmeye yönelik pek çok nesne, yöntem ve sınıf mevcuttur. (Bu işlemlere örnek vermek gerekirse; Kayıt ekleme, silme, değiştirme, arama, her türlü SQL işlemleri, transaction yönetimi, Stored Procedure kullanımı, ilişki yönetimi vb. gibi.)
ADO.NET ile veritabanı işlemleri temel olarak 2 kısımda incelenebilir;
• Bağlantılı Veritabanı İşlemleri (Connected Database Operations): Uygulamanın veritabanına sürekli bağlı olduğu yöntemdir. Değişiklikler veritabanına direk olarak yansımaktadır. Gerçek zamanlı erişim sağlar. Temel nesneleri Command ve DataReader nesneleridir.
• Bağlantısız Veritabanı İşlemleri (Disconnected Database Operations): Uygulama veritabanına sürekli bağlı değildir. İşlemler veritabanının gerekli tablolarını içeren sanal bir kopyası üzerinde yapılır. Gerektiği zaman bu veriler veritabanına aktarılır. Bu aktarımlar haricinde işlemler bağlantısız bir ortamda gerçekleşmektedir.

Bu yöntemin temel nesneleri DataSet ve DataAdapter nesneleridir. Yapı şekilde görüldüğü gibidir.

Dataset: Sanal veritabanı da diyebiliriz. Fiziksel veritabanındaki tabloları, satırları, ilişkileri yapısında barındırabilir. Bellekte geçiçi olarak tutulur.
DataAdapter: Datasetle, veritabanının haberleşmesini sağlayan nesne.
Connection: Veritabanına bağlanmak için kullanılan nesne
Veritabanı: Verilerin fiziksel olarak tutulduğu asıl veri kaynağı.
DataAdapter veri kaynağı ve DataSet arasındaki iletişimi sağlar. Tamamen çevrimdışı olarak ve veri sağlayıcı türünden bağımsız olarak çalışan DataSet içerisinde tabloları, bu tablolara ilişkin satır ve sütunları, tabloların birbirleriyle ilişkilerini hafızada saklayan bir araçtır. DataAdapter ise gerektiğinde DataSet’e veritabanında bulunan tabloları aktarır aynı zamanda DataSet’ten aldığı verileri veritabanına işleyebilmektedir. Connection ve DataAdapter sınıfları ise DataSet’ten farklı olarak veri sağlayıcı türüne bağımlı olarak çalışırlar (Örn; Access veritabanı bağlantısı için OleDbConnection ve OleDbDataAdapter nesneleri, SQL Server veritabanı bağlantısı için ise SqlConnection ve SqlDataAdapter nesneleri kullanılır). Veritabanı ile olan fiziksel bağlantı Connection sınıfı ile gerçekleştirilebilmektedir. Aşağıdaki şekle bakarak bağlantı yapısını daha somut bir şekilde kafanızda canlandırabilirsiniz;

DataSet’i daha iyi anlayabilmeniz açısından özelliklerine değinecek olursak;
Her zaman çevrimdışı olarak çalışır. Veritabanı ile direk bağlantısı yoktur. Tamamen bağımsız olarak çalışır.
İçerisinde tabloları, bu tablolara ait satır ve sütunları, tablo görüntülerini ve ilişkilerini barındırır.
Hızlıdır, hafızada tutulduğu için veritabanı ve ağ ortamındaki yavaşlıklar onu direk etkilemez.
Veritabanı sağlayıcılarından bağımsızdır. Uyumlu DataAdapter olduğu sürece DataSet’ler bütün veritabanı sağlayıcılarıyla çalışabilir.
Direk olarak veritabanı üzerinde işlem yapılmadığı için yapılan değişikliklerin geri dönüşü kolaydır. Yapılan değişikliklerin kaydını tutar. (Anahtar Kelime: RowState)
DataAdapter nesnesi ise veri sağlayıcı türlerine göre değişiklik göstermektedir. Fakat kullanım şekli ve yapısı hepsinde aynıdır. DataAdapter’ın 2 temel işlevi vardır
1. Veritabanındaki istenen verileri DataSet’e aktarmak
2. DataSet’teki istenen verileri veritabanına aktarmak
Veritabanındaki istenen verileri DataSet’e aktarmak için DataAdapter’ın Fill() metodu kullanılır.
DataSet’teki istenen verileri veritabanına aktarmak için ise DataAdapter’ın Update() metodu çağrılır.
Connection nesnesi de DataAdapter’ın veritabana bağlanarak verilere ulaşabilmesi ve veritabanında değişiklik yapabilmesi için gerekli fiziksel bağlantının açılmasını ve kapanmasını sağlar. Veri sağlayıcısına göre çeşitlilik göstermekle beraber kullanım ve yapı itibarıyle farklılık göstermez.
Dilerseniz verilen bu ön bilgilerin ışığında, temel veritabanı işlemlerinin nasıl yapıldığını uygulamalı olarak görelim.
Bunun için ilk önce basit bir access veritabanı oluşturalım, içerisinde “kisi” isimli bir tablo olsun. Alanlar ise id (AutoNumber), ad (Text), soyad (Text), meslek (Text) olsun.
Yeni bir proje açın ve aşağıdakine benzer bir form oluşturun.

Şimdi de tasarladığımız bu formun kodlarını oluşturmaya başlayın. Aşağıda bu forma ilişkin örnek kodlar bulunmaktadır.
Kod: Tümünü seç
...
//access bağlantı nesnelerini içeren sınıf
using System.Data.OleDb;
namespace Project1
{
public class WinForm1 : System.Windows.Forms.Form
{
...
//Veritabanı baglantı araçlarını formun her yerinde
// kullanabilmek için öntanımlama yapılıyor
OleDbConnection baglanti;
OleDbDataAdapter adaptor;
OleDbCommandBuilder komutolusturucu;
DataSet dsKisi;
DataRow yenikayit;
//Kaydet butonuna basmadan önce işlem tipi olarak
//”Yeni Kayıt” veya “Düzenle” butonlarından hangisine
// basıldığını anlamak için bir değişken oluşturuluyor
string islemtipi;
//Kayıtlar arasında dolasma işlemini gerçekleştirmek
//amacıyla seçili satırı gösteren değişken oluşturuluyor
int secilisatir=0;
...
private void WinForm1_Load(object sender, System.EventArgs e)
{
//Bağlantıyı sağlayacak olan ConnectionString yazılıyor.
baglanti=new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source="+Application.StartupPath+"\\..\\..\\defter.mdb");
baglanti.Open(); //Bağlantı açıldı.
// Adaptör nesnesi tanımlanıyor. SelectCommand’ı belirtiliyor.
adaptor=new OleDbDataAdapter("select * from kisi",baglanti);
//Yeni bir DataSet oluşturduk.
dsKisi=new DataSet();
//Adaptörümüz SelectCommand’ı ile seçtiği tabloyu,
//dsKisi DataSet’inin içerisine “kisi” tablo ismiyle kopyalıyor
adaptor.Fill(dsKisi,"kisi");
//Seçili satırdaki ilgili alanları textboxlara
// dolduran fonksiyon çağrılıyor
alanlaridoldur();
//Yeni Kayıt ve Düzenle butonlarına basmadan
// Textboxlar’a veri girişini engelleyen fonksiyon çağrılıyor
alanlarikilitle();
//Yeni Kayıt ve Düzenle butonlarına basmadan
// Güncelle ve İptal butonlarının kilidini açan fonksiyon çağrılıyor
butonkilidiniac();
//DefaultView bizim “kisi” tablomuzun bir kopyası gibi
//düşünülebilir. Aradaki fark ise silinen satırları içermemesi
//Biliyoruz ki Delete() metoduyla silinen satırlar adaptörün
//Update() komutu gelene kadar hala tabloda tutuluyor
//Bu yüzden view kullanıyoruz.Varsayılan görüntünün
//Silinen satırlar hariç bütün satırları içermesini belirtiyoruz.
//Bu sayede kayıtları gösterirken bu görüntüyü kullanacağız. dsKisi.Tables["kisi"].DefaultView.RowStateFilter=DataViewRowState.CurrentRows;
}
private void alanlaridoldur()
{
int i=secilisatir;
try
{
//TextBoxlar’a DataSet’in DefaultView’ında
//bulunan seçili kaydın verileri dolduruluyor
textBox1.Text=dsKisi.Tables["kisi"].DefaultView.Table.Rows[i]["ad"].ToString();
textBox2.Text=dsKisi.Tables["kisi"].DefaultView.Table.Rows[i]["soyad"].ToString();
textBox3.Text=dsKisi.Tables["kisi"].DefaultView.Table.Rows[i]["meslek"].ToString();
//Seçili kaydın numarası gösteriliyor
textBox4.Text=(i+1).ToString();
//Toplam kayıt sayısı gösteriliyor
textBox5.Text=dsKisi.Tables["kisi"].DefaultView.Count.ToString();
}
catch(Exception e)
{
MessageBox.Show(e.Message);
}
}
private void alanlarikilitle()
{
//alanlara veri girişini engelledik
textBox1.Enabled=false;
textBox2.Enabled=false;
textBox3.Enabled=false;
}
private void alanlarinkilidiniac()
{
//alanlara veri girişini açtık
textBox1.Enabled=true;
textBox2.Enabled=true;
textBox3.Enabled=true;
}
private void alanlaritemizle()
{
//alanlar temizleniyor
textBox1.Text="";
textBox2.Text="";
textBox3.Text="";
textBox4.Text="";
textBox5.Text="";
}
private void butonkilitle()
{
//Yeni Kayıt veya Düzenle butonlarına basıldığında
//butonların olması gereken durumlar ayarlanıyor
btnSon.Enabled=false;
btnSonraki.Enabled=false;
btnOnceki.Enabled=false;
btnIlk.Enabled=false;
btnYeniKayit.Enabled=false;
btnDuzenle.Enabled=false;
btnSil.Enabled=false;
textBox4.Enabled=false;
btnGuncelle.Enabled=true;
btnIptal.Enabled=true;
}
private void butonkilidiniac()
{
//Güncelle veya İptal butonlarına basıldığında
//butonların olması gereken durumlar ayarlanıyor
btnSon.Enabled=true;
btnSonraki.Enabled=true;
btnOnceki.Enabled=true;
btnIlk.Enabled=true;
btnYeniKayit.Enabled=true;
btnDuzenle.Enabled=true;
btnSil.Enabled=true;
textBox4.Enabled=true;
btnGuncelle.Enabled=false;
btnIptal.Enabled=false;
}
private void btnYeniKayit_Click(object sender, System.EventArgs e)
{
//Güncelle butonunda işlem tipini bilebilmek için
//değişkene yeni değeri veriliyor
islemtipi="yeni";
butonkilitle();
alanlarinkilidiniac();
alanlaritemizle();
}
private void btnDuzenle_Click(object sender, System.EventArgs e)
{
//Güncelle butonunda işlem tipini bilebilmek için
//değişkene duzenle değeri veriliyor
islemtipi="duzenle";
butonkilitle();
alanlarinkilidiniac();
}
private void btnSil_Click(object sender, System.EventArgs e)
{
if(MessageBox.Show("Kaydı kalıcı olarak silmek istediğinize emin misiniz?","Bilgi",MessageBoxButtons.YesNo,MessageBoxIcon.Question,MessageBoxDefaultButton.Button2)==DialogResult.Yes)
{
try
{
//Veritabanında daha önceden kayıtlı olan satırlar silindiğinde
//kayıtlar arasında gezerken
//”Deleted row information cannot be accessed through row”
//hatasını aşmak için böyle bir yöntem izledik. Buradaki ilk if bloğu
//seçili satırın daha önce veritabanında kayıtlı olup olmadığını anlıyor
string s="";
if (dsKisi.Tables["kisi"].Rows[secilisatir].RowState==DataRowState.Unchanged)
s="Unchanged";
dsKisi.Tables["kisi"].Rows[secilisatir].Delete();
//Bu if bloğu ise eğer satır daha önceden kayıtlı ise adaptorün
//Update() metodunu çalıştırıyor. Delete komutunun veritabanına
//yansıtılmasını sağlıyor.
if(s=="Unchanged")
{
//Adaptorün SelectCommand’ından hareketler InsertCommand,
//DeleteCommand ve UpdateCommand ifadelerini otomatik olarak
//oluşturuyor. Aksi takdirde Update Exception döndürecektir.
komutolusturucu=new OleDbCommandBuilder(adaptor);
adaptor.Update(dsKisi,"kisi");
}
MessageBox.Show("Kayıt silindi", "Bilgi", MessageBoxButtons.OK, MessageBoxIcon.Information, MessageBoxDefaultButton.Button1);
if (secilisatir>0)
secilisatir--;
alanlaridoldur();
}
catch(Exception hata)
{
MessageBox.Show(hata.ToString(), "Hata", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1);
}
}
}
private void btnGuncelle_Click(object sender, System.EventArgs e)
{
//Eğer yeni kayıt butonuna basıldıktan sonra Güncelle’ye basıldıysa
if (islemtipi=="yeni")
{
try
{
//DataSet’te boş yeni bir satır oluşturuluyor.
yenikayit=dsKisi.Tables["kisi"].NewRow();
//Satırdaki alanlara değerler atanıyor
yenikayit["ad"]=textBox1.Text;
yenikayit["soyad"]=textBox2.Text;
yenikayit["meslek"]=textBox3.Text;
//Satır datasete ekleniyor.
dsKisi.Tables["kisi"].Rows.Add(yenikayit);
MessageBox.Show("Kayıt başarılı bir şekilde eklendi.","Bilgi");
alanlaridoldur();
alanlarikilitle();
butonkilidiniac();
}
catch(Exception hata)
{
MessageBox.Show(hata.ToString(),"Hata",MessageBoxButtons.OK,MessageBoxIcon.Error,MessageBoxDefaultButton.Button1);
}
}
else if (islemtipi=="duzenle")
{
try
{
//Veritabanında daha önceden kayıtlı olan satırlar düzenlendikten
//sonra silindiğinde, kayıtlar arasında gezerken
//”Deleted row information cannot be accessed through row”
//hatasını aşmak için böyle bir yöntem izledik. Buradaki ilk if bloğu
//seçili satırın daha önce veritabanında kayıtlı olup olmadığını anlıyor
string s="";
if (dsKisi.Tables["kisi"].Rows[secilisatir].RowState==DataRowState.Unchanged)
s="Unchanged";
yenikayit=dsKisi.Tables["kisi"].Rows[secilisatir];
yenikayit["ad"]=textBox1.Text;
yenikayit["soyad"]=textBox2.Text;
yenikayit["meslek"]=textBox3.Text;
//Bu if bloğu ise eğer satır daha önceden kayıtlı ise adaptorün
//Update() metodunu çalıştırıyor. Değiştirilen bu satırın
//silinmeye çalışılması sonucu çıkacak olan hatayı
//önlemek için Delete komutunun veritabanına
//yansıtılmasını sağlıyor.
if(s=="Unchanged")
{
komutolusturucu=new OleDbCommandBuilder(adaptor);
adaptor.Update(dsKisi,"kisi");
}
MessageBox.Show("Kayıt başarılı bir şekilde güncellendi.","Bilgi");
alanlaridoldur();
alanlarikilitle();
butonkilidiniac();
}
catch(Exception hata)
{
MessageBox.Show(hata.ToString(), "Hata", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1);
}
}
}
private void btnIptal_Click(object sender, System.EventArgs e)
{
alanlaridoldur();
butonkilidiniac();
alanlarikilitle();
}
private void btnIlk_Click(object sender, System.EventArgs e)
{
//İlk kayda git
secilisatir=0;
alanlaridoldur();
btnOnceki.Enabled=false;
btnIlk.Enabled=false;
btnSon.Enabled=true;
btnSonraki.Enabled=true;
}
private void btnOnceki_Click(object sender, System.EventArgs e)
{
if (secilisatir>0)
{
//Bir önceki kayda git
secilisatir--;
alanlaridoldur();
btnSon.Enabled=true;
btnSonraki.Enabled=true;
}
else
{
btnOnceki.Enabled=false;
btnIlk.Enabled=false;
}
}
private void btnSonraki_Click(object sender, System.EventArgs e)
{
if (secilisatir+1<dsKisi.Tables["kisi"].DefaultView.Count)
{
//Bir sonraki kayda git
secilisatir++;
alanlaridoldur();
btnOnceki.Enabled=true;
btnIlk.Enabled=true;
}
else
{
btnSon.Enabled=false;
btnSonraki.Enabled=false;
}
}
private void btnSon_Click(object sender, System.EventArgs e)
{
//Son kayda git
secilisatir=dsKisi.Tables["kisi"].DefaultView.Count-1;
alanlaridoldur();
btnOnceki.Enabled=true;
btnIlk.Enabled=true;
btnSon.Enabled=false;
btnSonraki.Enabled=false;
}
private void textBox4_TextChanged(object sender, System.EventArgs e)
{
// TextBox’ta yazılan pozisyondaki kayda gitmek için
if (textBox4.Text!="")
{
int i=dsKisi.Tables["kisi"].DefaultView.Count; //Toplam kayıt sayısı
int t=Convert.ToInt32(textBox4.Text); //Gidilecek olan kaydın pozisyonu
if (i>=t)
{
secilisatir=t-1;
//alanları doldur
int a=secilisatir;
textBox1.Text=dsKisi.Tables["kisi"].DefaultView.Table.Rows[a]["ad"].ToString();
textBox2.Text=dsKisi.Tables["kisi"].DefaultView.Table.Rows[a]["soyad"].ToString();
textBox3.Text=dsKisi.Tables["kisi"].DefaultView.Table.Rows[a]["meslek"].ToString();
}
else
{
secilisatir=i;
//alanları doldur
int a=secilisatir;
textBox1.Text=dsKisi.Tables["kisi"].DefaultView.Table.Rows[a]["ad"].ToString();
textBox2.Text=dsKisi.Tables["kisi"].DefaultView.Table.Rows[a]["soyad"].ToString();
textBox3.Text=dsKisi.Tables["kisi"].DefaultView.Table.Rows[a]["meslek"].ToString();
}
}
}
private void textBox4_KeyPress(object sender, System.Windows.Forms.KeyPressEventArgs e)
{
//yalnız karakter girilmesi için
if (!Char.IsDigit(e.KeyChar))
e.Handled = true;
}
private void WinForm1_Closed(object sender, System.EventArgs e)
{
//DataSet’te yapılmış olan bütün değişiklikler
//veritabanına gönderiliyor ve bağlantı kesiliyor.
komutolusturucu=new OleDbCommandBuilder(adaptor);
adaptor.Update(dsKisi,"kisi");
baglanti.Close();
}
}
}
Eleştiri ve önerilerinizi bekliyorum.