9. Veritabanlarına erişim
9.1. ADO.NET bağlayıcısı
Daha önce çeşitli durumlarda kullandığımız katmanlı mimariyi tekrar ele alalım
![]() |
İncelenen örneklerde, [dao] katmanı şu ana kadar iki tür veri kaynağını kullanmıştır:
- kodun içine sabit olarak yerleştirilmiş veriler
- metin dosyalarından gelen veriler
Bu bölümde, verilerin bir veritabanından geldiği durumu inceleyeceğiz. Bu durumda 3 katmanlı mimari, çok katmanlı bir mimariye dönüşür. Bunun çeşitli türleri mevcuttur. Aşağıdaki örnekle temel kavramları inceleyeceğiz:
![]() |
Yukarıdaki şemada, [dao] [1] katmanı,kullanılan ve onunla birlikte sağlanan SGBD'e özgü bir sınıf kütüphanesi aracılığıyla iletişim kurar. Bu katman, ADO (Active X Data Objects) adı altında toplanan standart işlevleri uygular. Bu tür bir katmana sağlayıcı (burada bir veritabanına erişim sağlayıcısı) veya konektör denir. Artık çoğu SGBD, bir ADO.NET konektörüne sahiptir; oysa .NET platformunun ilk zamanlarında durum böyle değildi. .NET konektörleri, [dao] katmanına standart bir arayüz sunmadığından, bu katmanın kodunda konektör sınıflarının adları yer almaktadır. SGBD değiştirilirse, konektör ve sınıflar da değişir; bu durumda [dao] katmanının da değiştirilmesi gerekir. Bu, hem verimli bir mimaridir çünkü .NET konektörü belirli bir SGBD için yazıldığından, onu en iyi şekilde kullanmayı bilir; aynı zamanda katı bir mimaridir çünkü SGBD'i değiştirmek, [dao] katmanını da değiştirmek anlamına gelir. Bu ikinci argüman göreceli olarak değerlendirilmelidir: şirketler SGBD'i çok sık değiştirmezler. Ayrıca, daha sonra göreceğimiz üzere, .NET'in 2.0 sürümünden itibaren, performanstan ödün vermeden esneklik sağlayan genel amaçlı bir konektör mevcuttur.
9.2. Bir veri kaynağının iki kullanım şekli
.NET platformu, bir veri kaynağının iki farklı şekilde kullanılmasına olanak tanır:
- bağlı mod
- çevrimdışı mod
Bağlı modda, uygulama
- veri kaynağıyla bir bağlantı kurar
- veri kaynağıyla okuma/yazma işlemlerini gerçekleştirir
- bağlantıyı kapatır
Çevrimdışı modda, uygulama
- veri kaynağıyla bir bağlantı açar
- kaynağın verilerinin tamamının veya bir kısmının bellek kopyasını alır
- bağlantıyı kapatır
- verilerin bellek kopyası üzerinde okuma/yazma işlemlerini gerçekleştirir
- işlem bittiğinde, bir bağlantı açar, değiştirilen verileri veri kaynağına göndererek bunların işlenmesini sağlar ve bağlantıyı kapatır
Burada yalnızca bağlı modu ele alıyoruz.
9.3. Veritabanı kullanımının temel kavramları
SQL Server Compact 3.5 veritabanını kullanarak veritabanı kullanımının temel kavramlarını açıklayacağız. Bu SGBD, Visual Studio Express ile birlikte gelir. Bu, aynı anda yalnızca bir kullanıcıyı yönetebilen hafif bir SGBD'tir. Ancak, veritabanı programlamaya giriş yapmak için yeterlidir. Daha sonra, diğer SGBD'leri de tanıtacağız.
Kullanılacak mimari şu şekilde olacaktır:
![]() |
Bir [1] konsol uygulaması, bu SGBD QZ'nin Ado.Net konektörü aracılığıyla SqlServer Compact [3,4] türünde bir veritabanını kullanacaktır.XW2HTMLP003401ZQX'in konektörü aracılığıyla SqlServer Compact [3,4] türündeki bir veritabanını kullanacaktır.
9.3.1. : Örnek veritabanı
Veritabanını doğrudan Visual Studio Express'te oluşturacağız. Bunun için yeni bir konsol türü proje oluşturacağız.
![]() |
- [1]: proje
- [2]: "Veritabanı Gezgini" görünümünü açıyoruz
- [3]: Yeni bir bağlantı oluşturuyoruz
![]() |
- [4]: SGBD türünü seçiyoruz
- [5,6]: SGBD seçiliyor SQL Server Compact
- [7]: Veritabanı oluşturulur
- [8]: SQL Server Compact veritabanı, .sdf uzantılı tek bir dosyaya kapsüllenir. Veritabanının oluşturulacağı konumu belirtin; burada C# proje klasörüdür.
- [9]: Yeni veritabanına [dbarticles.sdf] adı verilir
- [10]: Fransızca dil seçiliyor. Bu, sıralama işlemleri üzerinde bir etkiye sahiptir.
- [11,12]: Veritabanı bir şifre ile korunabilir. Burada "dbarticles".
- [13]: Bilgi sayfasını onaylayın. Veritabanı fiziksel olarak oluşturulacaktır:
![]() |
- [14]: az önce oluşturulan veritabanının adı
- [15]: Her seferinde şifreyi yeniden girmek zorunda kalmamak için "Şifremi kaydet" seçeneğini işaretleyin
- [16]: Bağlantıyı kontrol ediyoruz
- [17]: Her şey yolunda
- [18]: Bilgi sayfasını onaylayın
- [19]: Bağlantı, veritabanı gezgininde görünür
- [20]: Şu anda veritabanında tablo yok. Bir tablo oluşturuluyor. Bir makale aşağıdaki alanlara sahip olacak:
- id: benzersiz bir kimlik - birincil anahtar
- nom: ürün adı - benzersiz
- prix: ürün fiyatı
- stockactuel: mevcut stok miktarı
- stockminimum: ürünün yeniden stoklanması gereken minimum stok seviyesi
![]() |
- [21]: [id] alanı tamsayı türündedir ve tablonun [22] birincil anahtarıdır.
- [23]: Bu birincil anahtar, Identity türündedir. SGBD SQL Sunucusuna özgü bu kavram, birincil anahtarın SGBD tarafından kendiliğinden oluşturulacağını belirtir. Burada birincil anahtar, 1'den başlayan ve her yeni anahtar için 1 artan bir tamsayı olacaktır.
![]() |
- [24]: Diğer alanlar oluşturulur. [nom] alanının, [25] tekil olma kısıtlamalı bir 'e sahip olduğu unutulmamalıdır.
- [26]: tabloya bir ad verilir
- [27]: Tablo yapısı onaylandıktan sonra tablo veritabanında görünür.
![]() |
- [28]: Tablonun içeriğini görüntülemek isteniyor
- [29]: Şu anda tablo boş
- [30]: Tabloya birkaç veri girilir. Bir satır, bir sonraki satırı girmeye başladığımız anda onaylanır. [id] alanı doldurulmaz: bu alan, satır onaylandığında otomatik olarak oluşturulur.
Şimdi, şu anda projenin kök dizininde bulunan bu veritabanının, projenin çalışma klasörüne otomatik olarak kopyalanması için projeyi yapılandırmamız gerekiyor:
![]() |
- [1]: tüm dosyaları görüntülemek istiyoruz
- [2]: [dbarticles.sdf] veritabanı görünür
- [3]: projeye eklenir
![]() |
- [4]: Bir projeye veri kaynağı ekleme işlemi, burada ihtiyacımız olmayan bir sihirbazı başlatır [5].
- [6]: Veritabanı artık projenin bir parçasıdır. Normal moda geri dönüyoruz [7].
- [8]: veritabanıyla birlikte proje
- [9]: Veritabanının özelliklerinde, [10]'in projenin çalışma klasörüne otomatik olarak kopyalanacağı görülebilir. Yazacağımız program, veritabanını buradan alacaktır.
Artık kullanabileceğimiz bir veritabanımız olduğuna göre, bundan yararlanabileceğiz. Öncelikle, SQL ile ilgili birkaç hatırlatmada bulunacağız.
9.3.2. SQL dilinin dört temel komutu
SQL (Structured Language Query), veritabanlarını sorgulamak ve güncellemek için kullanılan, kısmen standartlaştırılmış bir dildir. Tüm SGBD dilleri, SQL'in standartlaştırılmış kısmına uymakla birlikte, SGBD'in belirli özelliklerinden yararlanan özel uzantıları dile ekler. Bunun iki örneğiyle daha önce karşılaştık: birincil anahtarların otomatik olarak oluşturulması ve bir tablonun sütunları için izin verilen türler genellikle SGBD'e bağlıdır.
Sunacağımız SQL dilinin dört temel komutu standartlaştırılmıştır ve tüm SGBD'ler tarafından kabul edilmektedir:
Bir veritabanında bulunan verileri elde etmeyi sağlayan sorgu. Yalnızca ilk satırdaki anahtar kelimeler zorunludur, diğerleri isteğe bağlıdır. Burada gösterilmeyen başka anahtar kelimeler de mevcuttur.
| |
Tabloya bir satır ekler. (col1, col2, ...) ifadesi, (val1, val2, ...) değerleriyle doldurulacak satırın sütunlarını belirtir. | |
Koşulu karşılayan tablo satırlarını günceller (where ifadesi yoksa tüm satırları). Bu satırlar için coli sütununa vali değeri atanır | |
Koşulu karşılayan tüm tablo satırlarını siler |
Daha önce oluşturduğumuz [dbarticles] veritabanında SQL komutlarını çalıştırabilen bir konsol uygulaması yazacağız. İşte bir örnek çalıştırma. Okuyucunun, verilen SQL komutlarını ve bunların sonuçlarını anlaması istenir.
- 1. satır: Bağlantı dizesi: Veritabanına bağlanmak için gerekli tüm parametreleri içerir.
- 3. satır: [articles] tablosunun içeriği istenir
- 16. satır: Yeni bir satır eklenir. id alanının bu işlemde başlatılmadığına dikkat edilmelidir; çünkü bu alanın değerini SGBD oluşturacaktır.
- 19. satır: Doğrulama. 28. satırda, satırın başarıyla eklendiği görülmektedir.
- 30. satır: Az önce eklenen ürünün fiyatı %10 artırılır.
- 33. satır: Doğrulama
- 42. satır: Fiyat artışı başarıyla gerçekleştirildi
- 44. satır: Daha önce eklediğimiz ürünü siliyoruz
- 47. satır: kontrol ediliyor
- satır 53-55: Ürün artık yok.
9.3.3. Bağlı mod için ADO.NET'in temel arayüzleri
ADO.NET konektörü aracılığıyla bir veritabanını kullanan bir uygulamanın şemasına geri dönelim:
![]() |
Bağlı modda uygulama:
- veri kaynağıyla bir bağlantı kurar
- veri kaynağıyla okuma/yazma işlemlerini gerçekleştirir
- bağlantıyı kapatır
Bu işlemlerle esas olarak üç ADO.NET arayüzü ilgilidir:
- IDbConnection, bağlantının özelliklerini ve yöntemlerini kapsar.
- IDbCommand, yürütülen SQL komutunun özelliklerini ve yöntemlerini kapsar.
- IDataReader, SQL Select komutunun sonucunun özelliklerini ve yöntemlerini kapsar.
arayüzü IDbConnection
Veritabanıyla olan bağlantıyı yönetmek için kullanılır. Bu arayüzün kullanacağımız M yöntemleri ve P özellikleri şunlardır:
Ad | Tür | Rol |
P | veritabanına bağlantı dizesi. Belirli bir veritabanıyla bağlantı kurulması için gerekli tüm parametreleri belirtir. | |
M | ConnectionString ile tanımlanan veritabanına bağlantıyı açar | |
M | bağlantıyı kapatır | |
M | işlemi başlatır. | |
P | bağlantı durumu: ConnectionState.Closed, ConnectionState.Open, ConnectionState.Connecting, ConnectionState.Executing, ConnectionState.Fetching, ConnectionState.Broken |
Connection, IDbConnection arayüzünü uygulayan bir sınıfsa, bağlantı şu şekilde açılabilir:
arayüzü IDbCommand
Bir SQL komutunu veya saklı yordamı çalıştırmak için kullanılır. Bu arayüzün kullanacağımız M yöntemleri ve P özellikleri şunlardır:
Ad | Tür | Rol |
P | yürütülmesi gerekeni belirtir - değerlerini bir sıralamadan alır: - CommandType.Text: CommandText özelliğinde tanımlanan SQL komutunu yürütür. Bu, varsayılan değerdir. - CommandType.StoredProcedure: veritabanında depolanmış bir yordamı yürütür | |
P | - CommandType = CommandType.Text ise çalıştırılacak SQL komutunun metni - CommandType = CommandType.StoredProcedure ise çalıştırılacak saklı yordamın adı | |
P | SQL emrini yürütmek için kullanılacak IDbConnection bağlantısı | |
P | SQL emrini yürütmek için kullanılacak IDbTransaction işlemi | |
P | parametreleri ayarlanmış bir SQL emrinin parametre listesi. "update articles set price=price*1.1 where id=@id" emrinin @id parametresi vardır. | |
M | SQL Select komutunu çalıştırmak için. Select komutunun sonucunu temsil eden bir IDataReader nesnesi elde edilir. | |
M | SQL Güncelleme, Ekleme, Silme komutunu yürütmek için. İşlemden etkilenen satır sayısı (güncellenen, eklenen, silinen) elde edilir. | |
M | SQL komutunu çalıştırmak için; Select komutu, "select count(*) from articles" örneğinde olduğu gibi tek bir sonuç döndürür. | |
M | SQL emrinin parametrelerini oluşturmak için IDbParameter. | |
M | farklı parametrelerle birden çok kez yürütüldüğünde, parametreli bir sorgunun yürütülmesini optimize etmenizi sağlar. |
Command, IDbCommand arayüzünü uygulayan bir sınıf ise, işlem içermeyen bir SQL emrinin yürütülmesi aşağıdaki gibi olacaktır:
arayüzü IDataReader
SQL Select emrinin sonuçlarını kapsüllemek için kullanılır. Bir IDataReader nesnesi, sırayla işlenen satır ve sütunlardan oluşan bir tabloyu temsil eder: önce 1. satır, sonra 2. satır, ... Bu arayüzün kullanacağımız M yöntemleri ve P özellikleri şunlardır:
Ad | Tür | Rol |
P | IDataReader tablosundaki sütun sayısı | |
M | GetName(i), IDataReader tablosundaki i numaralı sütunun adını döndürür. | |
P | Item[i], IDataReader tablosunun geçerli satırındaki i numaralı sütunu temsil eder. | |
M | IDataReader tablosunun bir sonraki satırına geçer. Okuma işlemi başarılı olursa True boole değerini, aksi takdirde False değerini döndürür. | |
M | IDataReader tablosunu kapatır. | |
M | GetBoolean(i): IDataReader tablosunun geçerli satırındaki i numaralı sütunun boole değerini döndürür. Benzer diğer yöntemler şunlardır: GetDateTime, GetDecimal, GetDouble, GetFloat, GetInt16, GetInt32, GetInt64, GetString. | |
M | Getvalue(i): IDataReader tablosunun geçerli satırındaki i numaralı sütunun değerini object türü olarak döndürür. | |
M | IsDBNull(i), IDataReader tablosunun geçerli satırındaki i numaralı sütunda değer yoksa (bu durum SQL NULL değeriyle gösterilir) True değerini döndürür. |
Bir IDataReader nesnesinin işlenmesi genellikle aşağıdaki gibi olur:
9.3.4. Hata yönetimi
Veritabanlı bir uygulamanın mimarisine tekrar göz atalım:
![]() |
[dao] katmanı, veritabanının kullanımı sırasında birçok hatayla karşılaşabilir. Bu hatalar, ADO.NET konektörü tarafından tetiklenen istisnalar olarak bildirilecektir. [dao] katmanındaki kod, bu istisnaları yönetmelidir. Veritabanıyla yapılan her işlem, olası bir istisnayı yakalamak, yönetmek ve gerekli kaynakları serbest bırakmak için bir try / catch / finally bloğu içinde gerçekleştirilmelidir. Böylece, yukarıda görülen Select komutunun sonucunu işlemek için kullanılan kod şu şekilde olur:
Ne olursa olsun, IDataReader ve IDbConnection nesneleri kapatılmalıdır. Bu nedenle, bu kapatma işlemi finally blokları içinde gerçekleştirilir.
Bağlantının kapatılması ve IDataReader nesnesinin kapatılması, bir using cümlesi ile otomatikleştirilebilir:
- 3. satırdaki using cümlesi, using(...){...} bloğu içinde açılan bağlantının, bloktan nasıl çıkılırsa çıksın (normal şekilde veya bir istisna oluşmasıyla) blok dışında kapatılacağını garanti eder. Bir finally tasarrufu sağlanır, ancak asıl fayda bu küçük tasarrufta değildir. using kullanımı, geliştiricinin bağlantıyı kendisi kapatma zahmetinden kurtarır. Oysa bir bağlantıyı kapatmayı unutmak fark edilmeyebilir ve SGBD, destekleyebileceği maksimum açık bağlantı sayısına her ulaştığında, uygulama rastgele bir şekilde "çökebilir".
- 11. satır: IDataReader nesnesini kapatmak için de benzer şekilde işlem yapılır.
9.3.5. Örnek projenin yapılandırılması
Nihai proje şu şekilde olacaktır:
![]() |
- [1]: Proje, bir yapılandırma dosyasına sahip olacaktır [App.config]
- [2]: Varsayılan olarak referanslanmamış iki DLL sınıfını kullanır; bu nedenle bunların proje referanslarına eklenmesi gerekir:
- [System.Configuration], [App.config] yapılandırma dosyasını kullanmak için
- [System.Data.SqlServerCe]: Sql Server Compact veritabanını kullanmak için
- [3, 4]: Bir projeye referansların nasıl ekleneceğini gösterir.
- [5, 6]: [App.config] dosyasını bir projeye nasıl ekleyeceğinizi hatırlatır.
[App.config] yapılandırma dosyası şu şekilde olacaktır:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<connectionStrings>
<add name="dbSqlServerCe" connectionString="Data Source=|DataDirectory|\dbarticles.sdf;Password=dbarticles;" />
</connectionStrings>
</configuration>
- 3-5. satırlar: Çoğul haldeki <connectionStrings> etiketi, veritabanlarına bağlantı dizelerini tanımlar. Bir bağlantı dizesi "parametre1=değer1;parametre2=değer2;..." biçimindedir. Bu, belirli bir veritabanıyla bağlantı kurmak için gerekli tüm parametreleri tanımlar. Bu bağlantı dizeleri her SGBD ile değişir. [http://www.connectionstrings.com/] sitesinde, başlıca SGBD'ler için bu dizelerin biçimi verilmiştir.
- 4. satır: Belirli bir bağlantı dizesini tanımlar; burada, daha önce oluşturduğumuz SQL Server Compact dbarticles.sdf veritabanına ait olanı:
- name = bağlantı dizesinin adı. C# programı, bu ad aracılığıyla bağlantı dizesini alır
- connectionString: SQL Server Compact veritabanı için bağlantı dizesi
- DataSource: veritabanının yolunu belirtir. |DataDirectory| sözdizimi, projenin çalıştırma klasörünü belirtir.
- Password: Veritabanının şifresi. Şifre yoksa bu parametre yoktur.
Önceki bağlantı dizesini almak için C# kodu şöyledir:
string connectionString = ConfigurationManager.ConnectionStrings["dbSqlServerCe"].ConnectionString;
- ConfigurationManager, DLL ve [System.Configuration] sınıflarının birleşimidir ve [App.config] dosyasını kullanmaya olanak tanır.
- ConnectionsStrings["nom"].ConnectionString: connectionString etiketinin < add name="ad" connectionString="..."> etiketinin <connectionStrings> bölümündeki [App.config]
Proje artık yapılandırılmıştır. Şimdi, daha önce bir çalışma örneğini gördüğümüz [Program.cs] sınıfını inceleyeceğiz.
9.3.6. Örnek program
[program.cs] programı şöyledir:
using System;
using System.Collections.Generic;
using System.Data.SqlServerCe;
using System.Text;
using System.Text.RegularExpressions;
using System.Configuration;
namespace Chap7 {
class SqlCommands {
static void Main(string[] args) {
// konsol uygulaması - klavyeden girilen SQL sorgularını yürütür
// bağlantı dizesi bir yapılandırma dosyasından alınan bir veritabanında
// yapılandırma dosyasının işlenmesi [App.config]
string connectionString = null;
try {
connectionString = ConfigurationManager.ConnectionStrings["dbSqlServerCe"].ConnectionString;
} catch (Exception e) {
Console.WriteLine("Erreur de configuration : {0}", e.Message);
return;
}
// bağlantı dizesinin görüntülenmesi
Console.WriteLine("Chaîne de connexion à la base : [{0}]\n", connectionString);
// kabul edilen SQL komutlarının bir sözlüğü oluşturulur
string[] commandesSQL = new string[] { "select", "insert", "update", "delete" };
Dictionary<string, bool> dicoCommandes = new Dictionary<string, bool>();
for (int i = 0; i < commandesSQL.Length; i++) {
dicoCommandes.Add(commandesSQL[i], true);
}
// klavyeden girilen komutların okunması ve yürütülmesi SQL
string requête = null; // sorgu metni SQL
string[] champs; // sorgudaki alanlar
Regex modèle = new Regex(@"\s+"); // boşluk dizisi
// klavyeden girilen SQL komutlarının giriş-çalıştırma döngüsü
while (true) {
// sorgu talebi
Console.Write("\nRequête SQL (rien pour arrêter) : ");
requête = Console.ReadLine().Trim().ToLower();
// bitti mi?
if (requête == "")
break;
// isteği alanlara ayrıştırma
champs = modèle.Split(requête);
// istek geçerli mi?
if (champs.Length == 0 || ! dicoCommandes.ContainsKey(champs[0])) {
// hata mesajı
Console.WriteLine("Requête invalide. Utilisez select, insert, update, delete ou rien pour arrêter");
// sonraki istek
continue;
}
// isteğin yürütülmesi
if (champs[0] == "select") {
ExecuteSelect(connectionString, requête);
} else
ExecuteUpdate(connectionString, requête);
}
}
// güncelleme isteğinin yürütülmesi
static void ExecuteUpdate(string connectionString, string requête) {
...
}
// Select sorgusunun yürütülmesi
static void ExecuteSelect(string connectionString, string requête) {
....
}
}
}
- 1-6. satırlar: Uygulamada kullanılan ad alanları. Bir SQL Server Compact veritabanının yönetimi, 3. satırdaki [System.Data.SqlServerCe] ad alanını gerektirir. Burada, SGBD'e ait özel bir ad alanına bağımlılık söz konusudur. Bundan, SGBD değiştirilirse programın da değiştirilmesi gerektiği sonucuna varılabilir.
- 18. satır: Veritabanına bağlantı dizesi [App.config] dosyasından okunur ve 25. satırda görüntülenir. Bu dize, veritabanıyla bağlantı kurmak için kullanılacaktır.
- 28-32. satırlar: İzin verilen dört SQL komutunun adlarını (select, insert, update, delete) saklayan bir sözlük.
- 40-62. satırlar: Klavyeden girilen SQL komutlarının alınması ve veritabanında yürütülmesi döngüsü
- 48. satır: Klavyeden girilen satır, ilk teriminin hangisi olduğunu belirlemek için alanlara ayrılır; bu terim şunlardan biri olmalıdır: select, insert, update, delete
- 50-55. satırlar: Sorgu geçersizse, bir hata mesajı görüntülenir ve bir sonraki sorguya geçilir.
- 57-61. satırlar: Girilen SQL komutu yürütülür. Bu yürütme, select komutu mu, yoksa insert, update, delete komutlarından biri mi olduğuna göre farklı bir şekilde gerçekleşir. İlk durumda, komut veritabanından verileri getirir ancak veritabanında herhangi bir değişiklik yapmaz; ikinci durumda ise veritabanını günceller ancak verileri getirmez. Her iki durumda da, yürütme işlemi iki parametre gerektiren bir yönteme devredilir:
- veritabanına bağlanmasını sağlayacak bağlantı dizesi
- bu bağlantı üzerinden çalıştırılacak SQL komutu
9.3.7. SELECT sorgusunun yürütülmesi
SQL emirlerinin yürütülmesi için aşağıdaki adımlar gereklidir:
- Veritabanına bağlanma
- SQL komutlarının veritabanına gönderilmesi
- SQL komutunun sonuçlarının işlenmesi
- Bağlantının kapatılması
- ve 3. adımlar tekrar tekrar gerçekleştirilir; bağlantı kapatma işlemi ise veritabanı işleme süreci tamamlandığında gerçekleşir. Açık bağlantılar, SGBD için sınırlı kaynaklardır. Bunlar tasarruflu kullanılmalıdır. Bu nedenle, açık bir bağlantının ömrü her zaman sınırlandırılmaya çalışılır. İncelenen örnekte, bağlantı her SQL komutundan sonra kapatılır. Bir sonraki SQL komutu için yeni bir bağlantı açılır. Bir bağlantının açılması/kapatılması maliyetlidir. Bu maliyeti azaltmak için, bazı SGBD sistemleri açık bağlantı havuzları kavramını sunar: uygulamanın başlatılması sırasında N adet bağlantı açılır ve havuza atanır. Bu bağlantılar, uygulama sonuna kadar açık kalır. Uygulama bir bağlantı açtığında, havuzda halihazırda açık olan N bağlantıdan birini alır. Bağlantıyı kapattığında ise bu bağlantı basitçe havuza geri eklenir. Bu sistemin avantajı, geliştirici için şeffaf olmasıdır: bağlantı havuzunu kullanmak için programda herhangi bir değişiklik yapılması gerekmez. Bağlantı havuzunun yapılandırması, SGBD'e bağlıdır.
Öncelikle SQL ve Select komutlarının yürütülmesine odaklanacağız. Örnek programımızdaki ExecuteSelect yöntemi şu şekildedir:
// Select sorgusunun yürütülmesi
static void ExecuteSelect(string connectionString, string requête) {
// olası istisnalar yönetiliyor
try {
using (SqlCeConnection connexion = new SqlCeConnection(connectionString)) {
// bağlantı açılıyor
connexion.Open();
// sqlCommand'i Select sorgusuyla çalıştırma
SqlCeCommand sqlCommand = new SqlCeCommand(requête, connexion);
SqlCeDataReader reader= sqlCommand.ExecuteReader();
// sonuçların görüntülenmesi
AfficheReader(reader);
}
} catch (Exception ex) {
// hata mesajı
Console.WriteLine("Erreur d'accès à la base de données (" + ex.Message + ")");
}
}
// okuyucu görüntüleme
static void AfficheReader(IDataReader reader) {
...
}
- 2. satır: Yöntem iki parametre alır:
- veritabanına bağlanmasını sağlayacak [connectionString] bağlantı dizesi
- bu bağlantı üzerinden yürütülecek SQL Select [requête] komutları
- 4. satır: Veritabanıyla yapılan her işlem, yönetilmesi gerekebilecek bir istisna oluşturabilir. Kullanıcı tarafından verilen SQL komutlarının sözdizimsel olarak hatalı olabileceği düşünülürse, bu durum burada daha da önemlidir. Kullanıcıya bu durumu bildirebilmemiz gerekir. Bu nedenle tüm kod bir try / catch bloğu içinde yer almaktadır.
- 5. satır: Burada birkaç husus vardır:
- veritabanı bağlantısı, [connectionString] bağlantı dizesiyle başlatılır. Henüz açılmamıştır. 7. satırda açılacaktır.
- using (Kaynak) {...} cümlesi, Ressource kaynağının (burada bir bağlantı) using tarafından kontrol edilen bloğun dışına çıkıldığında serbest bırakılmasını garanti eden bir sözdizimsel kolaylıktır.
- Bağlantı, SqlCeConnection türünde bir özel bağlantıdır; bu, SGBD SQL Server Compact'a özgüdür.
- 7. satır: Bağlantı açılmıştır. Bu anda bağlantı dizisinin parametreleri kullanılır.
- 9. satır: Özel bir SqlCeCommand nesnesi aracılığıyla bir SQL komutu gönderilir. 9. satır, bu nesneyi iki bilgiyle başlatır: kullanılacak bağlantı ve bu bağlantı üzerinden gönderilecek SQL komutu. SqlCeCommand nesnesi, Select emrini olduğu kadar Update, Insert ve Delete emirlerini de yürütmek için kullanılır. Nesnenin özellikleri ve yöntemleri 9.3.3. paragrafında açıklanmıştır.
- 10. satır: SQL ve Select komutları,SqlCeCommand nesnesinin ExecuteReader yöntemi aracılığıyla yürütülür; bu yöntem, yöntemleri ve özellikleri 9.3.3. paragrafında açıklanan bir IDataReader nesnesini döndürür.
- 12. satır: Sonuçların görüntülenmesi, aşağıdaki AfficheReader yöntemine devredilir:
// okuyucu görüntüleme
static void AfficheReader(IDataReader reader) {
using (reader) {
// sonuçların değerlendirilmesi
// -- sütunlar
StringBuilder ligne = new StringBuilder();
int i;
for (i = 0; i < reader.FieldCount - 1; i++) {
ligne.Append(reader.GetName(i)).Append(",");
}
ligne.Append(reader.GetName(i));
Console.WriteLine("\n{0}\n{1}\n{2}\n", "".PadLeft(ligne.Length, '-'), ligne, "".PadLeft(ligne.Length, '-'));
// -- veriler
while (reader.Read()) {
// geçerli satırın işlenmesi
ligne = new StringBuilder();
for (i = 0; i < reader.FieldCount; i++) {
ligne.Append(reader[i].ToString()).Append(" ");
}
Console.WriteLine(ligne);
}
}
}
- 2. satır: Yöntem, bir IDataReader nesnesi alır. Burada belirli bir sınıf değil, bir arayüz kullandığımızı belirtmek gerekir.
- 3. satır: using ifadesi, IDataReader nesnesinin otomatik olarak kapatılmasını yönetmek için kullanılır.
- 8-10. satırlar: Select sorgusunun sonuç tablosundaki sütun adları görüntülenir. Bunlar, “select col1, col2, ... from table ...” sorgusundaki coli sütunlarıdır.
- 14-21. satırlar: Sonuç tablosu taranır ve tablonun her satırındaki değerler görüntülenir.
- 18. satır: Sorgulanan tablo bilinmediği için sonuçtaki i numaralı sütunun türü bilinmemektedir. Dolayısıyla, reader.GetXXX(i) sözdizimi kullanılamaz; burada XXX, i numaralı sütunun türüdür, ancak bu tür bilinmemektedir. Bu durumda, i numaralı sütunun karakter dizisi biçiminde temsilini elde etmek için reader.Item[i].ToString() sözdizimi kullanılır. reader.Item[i].ToString() sözdizimi, reader[i].ToString() şeklinde kısaltılabilir.
9.3.8. Güncelleme emrinin yürütülmesi: INSERT, UPDATE, DELETE
ExecuteUpdate yönteminin kodu şöyledir:
// güncelleme sorgusunun yürütülmesi
static void ExecuteUpdate(string connectionString, string requête) {
// olası istisnaların yönetimi
try {
using (SqlCeConnection connexion = new SqlCeConnection(connectionString)) {
// bağlantı açılıyor
connexion.Open();
// sqlCommand'i güncelleme isteği ile yürütme
SqlCeCommand sqlCommand = new SqlCeCommand(requête, connexion);
int nbLignes = sqlCommand.ExecuteNonQuery();
// sonucun görüntülenmesi
Console.WriteLine("Il y a eu {0} ligne(s) modifiée(s)", nbLignes);
}
} catch (Exception ex) {
// hata mesajı
Console.WriteLine("Erreur d'accès à la base de données (" + ex.Message + ")");
}
}
Daha önce, Select sorgu emrinin yürütülmesinin, Update güncelleme emrinin yürütülmesinden farklı olmadığını, Insert, Delete komutlarının yürütülmesinden tek farkın, kullanılan SqlCeCommand nesnesinin yöntemi olduğunu belirtmiştik: ExecuteReader için Select, ExecuteNonQuery, Update, Insert ve Delete için. Yukarıdaki kodda sadece bu son yöntemi ele alacağız:
- 10. satır: Update, Insert, Delete komutu, SqlCeCommand nesnesinin ExecuteNonQuery yöntemi tarafından yürütülür. Başarılı olursa, bu yöntem güncellenen (update), eklenen (insert) veya silinen (delete) satır sayısını döndürür.
- 12. satır: Bu satır sayısı ekranda görüntülenir
Okuyucunun, bu kodun bir yürütme örneğini 9.3.2. paragrafında incelemesi önerilir.
9.4. Diğer ADO.NET bağlayıcıları
İncelediğimiz kod tescillidir: [System.Data.SqlServerCe] ad alanına bağlıdır ve SGBD SQL Server Compact için tasarlanmıştır. Şimdi aynı programı farklı .NET bağlayıcılarıyla oluşturup ne gibi değişiklikler olduğunu göreceğiz.
9.4.1. SQL Server 2005 Bağlayıcısı
Kullanılacak mimari şu şekilde olacaktır:
![]() |
SQL Server 2005'in kurulumu, eklerdeki 1.1. paragrafında açıklanmaktadır.
Daha önce olduğu gibi aynı çözüm içinde ikinci bir proje oluşturuyoruz, ardından SQL Server 2005 veritabanını oluşturuyoruz. Aşağıdaki işlemlerden önce SGBD ve SQL Server 2005'in başlatılması gerekir:
![]() |
- [1]: Mevcut çözümde yeni bir proje oluşturun ve bunu geçerli proje yapın.
- [2]: Yeni bir bağlantı oluşturun
- [3]: bağlantı türünü seçin
![]() |
- [4]: SGBD SQL Sunucusunu seçin
- [5]: önceki seçimin sonucu
- [6]: [Browse] düğmesini kullanarak SQL Server 2005 veritabanının oluşturulacağı konumu belirtin. Veritabanı bir .mdf dosyası içinde bulunur.
- [7]: Yeni projenin kök dizinini seçin ve veritabanına [dbarticles.mdf] adını verin.
- [8]: Windows kimlik doğrulamasını kullanın.
- [9]: Bilgi sayfasını onaylayın
![]() |
- [11]: SQL Server veritabanı
- [12]: Bir tablo oluşturun. Bu tablo, daha önce oluşturulan SQL Server Compact veritabanıyla aynı olacaktır.
- [13]: [id] alanı
- [14]: [id] alanı, Identity türündedir.
- [15,16]: [id] alanı birincil anahtardır
![]() |
- [17]: Tablonun diğer alanları
- [18]: Tabloyu kaydederken (Ctrl+S) ona [articles] adını verin.
Şimdi tabloya veri girmemiz gerekiyor:
![]() | ![]() |
Veritabanını projeye ekliyoruz:
![]() |
Projenin referansları şunlardır:
![]() |
[App.config] yapılandırma dosyası şu şekildedir:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<connectionStrings>
<add name="connectString1" connectionString="Data Source=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|\dbarticles.mdf;Integrated Security=True;Connect Timeout=30;User Instance=True;" />
<add name="connectString2" connectionString="Data Source=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|\dbarticles.mdf;Uid=sa;Pwd=msde;Connect Timeout=30;" />
</connectionStrings>
</configuration>
- 4. satır: Windows kimlik doğrulamalı [dbarticles.mdf] veritabanına bağlantı dizesi
- 5. satır: [dbarticles.mdf] veritabanına, SQL Sunucusu kimlik doğrulaması ile bağlantı dizesi. [sa,msde], 1.1. paragrafında tanımlandığı üzere SQL Server sunucusunun yöneticisinin kullanıcı adı ve şifre çiftidir.
[Program.cs] programı şu şekilde çalışır:
using System.Data.SqlClient;
...
namespace Chap7 {
class SqlCommands {
static void Main(string[] args) {
...
// [App.config] yapılandırma dosyasının işlenmesi
string connectionString = null;
try {
connectionString = ConfigurationManager.ConnectionStrings["connectString2"].ConnectionString;
} catch (Exception e) {
...
}
...
// klavyeden girilen komutların okunması ve yürütülmesi SQL
...
}
// güncelleme isteğinin yürütülmesi
static void ExecuteUpdate(string connectionString, string requête) {
// olası istisnaların yönetimi
try {
using (SqlConnection connexion = new SqlConnection(connectionString)) {
// bağlantı açma
connexion.Open();
// sqlCommand komutunu güncelleme isteği ile yürütme
SqlCommand sqlCommand = new SqlCommand(requête, connexion);
int nbLignes = sqlCommand.ExecuteNonQuery();
// sonuç görüntüleme
Console.WriteLine("Il y a eu {0} ligne(s) modifiée(s)", nbLignes);
}
} catch (Exception ex) {
....
}
}
// Select sorgusunun yürütülmesi
static void ExecuteSelect(string connectionString, string requête) {
// olası istisnalar yönetiliyor
try {
using (SqlConnection connexion = new SqlConnection(connectionString)) {
// bağlantı açma
connexion.Open();
// sqlCommand'i Select sorgusuyla çalıştırma
SqlCommand sqlCommand = new SqlCommand(requête, connexion);
SqlDataReader reader = sqlCommand.ExecuteReader();
// sonuçların işlenmesi
...
}
} catch (Exception ex) {
...
}
}
}
}
- 1. satır: [System.Data.SqlClient] ad alanı, bir SQL Server 2005 veritabanını yönetmeye olanak tanıyan sınıfları içerir
- 24. satır: Bağlantı türü SQLConnection'tir
- 28. satır: SQL komutlarını kapsayan nesne, SQLCommand türündedir
- 47. satır: SQL Select komutunun sonucunu kapsayan nesne, SQLDataReader türündedir
Kod, sınıf isimleri hariç olmak üzere SGBD ve SQL Server Compact ile kullanılan kodla aynıdır. Bunu çalıştırmak için (11. satır), [App.config]'te tanımlanan iki bağlantı dizesinden herhangi biri kullanılabilir.
9.4.2. MySQL5 Bağlayıcı
Kullanılacak mimari şu şekilde olacaktır:
![]() |
MySQL5'in kurulumu eklerin 1.2 numaralı paragrafında, Ado.Net konektörünün kurulumu ise 1.2.5 numaralı paragrafında açıklanmaktadır.
Daha önce olduğu gibi aynı çözüm içinde üçüncü bir proje oluşturuyoruz ve bu projeye gerekli referansları ekliyoruz:
![]() |
- [1]: yeni proje
- [2]: referansların eklendiği proje
- [3]: DLL, [MySQL.Data], Ado.Net, MySql5 konektörlerinin yanı sıra [System.Configuration] ve [4]'in konektörleri.
Şimdi [dbarticles] veritabanını ve [articles] tablosunu oluşturuyoruz. SGBD ve MySQL5 çalıştırılmalıdır. Ayrıca, [Query Browser] istemcisi de çalıştırılır (bkz. paragraf 1.2.3).
![]() |
- [1]: [Query Browser]'te, [Schemata] [2] alanına sağ tıklayarak [3] adlı yeni bir şema (veritabanı anlamına gelen terim) oluşturulmalıdır.
- [4]: Veritabanının adı [dbarticles] olacaktır. [5]'te bu veritabanını görebiliriz. Şu anda içinde herhangi bir tablo bulunmamaktadır. Aşağıdaki SQL komut dosyasını çalıştıracağız:
- 1. satır: [dbarticles] veritabanı geçerli veritabanı haline gelir. Ardından gelen SQL komutları bu veritabanı üzerinde çalıştırılacaktır.
- 4-10. satırlar: [ARTICLES] tablosunun tanımı. MySQL tablosunun sahibi olan SQL'e dikkat edilmelidir. Sütun türleri ve birincil anahtarın otomatik olarak oluşturulması (AUTO_INCREMENT özniteliği), SGBD ve SQL Server Compact ve Express'te görülenlerden farklıdır.
- 12-14. satırlar: üç satır eklenmiştir
- 16-21. satırlar: sütunlara bütünlük kısıtlamaları eklenmiştir.
Bu komut dosyası [MySQL Query Browser]'te çalıştırılır:
![]() |
- [MySQL Query Browser] ve [6]'te, [7] komut dosyası yüklenir. Bu, [8]'te görülebilir. [9]'te ise komut dosyası çalıştırılır.
![]() |
- [10]'te, [articles] tablosu oluşturuldu. Üzerine çift tıklıyoruz. Bu işlem, içinde [12] sorgusunun bulunduğu ve [13] tarafından çalıştırılmaya hazır olan [11] penceresini açar. [14]'te, yürütme sonucu görülüyor. Beklenen üç satır da mevcut. [ID] alanındaki değerlerin otomatik olarak oluşturulduğuna dikkat edin (alanın AUTO_INCREMENT özniteliği).
Artık veritabanı hazır olduğuna göre, Visual Studio’da uygulama geliştirmeye geri dönebiliriz.
![]() |
[1] içinde, [Program.cs] programı ve [App.config] yapılandırma dosyası bulunmaktadır. Yapılandırma dosyası şöyledir:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<connectionStrings>
<add name="dbArticlesMySql5" connectionString="Server=localhost;Database=dbarticles;Uid=root;Pwd=root;" />
</connectionStrings>
</configuration>
- satırda, bağlantı dizesinin öğeleri şunlardır:
- Sunucu: SGBD, MySQL ve burada localhost, c.a.d dosyalarının bulunduğu makinenin adı. Programın çalıştırılacağı makine.
- Database: Yönetilen veritabanının adı; burada dbarticles
- Uid: Kullanıcının kullanıcı adı, burada root
- Pwd: Şifresi; burada root. Bu iki bilgi, 1.2. paragrafta oluşturulan yöneticiyi belirtir.
[Program.cs] programı, aşağıdaki ayrıntılar dışında önceki sürümlerle aynıdır:
MySql.Data.MySqlClient | |
MySqlConnection | |
MySqlCommand | |
MySqlDataReader |
Program, [App.config] dosyasındaki dbArticlesMySql5 adlı bağlantı dizesini kullanır. Çalıştırıldığında şu sonuçlar elde edilir:
9.4.3. ODBC bağlayıcısı
Kullanılacak mimari şu şekilde olacaktır:
![]() |
ODBC konektörlerinin avantajı, bunları kullanan uygulamalara standart bir arayüz sunmalarıdır. Böylece yeni uygulama, tek bir kodla, ODBC, c.a.d konektörlerine sahip tüm SGBD'lerle ve çoğu SGBD'lerle iletişim kurabilecektir. ODBC konektörlerinin performansı, belirli bir SGBD'in tüm özelliklerinden yararlanabilen "özel" konektörlerin performansından daha düşüktür. Bunun karşılığında, uygulama açısından büyük bir esneklik elde edilir: kodu değiştirmeden SGBD'i değiştirmek mümkündür.
Uygulamanın, kendisine verilen bağlantı dizesine göre bir MySQL5 veritabanını veya bir SQL Server Express veritabanını kullandığı bir örnek üzerinde duracağız. Aşağıda, şu varsayımları kabul ediyoruz:
- SGBD, SQL Server Express ve MySQL5 başlatılmış
- MySQL5 için ODBC sürücüsünün makinede mevcut olduğunu varsayıyoruz (bkz. paragraf 1.2.6). SQL Server 2005 için sürücü varsayılan olarak mevcuttur.
- Kullanılan veritabanları, MySQL5 veritabanı için 9.4.2 numaralı paragrafta belirtilenler, SQL Server Express veritabanı için ise 9.4.1 numaralı paragrafta belirtilenlerdir.
Yeni Visual Studio projesi şöyledir:
![]() |
Yukarıda, 9.4.1 numaralı paragrafta oluşturulan SQL Server [dbarticles.mdf] veritabanı, proje klasörüne kopyalanmıştır.
[App.config] yapılandırma dosyası şu şekildedir:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<connectionStrings>
<add name="dbArticlesOdbcMySql5" connectionString="Driver={MySQL ODBC 3.51 Driver};Server=localhost;Database=dbarticles; User=root;Password=root;" />
<add name="dbArticlesOdbcSqlServer2005" connectionString="Driver={SQL Native Client};Server=.\SQLExpress;AttachDbFilename=|DataDirectory|\dbarticles.mdf;Uid=sa;Pwd=msde;" />
</connectionStrings>
</configuration>
- 4. satır: ODBC MySQL5 kaynağının bağlantı dizesi. Bu, daha önce incelenmiş bir dizedir ve içinde kullanılacak ODBC sürücüsünü tanımlayan yeni bir Driver parametresi bulunur.
- 5. satır: ODBC SQL Server Express kaynağının bağlantı dizesi. Bu, önceki bir örnekte zaten kullanılmış olan ve Driver parametresinin eklendiği dizedir.
[Program.cs] programı, aşağıdaki ayrıntılar dışında önceki sürümlerle aynıdır:
System.Data.Odbc | |
OdbcConnection | |
OdbcCommand | |
OdbcDataReader |
Program, [App.config] dosyasında tanımlanan iki bağlantı dizesinden birini kullanır. Çalıştırıldığında şu sonuçlar elde edilir:
[dbArticlesOdbcSqlServer2005] bağlantı dizesiyle:
[dbArticlesOdbcMySql5] bağlantı dizesiyle:
9.4.4. Bağlayıcı OLE DB
Kullanılacak mimari şu şekilde olacaktır:
![]() |
ODBC konektörleri gibi, OLE ve DB (Object Linking and Embedding DataBase) konektörleri de bunları kullanan uygulamalara standart bir arayüz sunar. ODBC sürücüleri, veritabanlarına erişim sağlar. OLE ve DB sürücüleri için veri kaynakları daha çeşitlidir: veritabanları, e-posta sistemleri, adres defterleri vb. Bir yazılım geliştiricisi karar verirse, herhangi bir veri kaynağı bir Ole DB sürücüsünün konusu olabilir. Böylece çok çeşitli verilere standart bir erişim sağlanır.
Uygulamanın, kendisine verilen bağlantı dizisine göre bir ACCESS veritabanını veya bir SQL Server Express veritabanını kullandığı bir örneği inceleyeceğiz. Aşağıda, SGBD SQL Server Express'in başlatıldığını ve kullanılan veritabanının önceki örnekteki veritabanı olduğunu varsayıyoruz.
Yeni Visual Studio projesi şu şekildedir:
![]() |
- [1]'te: OLE ve DB konektörleri için gerekli ad alanı, yukarıdaki [System.Data] referansında bulunan [System.Data.OleDb]'tir. SQL Server veritabanı, önceki projeden kopyalanmıştır. [dbarticles.mdb] veritabanı Access ile oluşturulmuştur.
- [2]'te: SQL Sunucu veritabanı gibi, ACCESS veritabanı da projenin çalıştırma klasörüne otomatik olarak kopyalanması için [Copy to Output Directory=Copy Always] özelliğine sahiptir.
ACCESS [dbarticles.mdb] veritabanı şu şekildedir:
![]() |
[1] dosyasında [articles] tablosunun yapısı, [2] dosyasında ise içeriği yer almaktadır.
[App.config] yapılandırma dosyası şu şekildedir:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<connectionStrings>
<add name="dbArticlesOleDbAccess" connectionString="Provider=Microsoft.Jet.OLEDB.4.0;Data Source=|DataDirectory|\dbarticles.mdb;"/>
<add name="dbArticlesOleDbSqlServer2005" connectionString="Provider=SQLNCLI;Server=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|\dbarticles.mdf;Uid=sa;Pwd=msde;" />
</connectionStrings>
</configuration>
- 4. satır: OLE, DB ve ACCESS kaynaklarının bağlantı dizesi. Burada, kullanılacak OLE DB sürücüsünü tanımlayan Provider parametresi ile veritabanının yolu yer almaktadır
- 5. satır: OLE DB Server Express kaynağının bağlantı dizesi.
[Program.cs] programı, aşağıdaki ayrıntılar dışında önceki sürümlerle aynıdır:
System.Data.OleDb | |
OleDbConnection | |
OleDbCommand | |
OleDbDataReader |
Program, [App.config] dosyasında tanımlanan iki bağlantı dizesinden birini kullanır. [dbArticlesOleDbAccess] bağlantı dizesiyle çalıştırıldığında şu sonuçlar elde edilir:
9.4.5. Genel bağlayıcı
Kullanılacak mimari şu şekilde olacaktır:
![]() |
ODBC, OLE ve DB konektörleri gibi, genel amaçlı konektör de onu kullanan uygulamalara standart bir arayüz sunar, ancak esnekliği feda etmeden performansı artırır. Aslında, genel konektör, SGBD'in özel konektörlerine dayanmaktadır. Uygulama, genel konektörün sınıflarını kullanır. Bu sınıflar, uygulama ile özel konektör arasında aracı görevi görür.
Yukarıda, örneğin uygulama genel konektöre bir bağlantı talep ettiğinde, genel konektör ona bir IDbConnection örneği döndürür; 9.3.3 numaralı paragrafta açıklanan bağlantı arayüzü, yapılan talebin niteliğine göre MySQLConnection veya SQLConnection sınıfı tarafından uygulanır. Genel konektörün fabrika tipi sınıfları olduğu söylenir: bir fabrika sınıfı kullanılarak konektörden nesneler oluşturması ve bunlara ait referanslar (işaretçiler) vermesi istenir. Adı da buradan gelir (factory = fabrika, nesne üretim fabrikası).
Tüm SGBD kodları için tek bir genel konektör bulunmamaktadır (Nisan 2008). Bir makinede kurulu olanları öğrenmek için aşağıdaki programı kullanabilirsiniz:
using System;
using System.Data;
using System.Data.Common;
namespace Chap7 {
class Providers {
public static void Main() {
DataTable dt = DbProviderFactories.GetFactoryClasses();
foreach (DataColumn col in dt.Columns) {
Console.Write("{0}|", col.ColumnName);
}
Console.WriteLine("\n".PadRight(40, '-'));
foreach (DataRow row in dt.Rows) {
foreach (object item in row.ItemArray) {
Console.Write("{0}|", item);
}
Console.WriteLine("\n".PadRight(40, '-'));
}
}
}
}
- 8. satır: [DbProviderFactories.GetFactoryClasses()] statik yöntemi, kurulu genel konektörlerin listesini, bellekte yer alan bir veritabanı tablosu (DataTable) biçiminde döndürür.
- 9-11. satırlar: dt tablosunun sütun adlarını gösterir:
- dt.Columns, tablonun sütun listesidir. C sütunu, DataColumn türündedir
- [DataColumn] türündedir. ColumnName, sütunun adıdır
- 13-18. satırlar: dt tablosunun satırlarını gösterir:
- dt.Rows, tablonun satırlarının listesidir. L tipi bir satır, DataRow
- [DataRow]. ItemArray, her bir nesnenin satırın bir sütununu temsil ettiği bir nesne dizisidir
Benim bilgisayarımda çalıştırıldığında elde edilen sonuç şöyledir:
- 1. satır: Tabloda dört sütun bulunmaktadır. İlk üç sütun burada bizim için en yararlı olanlardır.
Aşağıdaki ekran görüntüsü, şu genel bağlayıcıların mevcut olduğunu göstermektedir:
Ad | Kimlik |
System.Data.Odbc | |
System.Data.OleDb | |
System.Data.OracleClient | |
System.Data.SqlClient | |
System.Data.SqlServerCe.3.5 | |
MySql.Data.MySqlClient |
C# programında bir genel konektöre, tanımlayıcısı aracılığıyla erişilebilir.
Şimdiye kadar oluşturduğumuz çeşitli veritabanlarını kullanan bir uygulama örneğini inceleyeceğiz. Uygulama iki parametre alacaktır:
- birinci parametre, doğru sınıf kütüphanesinin kullanılması için kullanılan SGBD türünü belirtir
- ikinci parametre ise bir bağlantı dizesi aracılığıyla yönetilen veritabanını belirtir.
Yeni Visual Studio projesi şu şekildedir:
![]() |
- [1]'te: genel konektörler için gerekli ad alanı, [System.Data] referansında bulunan [System.Data.common]'tir.
[App.config] yapılandırma dosyası şu şekildedir:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<connectionStrings>
<add name="dbArticlesSqlServerCe" connectionString="Data Source=|DataDirectory|\dbarticles.sdf;Password=dbarticles;" />
<add name="dbArticlesSqlServer" connectionString="Data Source=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|\dbarticles.mdf;Uid=sa;Pwd=msde;" />
<add name="dbArticlesMySql5" connectionString="Server=localhost;Database=dbarticles;Uid=root;Pwd=root;" />
<add name="dbArticlesOdbcMySql5" connectionString="Driver={MySQL ODBC 3.51 Driver};Server=localhost;Database=dbarticles; User=root;Password=root;Option=3;" />
<add name="dbArticlesOleDbSqlServer2005" connectionString="Provider=SQLNCLI;Server=.\SQLExpress;AttachDbFilename=|DataDirectory|\dbarticles.mdf;Uid=sa;Pwd=msde;" />
<add name="dbArticlesOdbcSqlServer2005" connectionString="Driver={SQL Native Client};Server=.\SQLExpress;AttachDbFilename=|DataDirectory|\dbarticles.mdf;Uid=sa;Pwd=msde;" />
<add name="dbArticlesOleDbAccess" connectionString="Provider=Microsoft.Jet.OLEDB.4.0;Data Source=|DataDirectory|\dbarticles.mdb;Persist Security Info=True"/>
</connectionStrings>
<appSettings>
<add key="factorySqlServerCe" value="System.Data.SqlServerCe.3.5"/>
<add key="factoryMySql" value="MySql.Data.MySqlClient"/>
<add key="factorySqlServer" value="System.Data.SqlClient"/>
<add key="factoryOdbc" value="System.Data.Odbc"/>
<add key="factoryOleDb" value="System.Data.OleDb"/>
</appSettings>
</configuration>
- 3-11. satırlar: Kullanılan çeşitli veritabanlarının bağlantı dizeleri.
- 13-17. satırlar: Kullanılacak genel bağlayıcıların adları
[Program.cs] programı şu şekildedir:
...
using System.Data.Common;
namespace Chap7 {
class SqlCommands {
static void Main(string[] args) {
// konsol uygulaması - klavyeden girilen SQL sorgularını yürütür
// bağlantı dizesi ve ilişkili SGBD konektörünün adı bir yapılandırma dosyasından alınan bir veritabanında
// parametrelerin doğrulanması
if (args.Length != 2) {
Console.WriteLine("Syntaxe : pg factory connectionString");
return;
}
// yapılandırma dosyasının işlenmesi
string factory = null;
string connectionString = null;
DbProviderFactory connecteur = null;
try {
// fabrika
factory = ConfigurationManager.AppSettings[args[0]];
// bağlantı zinciri
connectionString = ConfigurationManager.ConnectionStrings[args[1]].ConnectionString;
// SGBD için genel bir konektör alınır
connecteur = DbProviderFactories.GetFactory(factory);
} catch (Exception e) {
Console.WriteLine("Erreur de configuration : {0}", e.Message);
return;
}
// görüntülemeler
Console.WriteLine("Provider factory : [{0}]\n", factory);
Console.WriteLine("Chaîne de connexion à la base : [{0}]\n", connectionString);
...
// sorgunun yürütülmesi
if (champs[0] == "select") {
ExecuteSelect(connecteur,connectionString, requête);
} else
ExecuteUpdate(connecteur, connectionString, requête);
}
}
// güncelleme isteğinin yürütülmesi
static void ExecuteUpdate(DbProviderFactory connecteur, string connectionString, string requête) {
// olası istisnalar yönetiliyor
try {
using (DbConnection connexion = connecteur.CreateConnection()) {
// bağlantı yapılandırması
connexion.ConnectionString = connectionString;
// bağlantı kuruluyor
connexion.Open();
// Command yapılandırması
DbCommand sqlCommand = connecteur.CreateCommand();
sqlCommand.CommandText = requête;
sqlCommand.Connection = connexion;
// isteğin yürütülmesi
int nbLignes = sqlCommand.ExecuteNonQuery();
// sonuç görüntüleme
Console.WriteLine("Il y a eu {0} ligne(s) modifiée(s)", nbLignes);
}
} catch (Exception ex) {
// hata mesajı
Console.WriteLine("Erreur d'accès à la base de données (" + ex.Message + ")");
}
}
// Select sorgusunun yürütülmesi
static void ExecuteSelect(DbProviderFactory connecteur, string connectionString, string requête) {
// olası istisnaları yönetme
try {
using (DbConnection connexion = connecteur.CreateConnection()) {
// bağlantı yapılandırması
connexion.ConnectionString = connectionString;
// bağlantı kuruluyor
connexion.Open();
// Command yapılandırması
DbCommand sqlCommand = connecteur.CreateCommand();
sqlCommand.CommandText = requête;
sqlCommand.Connection = connexion;
// sorgunun yürütülmesi
DbDataReader reader = sqlCommand.ExecuteReader();
// sonuçların görüntülenmesi
...
}
} catch (Exception ex) {
// hata mesajı
Console.WriteLine("Erreur d'accès à la base de données (" + ex.Message + ")");
}
}
}
}
- 12-14. satırlar: Uygulama, [App.config] dosyasındaki anahtarlar biçiminde genel bağlayıcının adı ve veritabanına bağlantı dizesi olmak üzere iki parametre alır.
- 23. ve 25. satırlar: [App.config] dosyasından genel konektörün adı ve bağlantı dizesi alınır
- 27. satır: Genel bağlayıcı örneklenir. Bu andan itibaren, belirli bir SGBD ile ilişkilendirilir.
- 39-43. satırlar: Klavyeden girilen SQL komutunun yürütülmesi, aşağıdakileri içeren iki yönteme devredilir:
- yürütülecek sorgu
- sorgunun yürütüleceği veritabanını tanımlayan bağlantı dizesi
- veritabanını yöneten SGBD ile iletişim kurmak için kullanılacak sınıfları tanımlayan genel bağlayıcı.
- 50-54. satırlar: Genel konektörün CreateConnection yöntemi (50. satır) ile bir bağlantı kurulur ve ardından yönetilecek veritabanının bağlantı dizesi ile yapılandırılır (52. satır). Ardından bağlantı açılır (54. satır).
- 56-58. satırlar: SQL komutunun yürütülmesi için gerekli olan Command nesnesi, genel konektörün CreateCommand yöntemi ile oluşturulur. Ardından, yürütülecek SQL emrinin metni (satır 57) ve emrin yürütüleceği bağlantı (satır 58) ile yapılandırılır.
- 60. satır: SQL güncelleme komutu yürütülür
- 74-87. satırlar: Benzer bir kod bulunur. Yeni gelişme 84. satırda yer almaktadır. Select komutunun yürütülmesiyle elde edilen Reader nesnesi, DbDataReader türündedir ve daha önce karşılaştığımız OleDbDataReader, OdbcDataReader, ... gibi kullanılır.
İşte birkaç yürütme örneği.
MySQL5 veritabanıyla:
![]() |
[1] projesinin özellikler sayfasını açıp [Debug] [2] sekmesini seçiyoruz. [3]'te, [App.config]'in 14. satırındaki konektör anahtarı. [4]'te, [App.config]'in 6. satırındaki bağlantı dizisinin anahtarı. Yürütme sonuçları şöyledir:
SQL Server Compact veritabanıyla:
![]() |
[1]'te, [App.config]'in 13. satırındaki konektör anahtarı. [2]'te, [App.config]'in 4. satırındaki bağlantı dizesinin anahtarı. Çalıştırma sonuçları şöyledir:
Okuyucunun diğer veritabanlarını da test etmesi önerilir.
9.4.6. Hangi konektör seçilmeli?
Veritabanlı bir uygulamanın mimarisine geri dönelim:
![]() |
Çeşitli konektör türlerini inceledik ADO.NET:
- Özel konektörler en yüksek performansa sahiptir, ancak [dao] katmanını özel sınıflara bağımlı hale getirir. SGBD'i değiştirmek, [dao] katmanını da değiştirmek anlamına gelir.
- ODBC, OLE veya DB konektörleri, [dao] katmanını değiştirmeden birden fazla veritabanıyla çalışmaya olanak tanır. Bunlar, özel konektörlere göre daha düşük performansa sahiptir.
- Genel konektör, özel konektörlere dayanırken [dao] katmanına standart bir arayüz sunar.
Dolayısıyla, genel amaçlı konektör ideal konektör gibi görünmektedir. Ancak pratikte, genel amaçlı konektör bir SGBD'in tüm özelliklerini standart bir arayüzün arkasına gizleyememektedir. Bir sonraki paragrafta, parametreli sorgu kavramını inceleyeceğiz. SQL Sunucusu ile, aşağıdaki biçimdeki bir parametreli sorgu:
MySQL5 ile aynı sorgu şu şekilde yazılır:
Dolayısıyla sözdiziminde bir fark vardır. 9.3.3. paragrafta açıklanan, parametrelerle ilgili IDbCommand arayüzünün özelliği şöyledir:
SQL emrinin parametre listesi. "update articles set prix=prix*1.1 where id=@id" emri, @id parametresine sahiptir. |
Parameters özelliği, bir arayüz olan IDataParameterCollection türündedir. Bu özellik, SQL ve CommandText emirlerinin tüm parametrelerini temsil eder. Parameters özelliği, yine bir arayüz olan IDataParameter türündeki parametreleri eklemek için Add yöntemine sahiptir. Bu yöntem aşağıdaki özelliklere sahiptir:
- ParameterName: parametrenin adı
- DbType: parametrenin SQL türü
- Value: parametreye atanan değer
- ...
IDataParameter türü, SQL sırasındaki parametrelere uygundur
çünkü bu emirde adlandırılmış parametreler bulunmaktadır. ParameterName özelliği kullanılabilir.
IDataParameter türü, SQL
çünkü parametrelere isim verilmemiştir. Bu durumda, [IDbCommand.Parameters] koleksiyonuna parametrelerin eklenme sırası dikkate alınır. Bu örnekte, 4 parametreyi nom, prix, stockactuel, stockminimum sırasıyla eklemek gerekir. Adlandırılmış parametreli istekte, parametrelerin eklenme sırası önemli değildir. Sonuç olarak, geliştirici, parametreli bir isteğin parametrelerini başlatırken kullandığı SGBD'i tamamen göz ardı edemez. Bu, genel konektörün mevcut sınırlamalarından biridir.
Bu sınırlamaları aşan ve ayrıca [dao] katmanına yeni işlevler katan frameworks'ler mevcuttur:
![]() |
Çerçeve (framework), uygulamanın belirli bir mimariye göre tasarlanmasını kolaylaştırmayı amaçlayan bir sınıf kütüphaneleri kümesidir. Hem yüksek performanslı hem de [dao] değişikliklerine duyarlı olmayan SGBD katmanlarının yazılmasını sağlayan birçok çerçeve mevcuttur:
- Spring.Net [http://www.springframework.net/], bu belgede daha önce tanıtılmış olan bu çerçeve, incelenen genel konektörün sınırlamaları olmaksızın eşdeğerini sunar ve verilere erişimi kolaylaştıran çeşitli olanaklar sağlar. Java sürümü de mevcuttur.
- iBatis.Net [http://ibatis.apache.org], Spring.Net'ten daha eskidir ve daha zengin özelliklere sahiptir. Java sürümü mevcuttur.
- NHibernate [http://www.hibernate.org/], Java dünyasında çok tanınan Hibernate'in Java sürümünün bir uyarlamasıdır. NHibernate, [dao] katmanının SGBD ile SQL komutları göndermeden iletişim kurmasını sağlar. [dao] katmanı, Hibernate nesneleriyle çalışır. HBL sorgu dili (Hibernate Query Language), Hibernate tarafından yönetilen nesnelere sorgu gönderilmesini sağlar. SQL komutlarını gönderenler bu nesnelerdir. Hibernate, SGBD'e ait SQL'e uyum sağlayabilir.
- LINQ (INtegrated Sorgu Dili), 3.5 sürümüne entegre edilmiştir ve C# 2008'de kullanılabilir. LINQ, NHibernate'in izinden gidiyor, ancak şu an için (Mayıs 2008) yalnızca SGBD ve SQL Sunucusu desteklenmektedir. Bu durum zamanla değişecektir. LINQ, NHibernate'ten daha ileri düzeyde işlevler sunar: sorgu dili, üç farklı türde veri kaynağını standart bir şekilde sorgulamaya olanak tanır:
- nesne koleksiyonları (LINQ to Objects)
- bir XML dosyası (LINQ to Xml)
- bir veritabanı (LINQ to SQL)
Bu çerçeveler bu belgede ele alınmayacaktır. Ancak, profesyonel uygulamalarda bunların kullanılması şiddetle tavsiye edilir.
9.5. Parametreli sorgular
Bir önceki paragrafta parametreli sorgulardan bahsetmiştik. Bunları burada SGBD SQL Server Compact örneği ile sunuyoruz. Proje şu şekildedir
![]() |
- [1] projesinde. Yalnızca [App.config], [Article.cs] ve [Parametres.cs] kullanılmaktadır. Ayrıca SQL Server Ce [dbarticles.sdf] temeline de dikkat edilmelidir.
- [2]'te proje, [Parametres.cs]'i çalıştırmak üzere yapılandırılmıştır
- [3]'te, projenin referansları
[App.config] yapılandırma dosyası, veritabanına bağlantı dizesini tanımlar:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<connectionStrings>
<add name="dbArticlesSqlServerCe" connectionString="Data Source=|DataDirectory|\dbarticles.sdf;Password=dbarticles;" />
</connectionStrings>
</configuration>
[Article.cs] dosyası, [Article] sınıfını tanımlar. Article nesnesi, [dbarticles.sdf] veritabanındaki ARTICLES tablosunun bir satırındaki bilgileri sarmalamak için kullanılacaktır:
namespace Chap7 {
class Article {
// özellikler
public int Id { get; set; }
public string Nom { get; set; }
public decimal Prix { get; set; }
public int StockActuel { get; set; }
public int StockMinimum { get; set; }
// oluşturucular
public Article() {
}
public Article(int id, string nom, decimal prix, int stockActuel, int stockMinimum) {
Id = id;
Nom = nom;
Prix = prix;
StockActuel = stockActuel;
StockMinimum = stockMinimum;
}
}
}
[Parametres.cs] uygulaması, parametreli sorguları uygular:
using System;
using System.Data.SqlServerCe;
using System.Text;
using System.Data;
using System.Configuration;
namespace Chap7 {
class Parametres {
static void Main(string[] args) {
// yapılandırma dosyasının işlenmesi
string connectionString = null;
try {
// bağlantı dizesi
connectionString = ConfigurationManager.ConnectionStrings["dbArticlesSqlServerCe"].ConnectionString;
} catch (Exception e) {
Console.WriteLine("Erreur de configuration : {0}", e.Message);
return;
}
// görüntülemeler
Console.WriteLine("Chaîne de connexion à la base : [{0}]\n", connectionString);
// ürün tablosu oluşturma
Article[] articles = new Article[5];
for (int i = 1; i <= articles.Length; i++) {
articles[i-1] = new Article(0, "article" + i, i * 100, i * 10, i);
}
// olası istisnaların yönetimi
try {
// veritabanındaki mevcut ürünleri silme
ExecuteUpdate(connectionString, "delete from articles");
// tablodaki ürünleri görüntüleme
ExecuteSelect(connectionString, "select id,nom,prix,stockactuel,stockminimum from articles");
// ürün tablosunu veritabanına eklenir
InsertArticles(connectionString, articles);
// tablodaki ürünleri görüntüler
ExecuteSelect(connectionString, "select id,nom,prix,stockactuel,stockminimum from articles");
} catch (Exception ex) {
// hata mesajı
Console.WriteLine("Erreur d'accès à la base de données (" + ex.Message + ")");
}
}
// ürün tablosu ekleniyor
static void InsertArticles(string connectionString, Article[] articles) {
using (SqlCeConnection connexion = new SqlCeConnection(connectionString)) {
// bağlantı açılıyor
connexion.Open();
// komut yapılandırması
string requête = "insert into articles(nom,prix,stockactuel,stockminimum) values(@nom,@prix,@sa,@sm)";
SqlCeCommand sqlCommand = new SqlCeCommand(requête, connexion);
sqlCommand.Parameters.Add("@nom",SqlDbType.NVarChar,30);
sqlCommand.Parameters.Add("@prix", SqlDbType.Money);
sqlCommand.Parameters.Add("@sa", SqlDbType.Int);
sqlCommand.Parameters.Add("@sm", SqlDbType.Int);
// siparişin derlenmesi
sqlCommand.Prepare();
// satırların eklenmesi
for (int i = 0; i < articles.Length; i++) {
// parametrelerin başlatılması
sqlCommand.Parameters["@nom"].Value = articles[i].Nom;
sqlCommand.Parameters["@prix"].Value = articles[i].Prix;
sqlCommand.Parameters["@sa"].Value = articles[i].StockActuel;
sqlCommand.Parameters["@sm"].Value = articles[i].StockMinimum;
// sorgu yürütme
sqlCommand.ExecuteNonQuery();
}
}
}
// güncelleme sorgusunun yürütülmesi
static void ExecuteUpdate(string connectionString, string requête) {
...
}
// Select sorgusunun yürütülmesi
static void ExecuteSelect(string connectionString, string requête) {
...
}
// okuyucu görüntüleme
static void AfficheReader(IDataReader reader) {
...
}
}
Daha önce incelenenlere kıyasla yeni olan, 51-75. satırlardaki [InsertArticles] prosedürüdür:
- 51. satır: Prosedür iki parametre alır:
- prosedürün veritabanına bağlanmasını sağlayacak connectionString bağlantı dizesi
- veritabanındaki Articles tablosuna eklenmesi gereken Article nesne dizisi
- satır 56: [Article] nesnesinin eklenmesi için sorgu. Dört parametresi vardır:
- @nom: ürünün adı
- @prix: Ürünün fiyatı
- @sa: mevcut stok miktarı
- @sm: minimum stok miktarı
Bu parametreli sorgunun sözdizimi, SQL Server Compact'a özeldir. Bir önceki paragrafta, MySQL5 ile sözdiziminin şu şekilde olacağını görmüştük:
SQL Server Compact ile her parametrenin başına @ karakteri eklenmelidir. Parametre isimleri serbesttir.
- 58-61. satırlar: 4 parametrenin her birinin özellikleri tanımlanır ve bunlar, yürütülecek olan SQL emrini kapsayan SqlCeCommand nesnesinin parametre listesine tek tek eklenir.
Burada, altı imza içeren [SqlCeCommand].Parameters.Add yöntemini kullanıyoruz. Aşağıdaki iki imzayı kullanıyoruz:
Add(string parameterName, SQLDbType type)
parameterName adlı parametreyi ekler ve yapılandırır. Bu ad, yapılandırılmış parametreli sorgudaki adlardan biri olmalıdır: (@ad, ...). type, parametrenin ilgili olduğu sütunun SQL türünü belirtir. Aşağıdakiler de dahil olmak üzere birçok tür mevcuttur:
SQL türü | C# türü | açıklama |
Int64 | ||
DateTime | ||
Decimal | ||
Çift | ||
Int32 | ||
Ondalık | ||
Dize | sabit uzunluklu dize | |
Dize | değişken uzunlukta dize | |
Tek |
Add(string parameterName, SQLDbType type, int size)
üçüncü parametre size, sütunun boyutunu belirler. Bu bilgi yalnızca belirli türler için yararlıdır, örneğin SQL ve NVarChar türleri.
- 63. satır: Parametreli sorgu derlenir. Buna "hazırlama" da denir; yöntemin adı da buradan gelir. Bu işlem zorunlu değildir. Performansı artırmak amacıyla yapılır. Bir SGBD, bir SQL komutunu çalıştırdığında, komutu çalıştırmadan önce belirli bir optimizasyon işlemi gerçekleştirir. Parametrelendirilmiş bir sorgu, farklı parametrelerle birden çok kez çalıştırılmak üzere tasarlanmıştır. Sorgunun metni ise değişmez. Böylece optimizasyon işlemi yalnızca bir kez yapılabilir. Bazı SGBD'ler, parametreli sorguları "hazırlama" veya "derleme" olanağına sahiptir. Bu durumda, söz konusu sorgu için bir yürütme planı tanımlanır. Bu, daha önce bahsettiğimiz optimizasyon aşamasıdır. Derlendikten sonra, sorgu her seferinde yeni parametrelerle ancak aynı yürütme planı kullanılarak tekrar tekrar yürütülür.
Derleme, parametreli sorguların tek avantajı değildir. İncelediğimiz sorguyu tekrar ele alalım:
Sorgu metnini program yoluyla oluşturmak isteyebiliriz:
string requête="insert into articles(nom,prix,stockactuel,stockminimum) values('"+nom+"',"+prix+","+sa+","+sm+")";
Yukarıdaki örnekte, (ad, fiyat, sa, sm) değeri ("article1", 100, 10, 1) ise, önceki sorgu şu şekilde olur:
string requête="insert into articles(nom,prix,stockactuel,stockminimum) values('article1',100,10,1)";
Şimdi, (ad, fiyat, sa, sm) değerleri ("ürün1", 100, 10, 1) ise, önceki sorgu şu hale gelir:
string requête="insert into articles(nom,prix,stockactuel,stockminimum) values('l'article1',100,10,1)";
ve l'article1 adındaki kesme işareti nedeniyle sözdizimsel olarak hatalı hale gelir. nom, kullanıcı girişinden geliyorsa, bu, girişte kesme işareti olup olmadığını kontrol etmemiz ve varsa bunları etkisiz hale getirmemiz gerektiği anlamına gelir. Bu düzeltme işlemi SGBD'e bağlıdır. Hazırlanmış sorgunun avantajı, bu işi kendi başına yapmasıdır. Bu kolaylık tek başına hazırlanmış sorgu kullanımını haklı kılar.
- 65-73. satırlar: Tablodaki satırlar tek tek eklenir
- 67-70. satırlar: Sorgudaki dört parametrenin her biri, Value özelliği aracılığıyla değerini alır.
- 72. satır: Artık tamamlanmış olan ekleme sorgusu, her zamanki gibi yürütülür.
İşte bir yürütme örneği:
- 3. satır: Tablodaki tüm satırların silinmesinden sonra görüntülenen mesaj
- satır 5-7: tablonun boş olduğunu gösterir
- satır 10-18: 5 ürünün eklenmesinden sonra tablonun durumunu gösterir
9.6. Transactions
9.6.1. Genel Bilgiler
Bir işlem, "atomik" bir şekilde yürütülen bir dizi SQL komutudur:
- ya tüm işlemler başarılı olur
- ya da bunlardan biri başarısız olursa, ondan önceki tüm işlemler iptal edilir
Sonuç olarak, bir işlemin içindeki işlemler ya tümüyle başarıyla uygulanır ya da hiçbiri uygulanmaz. Kullanıcı işlemin kontrolünü elinde tuttuğunda, bir COMMIT komutuyla işlemi onaylar veya bir ROLLBACK komutuyla iptal eder.
Önceki örneklerimizde işlem kullanmadık. Oysa aslında bir işlem vardı, çünkü bir SGBD komutunda SQL komutu her zaman bir işlem kapsamında yürütülür. .NET istemcisi kendisi açık bir işlem başlatmazsa, SGBD istemcisi örtük bir işlem kullanır. Bu durumda iki yaygın durum söz konusudur:
- her bir SQL emri, emirden önce SGBD tarafından başlatılan ve ardından kapatılan bir işlemin konusu olur. Buna otomatik onay (autocommit) modu denir. Dolayısıyla her şey, sanki .NET istemcisi her bir SQL emri için ayrı bir işlem gerçekleştiriyormuş gibi gerçekleşir.
- SGBD, otomatik onay modunda değildir ve .NET istemcisinin bir işlem dışında gönderdiği ilk emir olan SQL ile örtük bir işlem başlatır ve bu işlemi istemcinin kapatmasına izin verir. .NET istemcisi tarafından gönderilen tüm SQL emirleri bu noktada örtük işlemin bir parçası haline gelir. Bu işlem, çeşitli olaylar sonucunda sona erebilir: müşteri bağlantıyı kapatır, yeni bir işlem başlatır, ... ancak bu durumda SGBD'e bağımlı bir durum ortaya çıkar. Bu moddan kaçınılmalıdır.
Varsayılan mod genellikle SGBD yapılandırmasıyla belirlenir. Bazı SGBD'ler varsayılan olarak otomatik onay modundadır, diğerleri ise değildir. SQLServer Compact, varsayılan olarak otomatik onay modundadır.
Farklı kullanıcıların SQL komutları, paralel olarak çalışan işlemler içinde aynı anda yürütülür. Bir işlem tarafından gerçekleştirilen işlemler, başka bir işlem tarafından gerçekleştirilenleri etkileyebilir. Farklı kullanıcıların işlemleri arasında dört farklı yalıtım düzeyi vardır:
- Uncommitted Read
- Onaylanmış Okuma
- Tekrarlanabilir Okuma
- Serileştirilebilir
Onaylanmamış Okuma
Bu yalıtım modu "Dirty Read" olarak da adlandırılır. İşte bu modda neler olabileceğine dair bir örnek:
- U1 adlı bir kullanıcı, T tablosu üzerinde bir işlem başlatır
- U2 adlı bir kullanıcı aynı T tablosu üzerinde bir işlem başlatır
- U1 adlı kullanıcı, T tablosundaki satırları değiştirir ancak henüz onaylamaz
- U2 kullanıcısı bu değişiklikleri "görür" ve gördüklerine dayanarak kararlar alır
- kullanıcı, ROLLBACK komutuyla işlemini iptal eder
- adımda, U2 kullanıcısının, daha sonra yanlış olduğu ortaya çıkacak verilere dayanarak bir karar verdiği görülmektedir.
Committed Read
Bu izolasyon modu, önceki sorunu önler. Bu modda, 4. adımdaki U2 kullanıcısı, U1 kullanıcısının T tablosunda yaptığı değişiklikleri "görmeyecektir". Bu değişiklikleri ancak U1 kullanıcısı işlemini tamamladıktan sonra görebilir.
"Unrepeatable Read" olarak da adlandırılan bu modda, yine de aşağıdaki durumlarla karşılaşılabilir:
- U1 adlı bir kullanıcı, T tablosu üzerinde bir işlem başlatır
- U2 adlı bir kullanıcı aynı T tablosunda bir işlem başlatır
- U2 kullanıcısı, belirli bir koşulu karşılayan T tablosundaki satırların C sütunundaki ortalamasını almak için SELECT işlemini gerçekleştirir
- kullanıcı U1, T tablosunun C sütunundaki belirli değerleri değiştirir (UPDATE) ve bunları onaylar (COMMIT)
- kullanıcı U2, 3. adımdaki ile aynı SELECT işlemini tekrar gerçekleştirir. U1 tarafından yapılan değişiklikler nedeniyle C sütununun ortalamasının değiştiğini fark edecektir.
Şimdi U2 kullanıcısı, yalnızca U1 tarafından "onaylanan" değişiklikleri görebilir. Ancak aynı işlem içinde kalırken, 3 ve 5 numaralı iki özdeş işlem farklı sonuçlar verir. "Unrepeatable Read" terimi bu durumu ifade eder. Bu, T tablosunun sabit bir görüntüsüne sahip olmak isteyen biri için can sıkıcı bir durumdur.
Tekrarlanabilir Okuma
Bu izolasyon modunda, bir kullanıcı aynı işlem içinde kaldığı sürece veritabanından yaptığı okumalarda aynı sonuçları alacağı garanti edilir. Kullanıcı, diğer işlemler tarafından yapılan değişikliklerin (onaylanmış olsalar bile) asla yansıtılmadığı bir anlık görüntü üzerinde çalışır. Bu değişiklikleri ancak kendisi COMMIT veya ROLLBACK komutuyla işlemini tamamladığında görebilir.
Ancak bu izolasyon modu henüz mükemmel değildir. Yukarıdaki 3. işlemden sonra, U2 kullanıcısı tarafından sorgulanan satırlar kilitlenir. 4. işlem sırasında, U1 kullanıcısı bu satırların C sütunundaki değerleri (UPDATE) değiştiremez. Ancak yeni satırlar ekleyebilir (INSERT). Eklenen satırlardan bazıları 3. adımda test edilen koşulu karşılıyorsa, 5. işlemde eklenen satırlar nedeniyle 3. adımda bulunan ortalamadan farklı bir ortalama elde edilir. Bu satırlara bazen "hayalet satırlar" denir.
Bu yeni sorunu çözmek için "Serializable" izolasyon moduna geçmek gerekir.
Serializable
Bu izolasyon modunda, işlemler birbirinden tamamen yalıtılmıştır. Bu mod, eşzamanlı olarak yürütülen iki işlemin sonucunun, sırayla gerçekleştirilmiş olsaydı elde edilecek sonuçla aynı olmasını garanti eder. Bu sonuca ulaşmak için, U1 kullanıcısının, U1 kullanıcısının SELECT işleminin sonucunu değiştirecek satırlar eklemek istediği 4. işlem sırasında, bu işlem engellenecektir. Bir hata mesajı, eklemenin mümkün olmadığını bildirecektir. U2 kullanıcısı işlemini onayladığında ekleme mümkün hale gelecektir.
Dört işlem yalıtım seviyesi SQL, tüm SGBD'lerde mevcut değildir. Varsayılan yalıtım seviyesi genellikle Committed Read seviyesidir. Bir işlem için istenen yalıtım seviyesi, bir .NET istemcisi tarafından açık bir işlem oluşturulurken açıkça belirtilebilir.
9.6.2. API işlem yönetimi
Bir bağlantı, 9.3.3. paragrafında sunulan IDbConnection arayüzünü uygular. Bu arayüz aşağıdaki yöntemi içerir:
M | bir işlemi başlatır. |
Bu yöntemin iki imzası vardır:
- IDbTransaction BeginTransaction(): bir işlem başlatır ve bu işlemi kontrol etmeyi sağlayan IDbTransaction nesnesini döndürür
- IDbTransaction BeginTransaction(IsolationLevel seviye) : işlem için istenen güvenlik seviyesini daha ayrıntılı olarak belirtir. level, değerlerini aşağıdaki sıralamadan alır:
işlem, henüz onaylamadığı başka bir işlem tarafından yazılmış verileri okuyabilir – kaçınılmalıdır | |
işlem, henüz onaylamadığı başka bir işlem tarafından yazılan verileri okuyamaz. Ancak, işlemde arka arkaya iki kez okunan veriler değişebilir (tekrarlanamayan okumalar), çünkü bu arada başka bir işlem verileri değiştirmiş olabilir (okunan satırlar kilitlenmez; yalnızca güncellenen satırlar kilitlenir). Ayrıca, başka bir işlem, ikinci okumaya dahil edilecek satırlar (hayalet satırlar) eklemiş olabilir. | |
İşlem tarafından okunan satırlar, güncellenen satırlar gibi kilitlenir. Bu, başka bir işlemin bu satırları değiştirmesini engeller. Ancak bu, yeni satırların eklenmesini engellemez. | |
İşlem tarafından kullanılan tablolar kilitlenir ve başka bir işlemin yeni satır eklemesini engeller. Sanki işlem tek başına çalışıyormuş gibi her şey gerçekleşir. İşlemler artık paralel olarak çalışmadığından performans düşer. | |
İşlem, T zamanında oluşturulan verilerin bir kopyası üzerinde çalışır. İşlem salt okunur durumdayken kullanılır. serializable ile aynı sonucu verir, ancak bunun maliyetini ortadan kaldırır. |
İşlem başlatıldıktan sonra, IDbTransaction türündeki nesne tarafından kontrol edilir; bu bir arayüzdür ve biz bu arayüzün aşağıdaki P özelliklerini ve M yöntemlerini kullanacağız:
Ad | Tür | Rol |
P | IDbConnection bağlantısı, şu işlemi destekler: | |
M | işlemi onaylar - işlemde verilen SQL emirlerinin sonuçları veritabanına kopyalanır. | |
M | işlemi geçersiz kılar - işlemde verilen SQL komutlarının sonuçları veritabanına kopyalanmaz. |
9.6.3. Örnek program
Önceki projeyi ele alarak şimdi [Transactions.cs] programına odaklanacağız:
![]() |
- [1]'te, proje.
- [2]'te proje, [Transactions.cs]'i çalıştıracak şekilde yapılandırılmıştır
[Transactions.cs] kodunun içeriği şöyledir:
using System;
using System.Configuration;
using System.Data;
using System.Data.SqlServerCe;
using System.Text;
namespace Chap7 {
class Transactions {
static void Main(string[] args) {
// yapılandırma dosyasının işlenmesi
string connectionString = null;
try {
// bağlantı dizesi
connectionString = ConfigurationManager.ConnectionStrings["dbArticlesSqlServerCe"].ConnectionString;
} catch (Exception e) {
Console.WriteLine("Erreur de configuration : {0}", e.Message);
return;
}
// görüntülemeler
Console.WriteLine("Chaîne de connexion à la base : [{0}]\n", connectionString);
// aynı ada sahip 2 öğeden oluşan bir tablo oluşturma
Article[] articles = new Article[2];
for (int i = 1; i <= articles.Length; i++) {
articles[i - 1] = new Article(0, "article", i * 100, i * 10, i);
}
// olası istisnaların yönetimi
try {
Console.WriteLine("Insertion sans transaction...");
// ürün tablosunu önce işlem yapmadan veritabanına eklenir
ExecuteUpdate(connectionString, "delete from articles");
try {
InsertArticlesOutOfTransaction(connectionString, articles);
} catch (Exception ex) {
// hata mesajı
Console.WriteLine("Erreur d'accès à la base de données (" + ex.Message + ")");
}
ExecuteSelect(connectionString, "select id,nom,prix,stockactuel,stockminimum from articles");
// aynı işlemi bu sefer bir işlem içinde tekrar yapıyoruz
Console.WriteLine("\n\nInsertion dans une transaction...");
ExecuteUpdate(connectionString, "delete from articles");
InsertArticlesInTransaction(connectionString, articles);
ExecuteSelect(connectionString, "select id,nom,prix,stockactuel,stockminimum from articles");
} catch (Exception ex) {
// hata mesajı
Console.WriteLine("Erreur d'accès à la base de données (" + ex.Message + ")");
}
}
// işlem yapmadan ürün tablosu ekleniyor
static void InsertArticlesOutOfTransaction(string connectionString, Article[] articles) {
....
}
// bir işlem içinde ürün tablosu ekleniyor
static void InsertArticlesInTransaction(string connectionString, Article[] articles) {
....
}
// güncelleme sorgusunun yürütülmesi
static void ExecuteUpdate(string connectionString, string requête) {
....
}
// Select sorgusunun yürütülmesi
static void ExecuteSelect(string connectionString, string requête) {
...
}
// okuyucu görüntüleme
static void AfficheReader(IDataReader reader) {
...
}
}
}
}
- 12-19. satırlar: SQLServer veritabanına bağlantı dizesi, [App.config]'ten okunur
- 25-28. satırlar: iki nesneden oluşan bir tablo Article oluşturulur. Bu iki nesnenin adı "article"dir. Ancak, [dbarticles.sdf] veritabanında [nom] sütununda tekillik kısıtlaması vardır (bkz. paragraf 9.3.1). Dolayısıyla bu iki öğe veritabanında aynı anda bulunamaz. "article" adlı iki ürün, articles tablosuna eklenir. Bu durumda bir sorun ortaya çıkacaktır: c.a.d. Bu, SGBD tarafından tetiklenen ve ADO.NET konektörü tarafından iletilen bir istisnadır. İşlemin etkisini göstermek için, iki ürün farklı ortamlara eklenecektir:
- öncelikle herhangi bir işlem dışında. Burada unutulmaması gereken nokta, bu durumda SQLServer Compact'ın otomatik onay modunda çalıştığıdır; c.a.d, her bir SQL emrini örtük bir işleme ekler. İlk ürün eklenecektir. İkincisi ise eklenmeyecektir.
- Daha sonra her iki eklemeyi de kapsayan açık bir işleme alınır. İkinci ekleme başarısız olacağından, ilki de geri alınır. Sonuç olarak hiçbir ekleme yapılmaz.
- 33. satır: articles tablosu boşaltılır
- 35. satır: Açık işlem olmadan iki maddenin eklenmesi. İkinci eklemenin bir istisna oluşturacağı bilindiğinden, bu durum bir try/catch bloğu ile yönetilir
- 46. satır: articles tablosu görüntülenir
- satır 44-46: Aynı işlem dizisi tekrarlanır, ancak bu sefer eklemeler için açık bir işlem kullanılır. Karşılaşılan istisna, burada InsertArticlesInTransaction yöntemi ile yönetilir.
- 54-56. satırlar: InsertArticlesOutOfTransaction yöntemi, daha önce incelenen [Parametres.cs] programındaki InsertArticles yöntemidir.
- 64-66. satırlar: ExecuteUpdate yöntemi, öncekiyle aynıdır. SQL komutu, örtük bir işlem içinde yürütülür. Bu, bu durumda SQLServer Compact'ın otomatik onay modunda çalıştığı bilindiği için mümkündür.
- 69-71. satırlar: ExecuteSelect yöntemi için de durum aynıdır.
InsertArticlesInTransaction yöntemi şöyledir:
// bir işlemde ürün tablosu ekleme
static void InsertArticlesInTransaction(string connectionString, Article[] articles) {
using (SqlCeConnection connexion = new SqlCeConnection(connectionString)) {
// bağlantı açma
connexion.Open();
// komut yapılandırma
string requête = "insert into articles(nom,prix,stockactuel,stockminimum) values(@nom,@prix,@sa,@sm)";
SqlCeCommand sqlCommand = new SqlCeCommand(requête, connexion);
sqlCommand.Parameters.Add("@nom", SqlDbType.NVarChar, 30);
sqlCommand.Parameters.Add("@prix", SqlDbType.Money);
sqlCommand.Parameters.Add("@sa", SqlDbType.Int);
sqlCommand.Parameters.Add("@sm", SqlDbType.Int);
// komutun derlenmesi
sqlCommand.Prepare();
// işlem
SqlCeTransaction transaction = null;
try {
// işlemin başlatılması
transaction = connexion.BeginTransaction(IsolationLevel.ReadCommitted);
// SQL komutu bu işlemde yürütülmelidir
sqlCommand.Transaction = transaction;
// satırların eklenmesi
for (int i = 0; i < articles.Length; i++) {
// parametrelerin başlatılması
sqlCommand.Parameters["@nom"].Value = articles[i].Nom;
sqlCommand.Parameters["@prix"].Value = articles[i].Prix;
sqlCommand.Parameters["@sa"].Value = articles[i].StockActuel;
sqlCommand.Parameters["@sm"].Value = articles[i].StockMinimum;
// sorgu yürütme
sqlCommand.ExecuteNonQuery();
}
// işlem onaylanıyor
transaction.Commit();
Console.WriteLine("transaction validée...");
} catch {
// işlemi geri alın
if (transaction != null)transaction.Rollback();
Console.WriteLine("transaction invalidée...");
}
}
}
Yukarıda incelenen [Parametres.cs] programındaki InsertArticles yönteminden farklı olan kısımları detaylandırıyoruz:
- 16. satır: bir SqlCeTransaction işlemi tanımlanmıştır.
- 17. ve 35. satırlar: 2. ekleme işleminin sonunda ortaya çıkacak istisnayı yönetmek için try / catch bloğu
- 19. satır: İşlem oluşturulur. Bu işlem, mevcut bağlantıya aittir.
- 21. satır: Parametreleri ayarlanmış SQL komutu işleme eklenir
- 23-31. satırlar: eklemeler yapılır
- 33. satır: Her şey yolunda gitti – işlem onaylandı – eklemeler veritabanına kalıcı olarak kaydedilecek.
- satır 37: bir sorun yaşandı. İşlem varsa iptal edilir.
Çalıştırma sonucunda şu sonuçlar elde edilir:
- 4. satır: ExecuteUpdate ("delete from articles") tarafından görüntülenmiştir - tabloda satır yoktu
- 5. satır: İkinci ekleme işlemi sırasında ortaya çıkan istisna. Mesaj, UQ__ARTICLES__0000000000000010 kısıtlamasının doğrulanmadığını gösteriyor. Veritabanının özelliklerine bakarak daha fazla bilgi edinebiliriz:
![]() |
- Visual Studio'daki [1] görünümünde, [dbarticles.sdf] veritabanına [2] bağlantısı oluşturulmuştur. Bu bağlantının bir dizini UQ__ARTICLES__0000000000000010'tir. Bu dizine sağ tıklayarak özelliklerine (Index properties) erişebiliriz
- [3,4]'te, UQ__ARTICLES__0000000000000010 indeksinin [NOM] sütunundaki tekillik kısıtlamasına karşılık geldiği görülür
- 7-11. satırlar: İki ekleme işleminden sonra articles tablosunun görüntüsü. Tablo boş değildir: ilk makale eklenmiştir.
- 15. satır: ExecuteUpdate ("delete from articles") tarafından görüntülenir - tabloda bir satır vardı
- 16. satır: İşlem başarısız olduğunda InsertArticlesInTransaction tarafından görüntülenen mesaj.
- 18-20. satırlar: Hiçbir ekleme yapılmadığını gösterir. İşlemin Rollback komutu, ilk eklemeyi geri almıştır.
9.7. ExecuteScalar yöntemi
9.7.1. 9.3.3 numaralı paragrafta açıklanan IDbCommand arayüzünün yöntemleri arasında şu yöntem de vardı:
M | SQL komutunu çalıştırmak için Select, tıpkı "select count(*) from articles" gibi tek bir sonuç döndürür. |
Burada bu yöntemin kullanımına dair bir örnek gösteriyoruz. Projeye geri dönelim:
![]() |
- [1]'te, proje.
- [2]'te proje, [ExecuteScalar.cs]'i çalıştıracak şekilde yapılandırılmıştır
[ExecuteScalar.cs] programı şu şekildedir:
...
namespace Chap7 {
class Scalar {
static void Main(string[] args) {
// yapılandırma dosyasının işlenmesi
string connectionString = null;
...
// görüntülemeler
Console.WriteLine("Chaîne de connexion à la base : [{0}]\n", connectionString);
// 5 öğeden oluşan bir tablo oluşturuluyor
Article[] articles = new Article[5];
for (int i = 1; i <= articles.Length; i++) {
articles[i - 1] = new Article(0, "article" + i, i * 100, i * 10, i);
}
// olası istisnalar yönetiliyor
try {
// ürün tablosunu bir işleme ekleniyor
ExecuteUpdate(connectionString, "delete from articles");
InsertArticlesInTransaction(connectionString, articles);
ExecuteSelect(connectionString, "select id,nom,prix,stockactuel,stockminimum from articles");
// ürün fiyatlarının ortalaması hesaplanıyor
decimal prixMoyen = (decimal)ExecuteScalar(connectionString, "select avg(prix) from articles");
Console.WriteLine("Prix moyen des articles={0}", prixMoyen);
// veya ürün sayısının hesaplanması
int nbArticles = (int)ExecuteScalar(connectionString, "select count(id) from articles");
Console.WriteLine("Nombre d'articles={0}", nbArticles);
} catch (Exception ex) {
// hata mesajı
Console.WriteLine("Erreur d'accès à la base de données (" + ex.Message + ")");
}
}
// bir işlem içine ürün tablosu ekleniyor
static void InsertArticlesInTransaction(string connectionString, Article[] articles) {
...
}
// güncelleme sorgusunun yürütülmesi
static object ExecuteScalar(string connectionString, string requête) {
using (SqlCeConnection connexion = new SqlCeConnection(connectionString)) {
// bağlantı açma
connexion.Open();
// sorgu yürütme
return new SqlCeCommand(requête, connexion).ExecuteScalar();
}
}
// güncelleme sorgusunun yürütülmesi
static void ExecuteUpdate(string connectionString, string requête) {
...
}
// Select sorgusunun yürütülmesi
static void ExecuteSelect(string connectionString, string requête) {
...
}
// okuyucu görüntüleme
static void AfficheReader(IDataReader reader) {
...
}
}
}
- 14-17. satırlar: 5 öğeden oluşan bir tablo oluşturulur
- 22. satır: articles tablosu boşaltılır
- 23. satır: tablo 5 ürünle doldurulur
- 24. satır: tablo görüntülenir
- 26. satır: Ürünlerin ortalama fiyatı sorgulanır
- 29. satır: Ürün sayısını sorgular
- satır 49: Bu değerlerin her birini hesaplamak için [IDbCommand].ExecuteScalar() yöntemi kullanılır.
Çalıştırma sonuçları şöyledir:
- ve 16. satırlar, ExecuteScalar yöntemi tarafından döndürülen iki değeri gösterir.
9.8. Örnek uygulama - sürüm 7
IMPOTS örnek uygulamasını tekrar ele alıyoruz. Son sürüm, 7.6. paragrafta incelenmişti. Bu, aşağıdaki üç katmanlı uygulamaydı:
![]() |
- [ui] katmanı bir grafik arayüz olan [A]'ti ve [dao] katmanı, verilerini [B] adlı bir metin dosyasından alıyordu.
- Katmanların örneklenmesi ve uygulamaya entegrasyonu Spring tarafından sağlanıyordu.
![]() |
[dao] katmanını, verilerini bir veritabanından alması için değiştiriyoruz.
9.8.1. 'i veritabanı
Önceki [B] metin dosyasının içeriği, MySQL5 adlı bir veritabanına aktarılır. Bunun nasıl yapılacağını aşağıda gösteriyoruz:
![]() |
- [1]: MySQL Administrator başlatıldı
- [2,3]: [Schemata] alanında sağ tıklayın ve yeni bir veritabanı oluşturmak için [Create Schema] seçeneğini seçin
- [4]: veritabanının adı [bdimpots] olacaktır
- [5]: Bu veritabanı, [Schemata] alanındaki veritabanlarına eklendi.
![]() |
- [6,7]: tabloya sağ tıklayın ve [Create New Table] seçeneğini seçerek bir tablo oluşturun
- [8]: Tablonun adı [tranches] olacaktır. Bu tablo, [id, limite, coeffR, coeffN] sütunlarına sahip olacaktır.
- [9,10]: [id], INTEGER türünde bir birincil anahtardır ve AUTO_INCREMENT özniteliğine sahiptir. [10]: satır eklenirken bu sütunu doldurma görevi SGBD'e aittir.
- [limite, coeffR, coeffN] sütunları, DOUBLE türündedir.
- [11,12]: Yeni tablo, veritabanındaki [Schema Tables] sekmesinde görünür.
![]() |
- [13,14]: tabloya veri girmek için
- [15]: [Query Browser] başlatıldı
- [16]: [limite, coeffR, coeffN] sütunları için veriler girildi ve doğrulandı. [id] sütunu, SGBD tarafından dolduruldu. Doğrulama, [17] ile gerçekleştirildi.
![]() |
- Yine [Query Browser] ve [18]'te, [20] sorgusu [19] ile yürütülür. Bu sorgu, 'admimpots' kullanıcı adını ve 'mdpimpots' şifresini oluşturur ve bu kullanıcıya bdimpots veritabanındaki tüm nesneler üzerinde tüm ayrıcalıkları (grant all privileges) verir (on bdimpots.*). Bu sayede, [bdimpots] veritabanında, [root] yöneticisi yerine [admimpots] kullanıcısıyla çalışabileceğiz.
9.8.2. Visual Studio çözümü
![]() |
Örnek uygulamanın 5. sürümü için incelenen yaklaşımı takip edeceğiz (bkz. paragraf 6.4). Aşağıdaki Visual Studio çözümünü aşamalı olarak oluşturacağız:
![]() |
- [1]: ImpotsV7 çözümü, uygulamanın üç katmanının her biri için birer tane olmak üzere üç projeden oluşur
- [2]'e: [dao] katmanına ait [dao] projesi, bundan böyle bir veritabanını kullanacak
- [3]'te: [metier] katmanına ait [metier] projesi. Burada, 6.4.4. paragrafında açıklanan sürüm 5'teki [metier] katmanını yeniden kullanıyoruz.
- [4]'te: [ui] katmanının [ui] projesi. Burada, 7.6. paragrafında açıklanan sürüm 6'nın [ui] katmanını ele alıyoruz.
Daha önce yazılmış iki katmanı, yani [ui] ve [metier] katmanlarını geri kazanmak için mevcut çalışmalardan yararlanıyoruz. Bu, seçilen katmanlı mimari sayesinde mümkün olmaktadır. Bununla birlikte, [ui] ve [metier] katmanlarının kaynak kodlarına ihtiyacımız olacak. Zira, katmanların DLL kodlarıyla yetinmek mümkün değildir. Sürüm 5'te, [metier] katmanının DLL katmanı oluşturulduğunda, bu katman [dao] katmanının DLL katmanına bağımlıydı. Bu bağımlılık, [metier] katmanındaki DLL'e sabit olarak kaydedilmiştir ([dao] katmanındaki DLL'in adı, sürüm, kimlik jetonu vb.). Dolayısıyla, sürüm 5 olan [ImpotsV5-metier.dll]'in DLL'i, yalnızca derlendiği DLL ve [ImpotsV5-dao.dll] ile çalışabilir. [dao] katmanındaki DLL değiştirilirse, [metier] katmanını yeniden derleyerek yeni bir DLL oluşturmak gerekir. Aynı durum [ui] katmanı için de geçerlidir. Dolayısıyla, [ui] ve [metier] katmanları değiştirilmeyecek, ancak yeni [dao] katmanındaki DLL ile çalışabilmeleri için yeniden derleneceklerdir.
9.8.3. [dao] katmanı
![]() |
![]() |
Proje referansları (projede [1]'e bakınız)
- nunit.framework: NUnit testi için
- System.Configuration: [App.config] yapılandırma dosyasını kullanmak için
- System.Data: bir veritabanı kullanıldığı için.
Varlıklar (projede [2]'e bakınız)
[TrancheImpot] ve [ImpotException] sınıfları önceki sürümlerinkilerle aynıdır.
[dao] katmanı (projede bkz. [3])
[IImpotDao] arayüzünde herhangi bir değişiklik yapılmamıştır:
using Entites;
namespace Dao {
public interface IImpotDao {
// vergi dilimleri
TrancheImpot[] TranchesImpot{get;}
}
}
Bu arayüzün [DataBaseImpot] uygulama sınıfı şöyledir:
using System;
using System.Collections.Generic;
using System.Data.Common;
using Entites;
namespace Dao {
public class DataBaseImpot : IImpotDao {
// vergi dilimleri
private TrancheImpot[] tranchesImpot;
public TrancheImpot[] TranchesImpot { get { return tranchesImpot; } }
// üretici
public DataBaseImpot(string factory, string connectionString, string requête) {
// fabrika: SGBD hedef fabrika
// connectionString: vergi dilimlerinin temelini oluşturan bağlantı zinciri
// olası istisnalar yönetilir
try {
// SGBD için genel bir bağlayıcı alınır
DbProviderFactory connecteur = DbProviderFactories.GetFactory(factory);
using (DbConnection connexion = connecteur.CreateConnection()) {
// bağlantı yapılandırması
connexion.ConnectionString = connectionString;
// bağlantı açılıyor
connexion.Open();
// Komut yapılandırması
DbCommand sqlCommand = connecteur.CreateCommand();
sqlCommand.CommandText = requête;
sqlCommand.Connection = connexion;
// isteğin yürütülmesi
List<TrancheImpot> listTrancheImpot = new List<TrancheImpot>();
using (DbDataReader reader = sqlCommand.ExecuteReader()) {
while (reader.Read()) {
// yeni bir vergi dilimi oluşturulur
listTrancheImpot.Add(new TrancheImpot() { Limite = reader.GetDecimal(0), CoeffR = reader.GetDecimal(1), CoeffN = reader.GetDecimal(2) });
}
}
// vergi dilimlerini kendi örneğine yerleştirme
tranchesImpot = listTrancheImpot.ToArray();
}
} catch (Exception ex) {
// istisna, ImpotException türüne kapsüllenir
throw new ImpotException("Erreur de lecture des tranches d'impôt", ex) { Code = 101 };
}
}
}
}
- 7. satır: [DataBaseImpot] sınıfı, [IImpotDao] arayüzünü uygular.
- 10. satır: Arayüzün [TranchesImpot] yönteminin uygulaması. Bu yöntem, sadece 9. satırdaki vergi dilimleri tablosuna bir referans döndürür. Bu tablo, sınıfın oluşturucusu tarafından oluşturulacaktır.
- 13. satır: Oluşturucu. Vergi dilimleri veritabanını kullanmak için genel bir bağlayıcı (bkz. 9.4.5. paragraf) kullanır. Oluşturucu üç parametre alır:
- Veritabanına bağlanmak, SQL komutlarını göndermek ve bir Select sorgusunun sonucunu işlemek için sınıfları talep edeceği "factory"nin adı.
- veritabanına bağlanmak için kullanması gereken bağlantı dizesi
- Vergi dilimlerini almak için yürütmesi gereken SQL Select komutu.
- 19. satır: "factory"dan bir konektör talep eder
- 20. satır: Bu konektörle bir bağlantı oluşturur. Bağlantı oluşturulur ancak henüz çalışır durumda değildir
- 22. satır: Bağlantı dizesi başlatılır. Artık bağlantı kurulabilir.
- 24. satır: Bağlantı kurulur
- 26. satır: SQL emrini yürütmek için konektörden bir [DbCommand] nesnesi istenir
- satır 27: Yürütülecek SQL emrini belirler
- 28. satır: Komutun yürütüleceği bağlantı belirlenir
- satır 30: [TrancheImpot] türündeki nesnelerden oluşan [listTrancheImpot] listesi boş olarak oluşturulur.
- satır 31: SQL Select komutu yürütülür
- satır 32-35: Select komutunun sonucu olan [DbDataReader] nesnesi işlenir. Select komutunun sonuç tablosundaki her satır, [TrancheImpot] türünde bir nesne oluşturmak için kullanılır ve bu nesne [listTrancheImpot] listesine eklenir.
- satır 38: [TrancheImpot] türündeki nesnelerden oluşan liste, 9. satırdaki diziye aktarılır.
- 40-43. satırlar: Olası bir istisna, [ImpotException] türünde kapsüllenir ve 101 (keyfi) hata kodu atanır.
[Test1] testi (projede bkz. [4])
[Test1] sınıfı, yalnızca vergi dilimlerini ekranda görüntüler. Bu sınıf, [dao] katmanını oluşturan komut (14. satır) hariç olmak üzere, sürüm 5'te (6.4.3. paragraf) zaten kullanılan sınıftır.
using System;
using Dao;
using Entites;
using System.Configuration;
namespace Tests {
class Test1 {
static void Main() {
// [dao] katmanı oluşturulur
IImpotDao dao = null;
try {
// [dao] katmanının oluşturulması
dao = new DataBaseImpot(ConfigurationManager.AppSettings["factoryMySql5"], ConfigurationManager.ConnectionStrings["dbImpotsMySql5"].ConnectionString, ConfigurationManager.AppSettings["requete"]);
} catch (ImpotException e) {
// hata mesajı görüntüleniyor
string msg = e.InnerException == null ? null : String.Format(", Exception d'origine : {0}", e.InnerException.Message);
Console.WriteLine("L'erreur suivante s'est produite : [Code={0},Message={1}{2}]", e.Code, e.Message, msg == null ? "" : msg);
// program durdurulur
Environment.Exit(1);
}
// vergi dilimlerinin görüntülenmesi
TrancheImpot[] tranchesImpot = dao.TranchesImpot;
foreach (TrancheImpot t in tranchesImpot) {
Console.WriteLine("{0}:{1}:{2}", t.Limite, t.CoeffR, t.CoeffN);
}
}
}
}
- satır, aşağıdaki [App.config] yapılandırma dosyasını kullanır:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<connectionStrings>
<add name="dbImpotsMySql5" connectionString="Server=localhost;Database=bdimpots;Uid=admimpots;Pwd=mdpimpots;" />
</connectionStrings>
<appSettings>
<add key="requete" value="select limite, coeffr, coeffn from tranches"/>
<add key="factoryMySql5" value="MySql.Data.MySqlClient"/>
</appSettings>
</configuration>
- 4. satır: MySQL5 veritabanına bağlantı dizesi. Bağlantıyı kuracak olanın [admimpots] kullanıcısı olduğu unutulmamalıdır.
- 8. satır: SGBD ile çalışmak için "factory" MySQL5
- 7. satır: Vergi dilimlerini almak için SQL Select sorgusu.
Proje, [Test1.cs]'i çalıştıracak şekilde yapılandırılmıştır:

Testin çalıştırılması aşağıdaki sonuçları vermektedir:
NUnit [NUnit1] testi (projede [4]'e bakınız)
[NUnit1] birim testi, [dao] katmanını oluşturan komut (16. satır) hariç olmak üzere, sürüm 5'te (6.4.3. paragraf) zaten kullanılan testtir.
using System;
using System.Configuration;
using Dao;
using Entites;
using NUnit.Framework;
namespace Tests {
[TestFixture]
public class NUnit1 : AssertionHelper{
// [dao] katmanı test edilecek
private IImpotDao dao;
// oluşturucu
public NUnit1() {
// [dao] katmanının başlatılması
dao = new DataBaseImpot(ConfigurationManager.AppSettings["factoryMySql5"], ConfigurationManager.ConnectionStrings["dbImpotsMySql5"].ConnectionString, ConfigurationManager.AppSettings["requete"]);
}
// test
[Test]
public void ShowTranchesImpot(){
// vergi dilimleri görüntüleniyor
TrancheImpot[] tranchesImpot = dao.TranchesImpot;
foreach (TrancheImpot t in tranchesImpot) {
Console.WriteLine("{0}:{1}:{2}", t.Limite, t.CoeffR, t.CoeffN);
}
// bazı testler
Expect(tranchesImpot.Length,EqualTo(7));
Expect(tranchesImpot[2].Limite,EqualTo(14753).Within(1e-6));
Expect(tranchesImpot[2].CoeffR, EqualTo(0.191).Within(1e-6));
Expect(tranchesImpot[2].CoeffN, EqualTo(1322.92).Within(1e-6));
}
}
}
Bu birim testini çalıştırmak için projenin [Class Library] türü olması gerekir:
![]() |
- [1] olarak: projenin türü değiştirildi
- [2]'e: Oluşturulan DLL, [ImpotsV7-dao.dll] olarak adlandırılacaktır
- [3]'te: projenin oluşturulmasından sonra (F6), [dao/bin/Release] klasörü DLL ve [ImpotsV7-dao.dll] dosyalarını içerir. Ayrıca, [App.config] adlı yapılandırma dosyası da [nom DLL].config olarak yeniden adlandırılmış olarak bu klasörde bulunur. Bu, Visual Studio'da standart bir uygulamadır.
Ardından DLL ve [ImpotsV7-dao.dll] dosyaları, NUnit çerçeve yapısına yüklenir ve çalıştırılır:
![]() |
- [1]'te: testler başarıyla tamamlandı. Artık [dao] katmanını çalışır durumda kabul ediyoruz. DLL dosyası, test sınıfları da dahil olmak üzere projenin tüm sınıflarını içerir. Bu sınıflar gereksizdir. Test sınıflarını hariç tutmak için DLL'i yeniden oluşturuyoruz.
- [2]'e: [tests] klasörü projeden çıkarılır
- [3]: yeni proje. Bu proje, yeni bir DLL oluşturmak üzere F6 tarafından yeniden oluşturulur. Uygulamanın [metier] ve [ui] katmanları tarafından kullanılacak olan dosya, bu DLL dosyasıdır.
9.8.4. [metier] katmanı
![]() |
![]() |
- [1]'te, [metier] projesi çözümün aktif projesi haline geldi
- [2]'te: proje referansları. Daha önce oluşturulan [dao] katmanının DLL üzerindeki referansına dikkat edilmelidir. Bu referans ekleme prosedürü, sürüm 5'in 6.4.4. paragrafında açıklanmıştır.
- [3]'te: [metier] katmanı. Bu, 5. sürümde 6.4.4. paragrafında açıklanan katmandır.
[metier] projesi, bir DLL oluşturmak üzere yapılandırılmıştır:
![]() |
- [1]: proje "sınıf kütüphanesi" türündedir
- [2]: Projenin oluşturulması sonucunda DLL, [ImpotsV7-metier.dll] ve [3] dosyaları üretilecektir.
Proje oluşturuldu (F6).
9.8.5. [ui] katmanı
![]() |
![]() |
- [1]'te, [ui] projesi, çözümün aktif projesi haline gelmiştir
- [2]'te: proje referansları. DLL'teki [dao] ve [metier] katmanlarının referanslarına dikkat edilmelidir.
- [3]'te: [ui] katmanı. Bu, 7.6. paragrafında açıklanan sürüm 6'ya aittir.
- [4]'te, [App.config] yapılandırma dosyası sürüm 6'dakine benzerdir. Aradaki tek fark, [dao] katmanının Spring tarafından örneklenme şeklidir:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<sectionGroup name="spring">
<section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core" />
<section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
</sectionGroup>
</configSections>
<spring>
<context>
<resource uri="config://spring/objects" />
</context>
<objects xmlns="http://www.springframework.net">
<object name="dao" type="Dao.DataBaseImpot, ImpotsV7-dao">
<constructor-arg index="0" value="MySql.Data.MySqlClient"/>
<constructor-arg index="1" value="Server=localhost;Database=bdimpots;Uid=admimpots;Pwd=mdpimpots;"/>
<constructor-arg index="2" value="select limite, coeffr, coeffn from tranches"/>
</object>
<object name="metier" type="Metier.ImpotMetier, ImpotsV7-metier">
<constructor-arg index="0" ref="dao"/>
</object>
</objects>
</spring>
</configuration>
- 11-25. satırlar: Spring yapılandırması
- 15-24. satırlar: Spring tarafından örneklenen nesneler
- satır 16-20: [dao] katmanının örneklenmesi
- 16. satır: [dao] katmanı, DLL içinde bulunan [Dao.DataBaseImpot] sınıfı tarafından örneklenir [ImpotsV7-Dao]
- 17-19. satırlar: [Dao.DataBaseImpot] sınıfının oluşturucusuna sağlanacak üç parametre (kullanılan SGBD'in factory'si, bağlantı dizesi, SQL isteği)
- 21-23. satırlar: [metier] katmanının örneklenmesi. Bu, sürüm 6'dakiyle aynı yapılandırmadır.
Testler
[ui] projesi şu şekilde yapılandırılmıştır:
![]() |
- [1]: proje türü "Windows Uygulaması"dır
- [2]: Projenin derlenmesi sonucunda [ImpotsV7-ui.exe] yürütülebilir dosyası oluşturulacaktır
[3] dosyasında bir çalıştırma örneği verilmiştir.
9.8.6. Veritabanını değiştirme
![]() |
Yukarıdaki [dao] katmanı, genel bir bağlayıcı ve MySQL5 tabanı kullanılarak yazılmıştır. Burada, yalnızca yapılandırmanın değişeceğini göstermek amacıyla SQL Server Compact tabanına geçmeyi planlıyoruz.
'in SQL Server Compact tabanı şu şekilde olacaktır:
![]() |
- [1]: Visual Studio'daki [DataBase Explorer] görünümünde yer alan [dbimpots.sdf] veritabanı. Bu veritabanı şifresiz olarak oluşturulmuştur.
- [3]: Verileri içeren [data] tablosu. MySQL5 veritabanında kullanılanlardan farklı tablo ve sütun adları kasıtlı olarak seçilmiştir; bunun amacı, bu tür ayrıntıların kod yerine yapılandırma dosyasına eklenmesinin önemini bir kez daha vurgulamaktır.
- [4]: [id] sütunu birincil anahtardır ve Identity özniteliğine sahiptir; bu sütuna değerleri atayacak olan ise SGBD'tir.
- [5]: [data] tablosunun içeriği.
![]() |
- [6]: [dbimpots.sdf] veritabanı, [ui] proje klasörüne yerleştirildi ve bu projeye entegre edildi.
- [7]: [dbimpots.sdf] veritabanı, projenin çalıştırma klasörüne kopyalanacaktır.
Yeni veritabanı için [App.config] yapılandırma dosyası şu şekildedir:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<sectionGroup name="spring">
<section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core" />
<section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
</sectionGroup>
</configSections>
<spring>
<context>
<resource uri="config://spring/objects" />
</context>
<objects xmlns="http://www.springframework.net">
<!--
<object name="dao" type="Dao.DataBaseImpot, ImpotsV7-dao">
<constructor-arg index="0" value="MySql.Data.MySqlClient"/>
<constructor-arg index="1" value="Server=localhost;Database=bdimpots;Uid=admimpots;Pwd=mdpimpots;"/>
<constructor-arg index="2" value="select limite, coeffr, coeffn from tranches"/>
</object>
-->
<object name="dao" type="Dao.DataBaseImpot, ImpotsV7-dao">
<constructor-arg index="0" value="System.Data.SqlServerCe.3.5"/>
<constructor-arg index="1" value="Data Source=|DataDirectory|\dbimpots.sdf;" />
<constructor-arg index="2" value="select data1, data2, data3 from data"/>
</object>
<object name="metier" type="Metier.ImpotMetier, ImpotsV7-metier">
<constructor-arg index="0" ref="dao"/>
</object>
</objects>
</spring>
</configuration>
- 23-27. satırlar: [dao] katmanının, [dbimpots.sdf] veritabanını kullanmak üzere yapılandırılması.
Çalıştırma sonuçları öncekilerle aynıdır. [dao] katmanının SGBD'teki değişikliklerden etkilenmemesi için genel bir konektör kullanmanın faydası dikkat çekicidir. Ancak bu konektörün, özellikle parametreli sorguların kullanıldığı durumlar dahil olmak üzere, her duruma uygun olmadığını gördük. Bu durumda, daha önce bahsedilen gibi, üçüncü taraf veri erişim çerçeveleri (Spring, iBatis, NHibernate, LINQ, ...) gibi başka çözümler de mevcuttur.
9.9. Daha fazla bilgi için...
- LINQ, birçok kitapta ele alınmaktadır; özellikle de bu belgenin giriş bölümünde daha önce bahsedilen O'Reilly yayınevinden çıkan C# 3.0 in a Nutshell, Joseph ve Ben Albahari adlı kitapta yer almaktadır.
- iBatis, Clinton Begin tarafından yazılan ve Manning yayınevi tarafından basılan “iBatis in Action” adlı kitapta ele alınmaktadır.
- Manning yayınevi tarafından yayınlanacak olan "Nhibernate in Action" kitabının Temmuz 2008'de çıkması planlanmaktadır
Spring, iBatis ve NHibernate için, bu farklı çerçevelerin web sitelerinde referans kılavuzları mevcuttur.
































































