Makale Özeti

ADO.NETin temelleri ve Datareader örnekleri

Makale

Ado.NET merhaba
Daha önce Windows platformunda veritabanı programlaması ile ilgilenen herkesin ADO ile iyi kötü bir tanışıklığı vardır. Günümüzde yazılan programların veritabanı ihtiyacı arttıkça veritabanları ile programlarımızın iletişimini sağlayan arabirimlerin de önemi arttı. Öyle ki artık ADO tek başına bir uzmanlık alanı geldi. Peki ama .NET Framework ile gelen ADO.NET bize ne gibi yenilikler ve kolaylıklar sunuyor ?
Birkaç yazıdan oluşacak bu dizide bu soruya yanıt bulmaya çalışacağız.Lafı fazla uzatmadan ADO.NET in temellerine ve bize sunduğu önemli classlara geçelim. Datareader , Dataset , Command  classları ve diğerleri... Bir veritabanını programınızda nasıl kullandığınızı ana hatları ile düşünün. Veritabanına bir bağlantı kurarsınız, bu bağlantı üzerinden bir komut yollarsınız (bir sql komutu ) ve geri dönen sonucu kullanırsınız. Ado ile çalışırken bu işlemi belki de binlerce defa yaptınız. Bu sırada da Connection, connectionstring ve recordset gibi kavramlar ile hayli samimi oldunuz. Ancak Ado ile kullanmaya alıştığınız bazı kavramlara artık elveda diyebilirsiniz. ADO.NET tamamen yeni bir programlama modeli ve araçlar ile geliyor.Şimdi bunların en önemlilerine, bir ısınma turu ile göz atalım.

Datareader class

Datareader classı bir veri kaynağından hızlı bir şekilde bilgi okumanız gerektiğinde kullanacağınız bir sınıf. (dikkatinizi çektiyse, object yerine class diyorum: çünkü ikisi farklı şeyler, ve Object Oriented programlaya saygısızlık etmek istemediğim için mümkün olduğunca bu ayrıma dikkat etmeye çalışacağım :) Diyelim tek yapmak istediğiniz, bir Access dosyası veya SQL veritabanı kullanarak, bir SQL sorgusunun sonuçlarını elde etmek . Sorgunuz Read-Only, yani bir bilgi yazmayacak, sadece belli kayıtları veri kaynağından alacaksınız, bu durumda Datareader class sizin için en uygun seçenek. Neden en uygun ? ve diğer seçenekler nelerdir ? En uygun seçenek, çünkü tam olarak bu basit işlemi yapmak için tasarlanmış bir class, ve bu işi son derece verimli ve hızlı yapmanıza olanak tanıyor. Datareader yerine ileride inceleyeceğimiz dataset sınıfını da kullanabiliriz, ama Dataset çok daha karmaşık işler için tasarlanmış bir sınıf, ve bu kadar basit bir veri alma işlemi için Dataset kullanmak gereksiz yere kaynak tüketmek olacaktır.

Peki o zaman, Datareadera geri dönelim; Datareader ile neler yapabiliriz ve bu işleri nasıl yapacağız ?

Önce bir bağlantıya ihtiyacımız var. Yani kullanacağımız veritabanına bir bağlantı açmalıyız. Bu işi de Connection class ile yapacağız. Adı üzerinde bağlantı kurmaya yarayan bir sınıf, ama kullanacağımız veri kaynağına göre farklı objeler yaratmalıyız.

  • Bir sql server veritabanı için SqlConnection objesini yaratarak kullanıyoruz.
  • Access dosyalarına erişmek için ise OleDBConnection  objesini yaratacağız.

Her iki sınıf da isimleri ile ne işe yaradığını açık seçik belirtiyor zaten, yine de ek bilgi olarak ekleyelim: eğer SQL server 6.x veya bir oledb kaynağına erişiyorsanız, OleDBConnection kullanmalısınız.
Bu yazıda kullanacağımız dil C# olacak, ve geliştirme ortamı olarak da herhangi bir metin editörünü kullanabilirsiniz. Bahsi geçen classlardan birer obje oluşturabilmek için bu classları içeren namespace lere referans  vermeliyiz. O zaman kullanacağımız dosyaları adım adım oluşturalım:

//Openacess.cs
using System;
using System.Data;
using System.Data.OleDb;

İşte bu satırlar ile access dosyasını açacağımız programımızın kaynak kodunu yazmaya başlamış olduk.

//OpenSQL.cs
using System;
using System.Data;
using System.Data.SqlClient;

Bu satırlar da SQL server bağlantımızın kullanacağı classlar için gerekli. Dikkat ederseniz namespace isimleri oldukça benzeşiyor.

Peki o zaman hemen bir access dosyasına bağlanmak için kullanacağınız OleDBConnection objesini nasıl yarattığımızı görelim:

OleDbConnection BenimBaglantim=new OleDbConnection(@"provider=Microsoft.Jet.OLEDB.4.0;Data Source=c:\veritabanı.mdb");

BenimBaglantim.Open();

Gördünüz gibi bağlantı açmak gayet kolay. Peki SQL server için nasıl bir bağlantı açıyoruz ?

SqlConnection BenimBaglantim=new SqlConnection("Data Source=localhost;initial Catalog=veritabani_adi;User id=kullanici_adi;password=sifre");

BenimBaglantim.Open();

Eh, oldukça benzer satırlar. Dikkat ederseniz, connection stringi yarattığımız objenin constructorına parametre olarak geçiyoruz. Bu yolu kullanmak zorunda değiliz, objeyi hiçbir parametre vermeden yaratıp, sonra ConnectionString propertysine kullanacağımız connectionstringi verebiliriz.
Peki, bağlantıyı açtık, artık veritabanına bir sorgu yollayabilir, ve sonucu alabiliriz. Peki ama komutu nasıl yollayacağız ? Command sınıfından yaratacağımız bir obje bu iş için bize yardımcı olacak.
SQL Server için:

SqlCommand benimkomutum=new SqlCommand("select * from tablo_adi",BenimBaglantim);


Access İçin:

OleDbCommand benimkomutum=new OleDbCommand("select * from tablo_adi",BenimBaglantim);


Burada da komutları parametre olarak objenin constructoruna geçtik. Artık elimizde bir bağlantı, o bağlantı üzerinden kullanabileceğimiz bir de sorgu var. O zaman sıra sorgumuzun sonuçlarını bize sunacak bir datareader objesi oluşturmakta.

SQL Server için:
SqlDataReader myreader=benimkomutum.ExecuteReader();
Burada önce datareadera bir referans oluşturuyor, sonra da bu referansa command objesi ile oluşturduğumuz datareader ı atıyoruz.

Access dosyası için:
OleDbDataReader myreader=benimkomutum.ExecuteReader();

Şimdi ne durumdayız bir bakalım: gerekli tüm nesneleri oluşturduk, ve veritabanlarına bağlantıları açtık. Daha sonra bu bağlantıları kullanarak çalıştırmak istediğimiz SQL komutunu belirttik, ve sonucu da bir datareader içine aldık. Harika, peki ama bu sonuçları nasıl kullanacağız ? İşte yanıtı:


//her iki örnekte de aynı:

for(int i=0;i<myreader.FieldCount;i++)
                  {
                        Console.WriteLine(myreader.GetName(i));
                  }
myreader.Read();
Console.WriteLine(myreader.GetString(1));
while (myreader.Read())
      {
      Console.WriteLine(myreader.GetString(1));
      }
myreader.Close();
BenimBaglantim.Close();

İşte bu kısımda datareaderın marifetlerinden bazılarını görüyoruz. İlk olarak for döngüsünün içindeki myreader.FieldCount propertysine dikkat. Datareader kullanarak bağlandığımız tablodaki kolonlarıln sayısını elde ediyoruz. Bu kolon sayısını kullanarak bir for döngüsü açıyoruz ve bu döngü içinde Datareaderin getname() fonksiyonu sayesinde kolonların isimlerini alıyoruz.
Daha sonra myreader.Read(); komutunu görebilirsiniz. Bu komut Datareader  classının temsil ettiği Rowu bir ilerletmeye yarıyor. Bu verdiğimiz komutu ado ile çalışırken kullandığımız meşhur rs.movefirst gibi düşünebilirsiniz. Birinci defa çalıştırıldığında datareader veritabanında ilk rowa işaret ediyor. Bu fonksiyon geriye BOOL tipinde bir değer döndürüyor. Eğer bağlandığımız veri kaynağında bir row ileri gitme işlemi başarılı olursa read() true değerini döndürür.
Daha sonra Console.WriteLine(myreader.GetString(1)); satırı ile basitçe nasıl veritabanındaki rowlardan bilgi alabileceğimizi görüyoruz . burada GetString(index) şeklinde kullandığımız fonksiyon index ile belirttiğimiz kolondaki bilgiyi alıyor.
Burada dikkat etmeniz gereken bir püf noktası var. Eğer bilgi almak istediğiniz alanın veri tipi sting değil de mesela bir int olsaydı, bu fonksiyon bir hata verirdi. Datareader classı performansdan kazanmak için size veri türlerini okurken çevirmek yerine direk olarak belirterek okuma şansı tanıyor. Eğer isterseniz her alanın türünü de okumanız mümkün. Peki ama hem türünü bilmiyorsunuz, hem de fazla uğraşmadan bilgiyi almak istiyorsunuz. O zaman onsole.WriteLine(myreader[1]); komutunu kullanabilirsiniz. Bu yazıma yabancı olan programcılara C# programmers reference içinde indexed properties kısmına bir göz atmalarını tavsiye ederim ( bu dokümanları .NET framework SDK içinde bulabilirsiniz)
Daha sonra, BOOL döndürdüğünü söylediğimiz read() fonksiyonu bize bir row döndürdüğü sürece veritabanındaki bilgileri okuyoruz. En sonunda ise önce datareaderi sonra da kullandığımız bağlantıyı kapatıyoruz. Birazdan göreceğiniz gibi bu kodları da bir namespace içinde kullanıyoruz.
Burada bir başka önemli noktaya dikkat çekmek istiyorum: .NET ile gelen Garbage Collection mekanizması sizin ayırdığınız her türlü kaynağı yönetemez!! Yani bir obje içinde bir veritabanı bağlantısı açıyorsanız, o bağlantı ile işiniz biter bitmez kapatın. .NET size bir çok kolaylık sağlayabilir ama bazı şeyleri de sizin yapmanız gerekecektir.
Şimdi kaynak kodumuzun tamamını ve nasıl derleyeceğimizi görelim: (her iki dosya da aşağıda )

//accessac.cs
using System;
using System.Data;
using System.Data.OleDb;
namespace accessfileopen
{
     
      class Class1
      {
            static void Main(string[] args)
            {
                  OleDbConnection BenimBaglantim=new OleDbConnection(@"provider=Microsoft.Jet.OLEDB.4.0;Data Source=c:\veritabanı.mdb");
                  BenimBaglantim.Open();
                  OleDbCommand benimkomutum=new OleDbCommand("select * from tablo_adi",BenimBaglantim);
                  OleDbDataReader myreader=benimkomutum.ExecuteReader();
                  for(int i=0;i<myreader.FieldCount;i++)
                  {
                        Console.WriteLine(myreader.GetName(i));
                  }
                  myreader.Read();
                 
                        Console.WriteLine(myreader.GetString(1));
                  while (myreader.Read())
                  {
                        Console.WriteLine(myreader[1]);
                  }
                  myreader.Close();
                  BenimBaglantim.Close();
                                   
                 
            }
      }
}

Bu dosyayı:
Csc /out:c:\accessac.exe c:\accessac.cs
İle derleyebilirsiniz. (derleyici ve ilgili konular için .NET framework kategorisi altındaki Assemblyler ile ilgili makalelere göz atabilirsiniz )

//sqlac.cs
using System;
using System.Data;
using System.Data.SqlClient;
namespace accessfileopen
{
     
      public class Class2
      {
            public Class2()
            {
            }
            public static void Main()
            {
                  SqlConnection BenimBaglantim=new SqlConnection("Data Source=localhost;initial Catalog=veritabani_adi;User id=kullanici_adi;password=sifre");
                  BenimBaglantim.Open();
                  SqlCommand benimkomutum=new SqlCommand("select * from tablo_adi",BenimBaglantim);
                  SqlDataReader myreader=benimkomutum.ExecuteReader();
                 
                  for(int i=0;i<myreader.FieldCount;i++)
                  {
                        Console.WriteLine(myreader.GetName(i));
                  }
                  myreader.Read();
                 
                  Console.WriteLine(myreader.GetString(1));
                  while (myreader.Read())
                  {
                        Console.WriteLine(myreader.GetString(1));
                  }
                  myreader.Close();
                  BenimBaglantim.Close();
                 
            }
      }
}

Bu dosyayı da:
Csc /out:c:\sqlsac.exe c:\sqlac.cs 
Yazarak derleyebilirsiniz.
Not: String kullanmam gereken yerlerde stringden önce kullandığım @ karakterine dikkat ettiniz mi ? Bu karakterin orada olmasının sebebi, " " tırnakları arasındaki string içinde herhangi bir escape sequence olursa , onun parse edilmesini engellemek . Yani dizinleri yazarken kullandığımız c:\dizin gibi bir yazım tarzının escape sequence olarak yorumlanmasın ve bize hata vermesini engellemiş oluyoruz.
Şimdilik bu kadar yeterli sanıyorum . Kendi başına son derece kapsamlı ve önemli bir konu olan ADO.NET e bir parça somut bir giriş yapmak istedim. Bu makaleyi takip eden yazılarda, ADO.NET in detaylarına ve .NET ile veritabanlarını nasıl kullanacağımıza göz atacağız. Bizi oldukça uzun bir yol bekliyor. Bir dahaki yazıda görüşmek üzere...