DataReader Nesnesi

Bir seçim sorgusundan dönen, salt-okunur kayıtları ifade eden DataReader nesnesini inceliyoruz.

Ö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
SqlCommand cmdCustomers= new SqlCommand("SELECT TOP 10 * FROM Customers", objConn);

// bir DataReader referansı tanımlayalım
SqlDataReader dtrCustomers ;

// ve komutu ExecuteReader() ile işletelim. Dönüş değeri bir DataReader nesnesi.
dtrCustomers = cmdCustomers.ExecuteReader();

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;
using System.Data;
using System.Data.SqlClient;

namespace Evcil.NET
{

public class DataReaderOrnegi
{

public static void Main()
{

SqlDataReader dtrCust;
// datareader nesnemizi elde edelim
dtrCust = GetCustomers();

// sütun sayısıni bilelim
int fieldCount = dtrCust.FieldCount;

Console.WriteLine("-------------------------------------------");

// Sütunları yan yana yazalım.. 15 lik hanelere, ve sola yaslayarak
for (int i=0 ; i < fieldCount ; i++)
{
Console.Write("{0}", dtrCust.GetName(i).PadRight(15));
}

Console.WriteLine(" -------------------------------------------");

// şimdi de, kayıtları listeleyelim..
while (dtrCust.Read())
{
// her sütun için..
for (int i=0 ; i < fieldCount ; i++)
{
Console.Write("{0}", dtrCust[i].ToString().PadRight(15));
}
Console.Write( " " );
}

// DataReader nesnesini kapatalım.
// komut davranışını CloseConnection olarak belirttiğimiz için
// ilişkili bağlantı da kapanmış olacak.
dtrCust.Close();

Console.Write(" Kapatmak bir tuş vuruşu... ");
Console.Read();

}

// Customers tablosundaki kayıtları bir DataReader nesnesiyle döndüren işlev
static SqlDataReader GetCustomers()
{
SqlConnection objConn = new SqlConnection("Integrated Security=SSPI; Database=Northwind");
objConn.Open();
SqlCommand cmdCustomers= new SqlCommand("SELECT TOP 10 CustomerID, ContactName FROM Customers", objConn);
// CommandBehavior.CloseConnection parametresini kullanıyoruz !!!
SqlDataReader dtrCustomers = cmdCustomers.ExecuteReader( CommandBehavior.CloseConnection );
return dtrCustomers;
// bağlantı nesnesine dokunmadığımıza dikkat edin.
}

}
}

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.

,

Arşivden

Yorum bulunmuyor.

Yorum yazabilirsiniz


* [b], [i] ve [quote] serbest