Makale Özeti

Bu dokumanda Microsoft .NET altyapısı ile sunulan veri erişim teknolojisi ADO.NET’i detaylı olarak ele alıyor olacağız..

Makale

Bu dokumanda Microsoft .NET altyapısı ile sunulan veri erişim teknolojisi ADO.NET’i detaylı olarak ele alıyor olacağız..

Ø       Giriş

Ø       ADO.NET Nesneleri

Ø       Bağlantı Oluşturma

Ø       Command ve DataReader Nesneleri ile Çalışmak

Ø       DataAdapter, Data Set, Form ve Form Kontrolleri

Ø       Verileri Düzenlemek, Silmek ve Oluşturmak 

Bu dokuman beş ana bölümden oluşmaktadır.

-         İlk olarak ADO.NET’in yapısı hakkında genel bilgiler veriyor olacağım

-         Daha sonra SQL Server veritabanına bağlantı oluşturma ve bu bağlantıyı güvenli hale getirme konusunu ele alıyor olacağım

-         Read-Only, forward-only veri erişimini detaylı olarak ele alarak, bu tür veri erişim tekniğine dair pek çok örnek geliştireceğiz

-         Sonraki adımda Windows Form uygulamalarında data set kullanımını ele alıyor olacağız. Windows Form kontrollerine programatik olarak veri bağlama konusunda pek çok örnek yapacağız. Aynı zamanda bu bölümde Parent-Child ilişkilerini ele alacağız.

-         Son olarak Windows Form uygulamalarından SQL Server’da yer alan verilerin düzenlenmesini, silinmesini ve yeni verilerin eklenmesini ele alıyor olacağız. 

1 - ADO.NET Nesneleri

Her ne kadar ADO.NET varsayılan olarak üç adet data provider sunuyor olsa da ben bu dokumanda SQL Server 2000 ile ilgili işlemlerde kullanacağımız SQL Server Data Provider’ı ele alıyor olacağım. SQL Server Provider’ı, System.Data.SQLClient namespace’i aracılığıyla kullanabiliriz. SQL Server Provider’ı ile bize sunulan ADO.NET nesnelerine erişebilmek için bu namespace’i aşağıdaki sözdizimi ile sınıfımıza (Form, Modul, Class vb) dahil etmemiz gerekiyor; 

Imports System.Data.SqlClient 

Altı temel ADO.NET nesne sınıfı vardır. Bu sınıflar; Connection, Command, DataReader, DataAdapter, DataSet ve DataView sınıflarıdır. Bu bölümde bu altı sınıfı ve bu sınıfların ADO.NET uygulamalarında sıklıkla kullanacağınız en önemli özellik ve metodlarına odaklanıyor olacağız.

Connection Sınıfı
Connection sınıfı uygulamanızın bir veri kaynağında yer alan verileri okumasını ve veri kaynağına ekleme yapabilmesini sağlar. Bu sınıfın SQL Data Provider ile bize sağladığı SqlConnection nesnesini kullanarak sunucuya (sql server) NT veya SQL Server Authentication mekanizmalarından birini kullanarak bağlanabiliriz. ConnectionString yapısı ADO’da kullandığımız ConnectionString’lere oldukça benzemektedir. Ek olarak bağlantıyı Open metodu ile açabiliyor, Close metodu ile kapatabiliyoruz. Bağlantı kurma/kapama esnasında oluşan exception’ları yakalayabiliyor ve gerekli işlemleri gerçekleştirebiliyoruz. Örneğin veri okunabilen bir backup sunucunuz varsa, ana sunucuya bağlanılamadığı durumlarda uygulamanızın backup sunucusuna bağlanarak işlem yapmasını kolayca sağlayabiliyoruz. Uygulamalarımız, kullandığı Connection nesnelerini işleri bittiğinde mutlaka kapatmalıdırlar.

SqlConnection nesnesinin ConnectionString özelliği bağlantı oluşturulurken kullanılacak bağlantı satırını tanımlamanıza olanak verir.  DataSource özelliği bağlantının kurulacağı SQL Server’ı, Database özelliği SqlConnection nesnesinin erişeceği sunucuda çalışılacak veritabanını, ConnectionTimeout özelliği uygulamanın SqlServer’dan yanıt gelmesi için ne kadar bekleyeceğini (süre dolduğunda bir exception oluşturulur) tanımlamanızı sağlar.  

Command Sınıfı

Command sınıfı sunucuya gönderilecek deyimleri barındırır. Bu deyimler, Select ifadeleri olabilir, data manupulation ifadeleri olabilir veya sunucu üzerinde yeni nesneler oluşturacak veya varolan nesneler üzerinde değişiklik yapan ifadeler olabilir.

Bu deyimleri sunucuya göndermek için Sql Server data provider tarafından sunulan SqlCommand nesnesini kullanırız. SqlCommand nesnesinin üç önemli özelliği vardır. Bunlar; Commandtext, CommandType ve Parameters özellikleridir. CommandText özelliği T-SQL ifadesini, stored procedure adını veya tablo adını içerir. ADO.NET, CommandText özelliğini varsayılan olarak T-Sql olarak tanımlamıştır. CommandType özelliği üç değer alabilir: Text, StoredProcedure ve TableDirect. CommandText özelliğinde bir Stored Procedure adı belirttiğimiz durumlarda CommandType özelliğini StoredProcedure olarak, bir veya daha fazla sayıdaki tablodan, tablo(lar)daki tüm sütun ve satırları alacağımız durumlarda TableDirect belirtiriz. TableDirect ile birden çok tablo kullanmak istediğimiz durumlarda tablo isimlerini virgül ile ayırırız.

SqlCommand nesnesinin Parameters özelliği bir kolleksiyondur. Parametreler, Stored Procedure’ler, diğer veritabanı nesneleri ve T-SQL ifadeleri için dinamik olarak argumanlar oluşturmakta kullanılır. Özellikle veritabanında veri ekleme / güncelleme işlemleri gerçekleştirdiğiniz durumlarda parametreler oldukça faydalı olacaktır. Parametreleri aynı zamanda Stored Procedure’lerden output parametrelerini almak ve Stored procedure’lerden değer döndürmek amacıyla kullanabilirsiniz.

Command nesnesi, farklı T-SQL işlemlerini gerçekleştirmek için çeşitli metodlar sunar. Örneğin bir SELECT ifadesi ile bir DataReader nesnesini doldurmak için ExecuteReader metodunu kullanabilirsiniz veya nesneler oluşturmak veya varolan nesnelerin özelliklerini değiştirmek gibi geriye resultset döndürmeyen işlemler için geriye sadece kaç kayıt etkilendiğini döndüren ExecuteNonQuery metodunu kullanabiliriz. Command nesnesinin bir diğer metodu ExecuteScalar ise geriye sonuç satırlarını değil, sorgu sonucunda oluşacak sonuç kümesinin ilk satırının birinci sütunudur.

DataReader Sınıfı
DataReader sınıfı bir veri kaynağına read-only ve forward-only bağlantı imkanı sağlar. DataReader sınıfından oluşturulan nesneler açık bağlantı kullanır. DataReader nesneleri ile veri okuyabiliriz ancak veri ekleme/düzenleme işlemleri gerçekleştiremez, sonuç kümesi içinde ileri-geri hareket edemeyiz. DataReader sınıfından türetilen nesnelerin kullanımı, performansın, esneklikten daha önemli olduğu durumlarda tercih edilebilir. Bunun nedeni her DataReader nesnesinin kendisine özel bir Connection nesnesine gereksinim duymasıdır. Sunucu tarafında istemcilerden açılabilecek maximum bağlantı sayısına ulaşıldığında, istemcilerin yeni bir bağlantı oluşturabilmek için bağlantıyı açık tutan DataReader nesnelerinden birinin bağlantısını kapatmasını beklemeleri gerekecektir.

SQL Data Provider tarafından sunulan SqlDataReader nesnesini kullanmak için SqlCommand nesnesinin geriye bir sonuç kümesi döndüren ExecuteReader metodunu kullanırız. DataReader ile açtığınız bağlantıyı kapatmanız, sunucu performansı açısından önemlidir. Bağlantıyı Connection nesnesinin Close metodu ile kapatabiliriz. RecordsAffected özelliği DataReader’ın seçtiği kayıt sayısını döndürmektedir ancak hatasız bir değer ancak bağlantı kapatıldıktan sonra elde edilebilmektedir. Eğer sorgunun çalıştırılması başarısızlıkla sonuçlanırsa DataReader nesnesinin FieldCount özelliği 0 olarak dönecektir.

Bir DataReader nesnesi populate edildiken hemen sonra sonuç kümesinin birinci satırına odaklanır. Birinci satırı okumak için DataReader nesnesinin Read metodunu kullanırız. Read metodunu yeniden çalıştırarak bir sonraki kayıda ulaşabiliriz. Son satıra ulaşıldığında, gidilecek bir sonraki satır olmadığından Read metodu False değerini döner. Sütun değerlerini GetInt32, GetString veya GetSqlDateTime gibi Get metodlarından herhangi biri ile alabiliriz. Bu metod sütun değerlerini doğru veri türü formatlamasıyla edinmemizi sağlar. (Örnek: Drd1.GetString(0) Drd1 adlı DataReader’da bulunulan satırın birinci sütununun değerini string türünde dönecektir)

DataAdapter Sınıfı
DataAdapter
sınıfı SQL Server veri kaynağı ile bir sonraki adımda inceleyeceğimiz DataSet sınıfından türetilmiş nesneler arasında köprü görevi görür. DataAdapter sınıfını kullanarak bir DataSet’i populate edebileceğimiz gibi, local bir DataSet içindeki verileri SQL Server’daki veri ile senkronize edebilirsiniz. DataAdapter sınıfı ADO.NET içindeki en temel sınıflardan biridir ve ADO.NET uygulamalarının esnekliğini arttırmak amacıyla geliştirilmiştir. DataSet sınıfı kendisine bağlı hiyerarşik bir yapısı olan nesneleri ile bağlantısız (disconnected) veri erişim imkanı sunar. Bu nedenle DataAdapter sınıfı veri kaynağına sabit bir bağlantı olmasına ihtiyaç duymaz ve veri sorgulama, güncelleme gibi işlemler için anlık bağlantılar kurar.

SQL Server Data Providerı bize SqlDataAdapter sınıfını sunar. SqlDataAdapter nesnesini bir SqlConnection nesnesi ile bağlantılı olarak kullanmamız gerekir. DataAdapter nesnesinin kullandığı SqlConnection nesnesi, DataSet nesnesinde tutulan local veri ile sunucudaki veri arasında köprü görevi görür.

Local bir DataSet nesnesini populate etmek için DataAdapter nesnesinin Fill metodunu kullanırız. Bu metodu kullanabilmek için DataAdapter nesnesi, açık bir bağlantıya ihtiyaç duyar. Fill metodu çalıştktan sonra uygulamanız bağlantıyı kapatabilir ve local DataSet nesnesinde barındırılan veri ile çalışmaya devam edebilir.

SqlDataAdapter sınıfı SQL Server üzerindeki veriler üzerinde değişiklik yapabilmemizi sağlayan Update metodunu ve bu metoda bağlı olaral UpdateCommand, DeleteCommand ve InsertCommand nenelerini sunar. Bu özellikler için doğrudan T-SQL ifadeleri kullanabilir veya stored procedure’leri kullanabilirsiniz. Bu işlemler için gerekli parametreleri, InsertCommand, UpdateCommand ve DeleteCommand özellikleri tarafından sunulan parameters özelliğini kullanarak belirtiriz. Bu komutları çalıştırdığımızda değişiklikler local DataSet’te yapılır. DataAdapter nesnesinin Update metodunu kullanarak local DataSet’teki işlemleri, Sql Server üzerinde geçerli kılarız. 

SqlDataAdapter nesnesi, veri kaynağı ve yerel dataset arasında optimistic concurency’i destekler. Gerçekte ADO.NET pessimistic concurrency’i (işlem yapılırken veritabanının işlemi yapan dışındaki kullanıcıların kullanımına karşı kilitlenmesi) aktif tutmaz. Keyset cursor’lar ve bağlantılı recordset’ler ADO.NET’in içinde yer almaz. Optimistic concurrency esnekliğin arttırılmasını sağlarken local verideki güncellemelerin sunucuyla senkronizasyonu aşamasında daha fazla dikkat gerektirir. Örneğin; siz son erişiminizi yaptıktan sonra değişikliğe uğramış bir veriyi, local veri ile güncellemek istediğinizde bir optimistic concurrency violation oluşacak ve bir exception oluşturulacaktır.

“ Optimistic ve pessimistic concurrency çok kullanıcılı platformlarda kullanılan iki veri yönetim mekanizmasıdır. Pessimistic concurrency kullanılırken kullanıcı veri üzerinde bir değişikliğe başladığı anda ilgili veri kaynağı diğer kullanıcıların kullanımına karşı kilitlenir ve bu kilit işlem tamamlanana kadar açılmaz. Optimistic concurrency’de ise bu tür bir kilitleme yapılmaz, dolayısıyla siz bir veriye güncellemek üzere eriştiğinizde, siz güncellemelerinizi kaydetmeden başka bir kullanıcı kendi güncellemelerini kaydedebilir. ”

ADO.NET optimistic concurrency violation’dan kaynaklanacak hataları önlemek için iki teknik sunuyor. Birincisi RowUpdated eventi için bir prosedür oluşturabilirsiniz. ADO.NET bu eventi sunucudaki verinin, değişiklik yapılmış bir local dataset ile güncellenmesi talebi sonrasında oluşturur. RowUpdated eventi ile hata oluşması durumunda yapılacak işlemleri her satır için tanımlayabilirsiniz. İkinci yönetmde ise Update metodunu çalıştırmadan önce  SqlDataAdapter nesnesinin ContinueUpdateOnError özelliğinin değerini True olarak değiştirebilirsiniz. Bu sayede ADO.NET’in tüm gerçekleştirilebilir güncellemeleri gerçekleştirmesini ve tüm hata mesajlarını local DataSet’e yazmasını böylece daha sonra bu hatalarla ilgilenebilmemizi sağlarız.

DataSet Sınıfı
DataSet
bellekte yer alan, bir veya daha fazla sayıda tablo ve tablolar arasında ilişkiler içerebilen bir nesnedir. DataSet’i bellekte oluşturulan bir veritabanı olarak düşünebiliriz. DataSet nesnesi ve kendisine bağlı alt nesneler ADO.NET’in temeli olan bağlantısız veri erişimi mimarisinin temel unsurudur. Aşağıdaki grafik DataSet nesne modelini görüntülemektedir.

 



Şekil 1:1 – DataSet Nesne Modeli

 

Bir DataSet içinde yer alan tablolar birer DataTable nesnesidir.. DataSet içindeki tüm tabloların birleşimi ortaya DataTableCollection sınıfını ortaya çıkarır. DataSet içinde yer alan tablolar, DataSet içindeki diğer tablolar ile ilişki içinde olabilir. Bu, Northwind örnek veritabanının aynısını bir DataSet içinde oluşturabileceğimiz anlamına geliyor. Klasik ADO’da yer alan RecordSet sadece bir sonuç kümesi barındırabildiğinden bu tür bir yapı oluşturma imkanımız yoktu.

Datatable nesnesi iki temel yapıdan oluşur: DataColumn ve DataRow nesneleri. DataTable nesnesindeki sütunlar, DataTable nesnesinin şemasını oluşturan DataColumnCollection nesnesini oluşturur. Örneğin her bir DataColumn nesnesi, ilgili sütunun kabul edebileceği veri türünü belirler. DataTable nesneleri PrimaryKey özelliğini kullanabilirler. Bu özelliği, bir veya daha fazla sayıda DataColumn nesnesi içeren DataColumn dizisi ile tanımlayabilirsiniz. DataRowCollection sınıfı DataTable içindeki tüm satırları içerir. DataTable içinde yeni bir satır oluşturmak için NewRow metodunu çalıştırırız. Data sonra sütun değerlerini belirtir ve yeni DataRowu DataTable nesnesinin DataRowCollection nesnesine dahil ederiz.

ADO.NET DataSet içindeki tablolar arasındaki ilişkiyi DataRelation nesnesi ile sağlar. Bir DataSet nesnesi içindeki tüm DataRelation’lar DataRelationCollection nesnesini oluşturur. Ek olarak Constraint sınıfını kullanarak tablolar için yeni Constraintler tanımlayabilirsiniz. Bir Constraint nesnesi ile unique veya foreign key constaintlerini tanımlayabilirsiniz. Bir DataRelation nesnesi iki tablo arasında Parent-Child ilişkisini temsil eder. İki tablo arasında bir DataRelation nesnesi oluşturduğunuzda ADO.NET otomatik olarak child tabloda foreign key, parent tablodaki primary key alanında da unique constrainti oluşturur. DataSet nesnesinin DataRelationCollection sınıfı DataSet içindeki tüm DataRelation nesnelerini içerir. DataRelation nesnelerinin üyelerine DataTable nesnesinin ChildRelations ve ParentRelations özellikleri ile erişebilirsiniz.

Bir DataTable nesnesinin DataRowCollection’ından bir DataRow silmek için kullanabileceğimiz iki yöntem vardır. Birinci yöntem DataTable içindeki DataRow nesnelerinde geçerli olacak Delete metodunun kullanımıdır. Bu metod, DataRow nesnesinin RowState özelliğine Deleted değerini verecektir ancak aslında satırı local tablodan silmiş olmayacaktır (Uygulamanız silinmiş bir satırı DataRow nesnesinin RejectChanges metodunu kullanarak geri getirebilir). Alternatif bir yöntem ise bir satır için Delete metodunun uygulanmasıdır, bu sayede bu satır, AcceptChanges metodu tarafından geri getirilemeyecektir.

İkinci yöntem ise DataTable nesnesinin DataRowCollection nesnesinin Remove metodunun kullanılmasıdır. Bu metod, silmek istediğiniz satırın indexini belirtmenizi gerektirir. Index değeri 0dan başlar ve her kayıt için 1 artarak devam eder. Bu yöntemin kullanılması ile silinen bir DataRow, geri alınamaz.

“Local değişiklikleri veri kaynağı ile senkronize etmeyi planladığınız durumlarda Delete metodunu kullanmanızı öneririm. Update metodu veri kaynağı ile senkronizasyon işlemi sonrasında satır için AcceptChanges metodunu çalıştıracaktır. Bir satırın Remove metodu ile silinmesi, Update metodunun çalıştırılması bir concurrency violation hatası verecektir.”

DataView Sınıfı
DataTable
nesnesindeki DataView nesnesini, SQL Server’daki view nesneleri gibi görebiliriz. DataView nesnesi, DataTable nesnesinde doğrudan mümkün olmayan filtreleme, sorgulama ve gelişmiş arama özelliklerine sahiptir. Bir DataTable nesnesi, farklı filtreleme ve sıralama ayarları tanımlanmış birden çok adet DataView nesnesine sahip olabilir. Bir DataView nesnesindeki satırları RowFilter veya RowStateFilter özelliklerinden biri ile filtreleyebilirsiniz. RowFilter özellikleri WHERE ifadesi ile aynı formattadır. RowFilter ifadelerini çift tırnak (“) içinde belirtiriz. Eğer ifade değişkenlerden değer alıyorsa bunları tek tırnak ile birleştiririz. RowStateFilter özelliği DataTable içindeki satırların DataViewRowState özelliklerine göre filtrelenmesini sağlar. Bu özelliğe göre sıralama yaparak Delete metodu ile silinen, yeni eklenen ve değerleri değiştirilen satırların listesini alabiliriz.

DataView nesnesinin Sort metodu ile DataView içindeki satırları sıralayabiliriz. Eğer birden çok satıra göre sıralama yapmak istiyorsak sütun isimlerini virgül (,) ile ayırarak sıralamayı gerçekleştirebiliriz. Bir sütuna göre artan (1-2-3-4..) sıralama yapmak için ASC, azalan (9-8-7..) sıralama yapmak için DESC ifadelerini kullanırız.

RowFilter özelliği ile bir satır veya sonuç kümesi döndürebilsekte, bunun kullanımı çok verimli değildir. RowFilter özelliğini genellikle bir defaya mahsus DataView nesnesini indexlemenin masrafından kaçınmak için kullanırız. Eğer bir satır veya birden çok satır bulmaya ihtiyaç duyarsanız Find veya FindRows metodunu çalıştırmanızı öneririm.  Her iki metodunda çalışması için önce Sort özelliğini bulmak istediğiniz kriterlere göre tanımlamanız gerekmektedir. FindRows metodu bir DataRowView dizisi döndürecektir. Find metodu Find kriteri ile eşleşen satırın satır indexini döndürecektir. Row indexi kullanarak DataView içindeki satırı görüntüleyebilir veya ilgili DataTable’ı görüntüleyebiliriz.

Bir sonraki bölümde, ADO.NET ile SQL Servera bağlantı oluşturma konusunu ele alıyor olacağız..

Kadir SÜMERKENT
kadirs@yazgelistir.com