DataReader Nesnesi
Önceki yazımızda
bahsettiğimiz gibi Command nesnesi, veri kaynağına karşı yürütülen ifadeleri (emkomutları/em)
temsil ediyor. Genellikle sorguları, bir veri seti döndürmek amacıyla işletiriz.
ADO.NET veri sağlayıcısı, komutları icra ettiren Command nesnesinin yanında, dönen
veri kümesini temsil edecek bir yapı da gerçeklemek durumundadır. Bu yapı, yazımızın
konusu olan DataReader nesnesidir.
DataReader, ADO ile ilgilenmiş olanlar için çok tanıdık bir nesneye benzer: RecordSet.
Yalnız DataReader sadece veri okumak amacıyla tasarlanmıştır ve çalışması veri kaynağıyla
kurulmuş canlı bağlantıyı gerektirir.
DataReader'in başlıca özellikleri:
DataReader, verilerin sadece ileri yönde hareket eden akışını temsil eder. DataReader
ile belli bir anda, sadece bir kayda (emsatıra/em) erişebiliriz. Tüm kayıtlara erişebilmek
için, veri akışının sonuna kadar hareket etmeliyiz. DataReader, kullandığı veri
kaynağı bağlantısını, kapanana kadar kendisi için kilitler.
DataReader Nesnesini Oluşturmak
Daha önceden bahsettiğimiz Connection ve Command nesnelerinin aksine, DataReader
nesnesini bir yapılandırıcı (constructor) metodla kendimiz oluşturamayız. DataReader,
Command nesnesinin ExecuteReader() metodu ile elde edilir. Bu metod, Command nesnesinin
ifade ettiği sorguyu işletir ve dönen kayıtları temsil edecek DataReader nesnesini
oluşturur. Kodlayarak görelim :
|
// Önce bir bağlantı oluşturup açalım SqlConnection objConn = new SqlConnection("Integrated Security=SSPI; Database=Northwind"); objConn.Open(); // bir seçim sorgusu oluşturalım. ihtiyacımız olan bir Command nesnesi // bir DataReader referansı tanımlayalım // ve komutu ExecuteReader() ile işletelim. Dönüş değeri bir DataReader nesnesi. |
Verdiğimiz kod parçasında dtrCustomers adındaki DataReader referansımıza, Command
nesnemizin ExecuteReader() metodundan dönen DataReader nesnemizi bağlıyoruz. Artık
elimizde canlı bir DataReader var ve kayıtları almayı deneyeceğiz. Ancak yeni oluşmuş
bir DataReader nesnesi, biz gerekli komutu verene kadar hiç bir kaydı getirmeyecektir.
Gerekli komut ise Read() metodudur. Bu metod ilk defa çağrıldığında ilk kaydı getirmek
isteyecektir. Kaydın var olup olmamasına göre true/ false değeri döndürecektir.
Tüm kayıtları almak istiyorsak, Read() metodunu false değeri alana kadar çağıran
bir çevrim (emloop/em) kurmalıyız. Bir kayda ulaştığımızı anladığımızda (yani Read()
metodu True değeri döndürdüğünde) belli bir sütunun değerine DataReader nesnesinin
Item indexer'i ile ulaşacağız. Hemen söylediklerimizi fîiliyata geçirelim
:
|
while ( dtrCustomers.Read() ) { Console.WriteLine( dtrCustomers["CompanyName"] ); } |
"Customers" tablosundan seçtiğimiz kayıtlarda "CompanyName" adlı sütunun değerini
alıyoruz. Kurduğumuz while çevrimine Read() metodunu işleterek girdiğimize dikkat
edin. Read() metodu her seferinde ibreyi bir kayıt sonraya ilerletiyor. Eğer getirecek
kayıt kalmamışsa bu metod false değeri verecektir; Ki bu da çevrimden çıkmak için
iyi bir neden
. RecordSet'te "sonsuz döngü" tadına aşina olanlar bu Read() metodunu
çok sevecekler.
Item üyelerinin dönüş tipi, tüm tiplerin atası olan System.Object'tir. Çalışma zamanında,
uygun .NET veri tipine çevrim (implicit casting) yapılmaktadır. Eğer, sütunun veri
tipini öneceden biliyorsak, DataReader'in özel eriştirici metodlarını kullanmak
daha verimli olacaktır. DataReader'in olası her veri tipi için eriştirici metodları
vardır: GetInt32(), GetString(), GetBoolean(), .. Yalnız bu metodları kullanırken
sütun adını değil, sadece sütun endeksini verebiliyorsunuz. Sütun ismini vererek,
endeksine ulaşmak da GetOrdinal() metoduyla mümkün.
Fazla geçmedi, DataReader'in açık olduğu sürece kullandığı bağlantıyı kilitlediğini
söylemiştik. DataReader bağlantının yakasını bırakana kadar, o bağlantı üzerinden
yeni bir operasyon yürütemezsiniz. Bağlantıyı tekrar eski haline getirebilmek için
DataReader'i, Close() metodunu çağırıp kapatmalıyız:
| dtrCustomers.Close(); |
Bu noktada hemen çok kullanışlı bir özellikten bahsedelim. Düşünün GetCustomers()
adında bir fonksiyon yazdınız. Bu fonksiyon, bir veritabanı bağlantısı açıp Customers
tablosundan tüm kayıtları seçecek ve dönen kayıtları bir DataReader nesnesi olarak
döndürecek. Ama DataReader nesnesini fonksiyondan döndürmemiz için, veritabanı bağlantısının
açık kalması gerekiyor. Yani fonksiyondan, bağlantı kapatılmadan çıkılacak demektir
bu. Peki sonra ne olacak? Bağlantımız nasıl ve kim tarafından kapanacak? Dışarıya
bir de, açtığımız bağlantının referansını döndürüp, fonksiyonun kullanıldığı yerde
bağlantının kapatılmasını ummalıyız belki. Ama hepsini unutun, çünkü ExecuteReader()
metodu, tam da bu işler için bir parametre alıyor: CommandBehavior.CloseConnection
. CommandBehavior sıralaması System.Data aduzayında buunuyor. (Bu sıralamanın diğer
üyelerini görmek için dokumantasyona başvurabilirsiniz.) Peki ExecuteReader() metodunu
bu CommandBehaviour.CloseConnection parametresiyle işletirsek dönen DataReader'de
ne farklı olacak? Çok basit: DataReader'in Close() metodu çağrıldığında, ilişkili
olduğu bağlantı nesnesinin de Close() metodu otomatikmen çağrılacak. Böylece bağlantının
kapatılıp kapatılmadığı konusunda endişemiz kalmayacak:
|
SqlDataReader dtrCustomers; dtrCustomers = cmdCustomers.ExecuteReader( CommandBehavior.CloseConnection ); dtrCustomers.Close(); // otomatikmen bağlantı da kapanacak. |
Tüm sütunları görmek için de şöyle bir yapı kurulabilir:
|
for (int i=0 ; i<dtrCustomers.FieldCount ; i++) { Console.WriteLine("{0}.sütun: {1}", i, dtrCustomers.GetName(i)); } //çıktısı: // 0.sütun: CustomerID // 1.sütun: CompanyName // 2.sütun: ContactName // 3.sütun: ContactTitle // ... |
Şimdi tüm bu belirttiklerimizi içeren komple bir konsol uygulaması yazalım:
| DataReaderOrnegi.cs | |
|
// derlemek için komut: // csc /r:System.Data.dll DataReaderOrnegi.cs using System; namespace Evcil.NET public class DataReaderOrnegi public static void Main() SqlDataReader dtrCust; // sütun sayısıni bilelim Console.WriteLine("-------------------------------------------"); // Sütunları yan yana yazalım.. 15 lik hanelere, ve sola yaslayarak Console.WriteLine(" // şimdi de, kayıtları listeleyelim.. // DataReader nesnesini kapatalım. Console.Write(" Kapatmak bir tuş vuruşu... "); } // Customers tablosundaki kayıtları bir DataReader nesnesiyle döndüren işlev } |
|
Programın çalışır görüntüsü:

Bir Not
Örneklerimizi, kayıtlar arasında, kurduğumuz bir çevrimle dolaşma mantığı üzerinde
yazdık. Ancak .NET'in GUI (Graphical User Interface) sunan teknolojilerinde
(Web Formları ve Windows Formları) veri bilinçli kontrolller olduğu için, verileri
gösterme işleminde genelde uygulamalarınızda böyle çevrimler kurmadan, tek bir satırda
kontrolün veri kaynağına, veri tutan nesnemizi atayarak gerçekleştiririz. (Örnek:
objDataGrid.DataSource = objDataReader ) Kayıtlar arası çevrim, kontrolün kendisi
tarafından otomatik yapılır. Bunlarla ilgili örnekleri, kontrolleri anlatacağımız
farklı yazılarımızda vereceğiz.
Ve Şema Bilgisi
Kimi zaman, gelen kayıt setinin şemasıyla ilgilenmemiz gerekebilir. Böyle bir durumda
DataReader'in GetSchemaTable() metodunu kullanıyoruz. Bu metod, ( System.Data ad
uzayına bağlı) DataTable türünde bir şema tablosu döndürüyor. Kullanımı şöyle:
|
// dtrCust adında bir SqlDataReader nesnemiz olsun DataTable dtCustSchema; dtCustSchema = dtrCust.GetSchemaTable(); |
Bir sütunun, .NET veri tipine ulaşmak için dönüş değeri System.Type olan, sütun
endeksini parametre alan GetFieldType() metodunu kullanırız:
|
// 0. sütunun .NET veri tipi Console.WriteLine ( dtrCust.GetFieldType(0).ToString() ) ; // örnek çıktı: System.Int32 |
Aynı sütunun veritabanına özgü veritipini ise GetDataTypeName() metodu ile string
türünde alırız:
|
// 0. sütundaki değerin SQL Server veri tipi Console.WriteLine ( dtrCust.GetDataTypeName(0) ) ; // örnek çıktı: int |
Bitirirken...
Bu yazıda, kayıtları okumakta kullandığımız temel ADO.NET nesnesi olan DataReader'in
özelliklerinden bahsettik. DataReader, büyük miktarda verileri göstermekte gerçekten
çok başarılıdır. Çünkü sadece tek bir kaydı hafızaya yükler. Daha önce gösterdiği
kayıtlar ve daha sonra göstereceği kayıtlar hakkında en ufak bilgisi yoktur ("İleriyi
görmez, geçmişi hatırlamaz") Bu nedenle, bu nesnenin, bir veritabanı tablosunu hafızada
temsil etmesini bekleyemeyiz. ADO.NET'in bu tip "offline" veri operasyonları
için sunduğu DataSet adında farklı bir nesne vardır. System.Data aduzayına bağlı
(yani tarafsız!) DataSet nesnesine, bir "Provider" nesnesi olan DataAdapter yardımıyle
veritabanından kayıt çekeriz. Ama ilginçtir ki, DataAdapter nesnesi de kayıtları,
kendi içinde DataReader nesnesi kullanarak almaktadır. ADO.NET konulu yazılarımız
devam edecek.
İyi çalışmalar.