Skip to content

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:

  1. bağlı mod
  2. çevrimdışı mod

Bağlı modda, uygulama

  1. veri kaynağıyla bir bağlantı kurar
  2. veri kaynağıyla okuma/yazma işlemlerini gerçekleştirir
  3. bağlantıyı kapatır

Çevrimdışı modda, uygulama

  1. veri kaynağıyla bir bağlantı açar
  2. kaynağın verilerinin tamamının veya bir kısmının bellek kopyasını alır
  3. bağlantıyı kapatır
  4. verilerin bellek kopyası üzerinde okuma/yazma işlemlerini gerçekleştirir
  5. 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:

select col1, col2,... 
from table1, table2,...
where condition
order by expression
...
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.
  1. "from" anahtar sözcüğünün ardından gelen tüm tablolarla birleştirme yapılır
  2. Yalnızca "select" anahtar sözcüğünün ardından gelen sütunlar tutulur
  3. Yalnızca "where" anahtar sözcüğündeki koşulu karşılayan satırlar tutulur
  4. "order by" anahtar sözcüğündeki ifadeye göre sıralanan satırlar, sorgunun sonucunu oluşturur. Bu sonuç bir tablodur.
insert into table(col1,col2, ...)
values (val1,val2, ...)
Tabloya bir satır ekler. (col1, col2, ...) ifadesi, (val1, val2, ...) değerleriyle doldurulacak satırın sütunlarını belirtir.
update table
set col1=val1, col2=val2
where condition
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
delete from table
where condition
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.

Chaîne de connexion à la base : [Data Source=|DataDirectory|\dbarticles.sdf;Password=dbarticles;Persist Security Info=True]

Requête SQL (rien pour arrêter) : select id,nom,prix,stockactuel,stockminimum from articles

------------------------------------
ID,NOM,PRIX,STOCKACTUEL,STOCKMINIMUM
------------------------------------

1 vélo 500 10 5
2 pompe 10 10 2
3 arc 600 4 1
4 flèches - lot de 6 100 12 20
5 combinaison de plongée 300 8 2
6 bouteilles d'oxygène 120 10 5

Requête SQL (rien pour arrêter) : insert into articles(nom,prix,stockactuel,stockminimum) values('x',100,10,1)
Il y a eu 1 ligne(s) modifiée(s)

Requête SQL (rien pour arrêter) : select id,nom,prix,stockactuel,stockminimum from articles

------------------------------------
ID,NOM,PRIX,STOCKACTUEL,STOCKMINIMUM
------------------------------------

1 vélo 500 10 5
...
6 bouteilles d'oxygène 120 10 5
9 x 100 10 1

Requête SQL (rien pour arrêter) : update articles set prix=prix*1.1 where id=9
Il y a eu 1 ligne(s) modifiée(s)

Requête SQL (rien pour arrêter) : select id,nom,prix,stockactuel,stockminimum from articles

------------------------------------
ID,NOM,PRIX,STOCKACTUEL,STOCKMINIMUM
------------------------------------

1 vélo 500 10 5
...
6 bouteilles d'oxygène 120 10 5
9 x 110 10 1

Requête SQL (rien pour arrêter) : delete from articles where id=9
Il y a eu 1 ligne(s) modifiée(s)

Requête SQL (rien pour arrêter) : select id,nom,prix,stockactuel,stockminimum from articles

------------------------------------
ID,NOM,PRIX,STOCKACTUEL,STOCKMINIMUM
------------------------------------

1 vélo 500 10 5
...
6 bouteilles d'oxygène 120 10 5
  • 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:

  1. veri kaynağıyla bir bağlantı kurar
  2. veri kaynağıyla okuma/yazma işlemlerini gerçekleştirir
  3. 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
ConnectionString
P
veritabanına bağlantı dizesi. Belirli bir veritabanıyla bağlantı kurulması için gerekli tüm parametreleri belirtir.
Open
M
ConnectionString ile tanımlanan veritabanına bağlantıyı açar
Close
M
bağlantıyı kapatır
BeginTransaction
M
işlemi başlatır.
State
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:

1
2
3
IDbConnection connexion=new Connection();
connexion.ConnectionString=...;
connexion.Open();

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
CommandType
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
CommandText
P
- CommandType = CommandType.Text ise çalıştırılacak SQL komutunun metni
- CommandType = CommandType.StoredProcedure ise çalıştırılacak saklı yordamın adı
Connection
P
SQL emrini yürütmek için kullanılacak IDbConnection bağlantısı
Transaction
P
SQL emrini yürütmek için kullanılacak IDbTransaction işlemi
Parameters
P
parametreleri ayarlanmış bir SQL emrinin parametre listesi. "update articles set price=price*1.1 where id=@id" emrinin @id parametresi vardır.
ExecuteReader
M
SQL Select komutunu çalıştırmak için. Select komutunun sonucunu temsil eden bir IDataReader nesnesi elde edilir.
ExecuteNonQuery
M
SQL Güncelleme, Ekleme, Silme komutunu yürütmek için. İşlemden etkilenen satır sayısı (güncellenen, eklenen, silinen) elde edilir.
ExecuteScalar
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.
CreateParameter
M
SQL emrinin parametrelerini oluşturmak için IDbParameter.
Prepare
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:

// bağlantı açılıyor 
IDbConnection connexion=...
connexion.Open();
// komut hazırlığı
IDbCommand commande=new Command();
commande.Connection=connexion;
// select komutunun yürütülmesi
commande.CommandText="select ...";
IDbDataReader reader=commande.ExecuteReader();
...
// update, insert, delete komutlarının yürütülmesi
commande.CommandText="insert ...";
int nbLignesInsérées=commande.ExecuteNonQuery();
...
// bağlantı kapatma
connexion.Close();

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
FieldCount
P
IDataReader tablosundaki sütun sayısı
GetName
M
GetName(i), IDataReader tablosundaki i numaralı sütunun adını döndürür.
Item
P
Item[i], IDataReader tablosunun geçerli satırındaki i numaralı sütunu temsil eder.
Read
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.
Close
M
IDataReader tablosunu kapatır.
GetBoolean
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.
Getvalue
M
Getvalue(i): IDataReader tablosunun geçerli satırındaki i numaralı sütunun değerini object türü olarak döndürür.
IsDBNull
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:

// bağlantı açma 
IDbConnection connexion=...
connexion.Open();
// komut hazırlığı
IDbCommand commande=new Command();
commande.Connection=connexion;
// select emrinin yürütülmesi
commande.CommandText="select ...";
IDataReader reader=commande.ExecuteReader();
// sonuçların değerlendirilmesi
while(reader.Read()){
     // geçerli satırı işleme
        ...
}
// okuyucuyu kapatma
reader.Close();
// bağlantıyı kapatma
connexion.Close();

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:

// bağlantı başlatma
IDbConnection connexion=...
// bağlantı işleme
try{
     // açma
    connexion.Open();
     // komut hazırlığı
    IDbCommand commande=new Command();
    commande.Connection=connexion;
     // seçim emrinin yürütülmesi
    commande.CommandText="select ...";
    IDbDataReader reader=commande.ExecuteReader();
     // sonuçların işlenmesi
    try{
        while(reader.Read()){
         // geçerli satırı işleme
        ...
    }finally{
         // okuyucuyu kapatma
        reader.Close();
    }
}catch(Exception ex){
     // istisna yönetimi
    ...
}finally{
     // bağlantıyı kapatma
    connexion.Close();
}
...

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:

// bağlantı işleme
try{
    using(IDbConnection connexion=...){
         // açma
        connexion.Open();
         // sipariş hazırlama
        IDbCommand commande=new Command();
        commande.Connection=connexion;
         // seçim emrinin yürütülmesi
        commande.CommandText="select ...";
        using(IDbDataReader reader=commande.ExecuteReader()){
             // sonuçların işlenmesi
            while(reader.Read()){
             // geçerli satırı işleme
            ...
        }// IData kullanılarak
    }//IDbConnection kullanılarak
}catch(Exception ex){
     // istisna yönetimi
    ...
}
..
  • 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:

  1. Veritabanına bağlanma
  2. SQL komutlarının veritabanına gönderilmesi
  3. SQL komutunun sonuçlarının işlenmesi
  4. Bağlantının kapatılması
  1. 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:
/* geçerli veritabanının seçilmesi */
USE dbarticles;
/* ürün tablosunun oluşturulması */
CREATE TABLE ARTICLES (
    ID            INTEGER PRIMARY KEY AUTO_INCREMENT,
    NOM           VARCHAR(20) NOT NULL,
    PRIX          DOUBLE PRECISION NOT NULL,
    STOCKACTUEL   INTEGER NOT NULL,
    STOCKMINIMUM  INTEGER NOT NULL
);
/* tabloya veri ekleme */
INSERT INTO ARTICLES (NOM, PRIX, STOCKACTUEL, STOCKMINIMUM) VALUES ('article1', 100, 10, 1);
INSERT INTO ARTICLES (NOM, PRIX, STOCKACTUEL, STOCKMINIMUM) VALUES ('article2', 200, 20, 2);
INSERT INTO ARTICLES (NOM, PRIX, STOCKACTUEL, STOCKMINIMUM) VALUES ('article3', 300, 30, 3);
/* kısıtlamaların eklenmesi */
ALTER TABLE ARTICLES ADD CONSTRAINT CHK_ID check (ID>0);
ALTER TABLE ARTICLES ADD CONSTRAINT CHK_PRIX check (PRIX>0);
ALTER TABLE ARTICLES ADD CONSTRAINT CHK_STOCKACTUEL check (STOCKACTUEL>0);
ALTER TABLE ARTICLES ADD CONSTRAINT CHK_STOCKMINIMUM check (STOCKMINIMUM>0);
ALTER TABLE ARTICLES ADD CONSTRAINT CHK_NOM check (NOM<>'');
ALTER TABLE ARTICLES ADD CONSTRAINT UNQ_NOM UNIQUE (NOM);
  • 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>
  1. 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:

espace de noms
MySql.Data.MySqlClient
classe Connection
MySqlConnection
classe Command
MySqlCommand
classe DataReader
MySqlDataReader

Program, [App.config] dosyasındaki dbArticlesMySql5 adlı bağlantı dizesini kullanır. Çalıştırıldığında şu sonuçlar elde edilir:

Chaîne de connexion à la base : [Server=localhost;Database=dbarticles;Uid=root;Pwd=root;]

Requête SQL (rien pour arrêter) : select * from articles

------------------------------------
ID,NOM,PRIX,STOCKACTUEL,STOCKMINIMUM
------------------------------------

1 article1 100 10 1
2 article2 200 20 2
3 article3 300 30 3

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:

espace de noms
System.Data.Odbc
classe Connection
OdbcConnection
classe Command
OdbcCommand
classe DataReader
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:

Chaîne de connexion à la base : [Driver={SQL Native Client};Server=.\SQLExpress;AttachDbFilename=|DataDirectory|\dbarticles.mdf;Uid=sa;Pwd=msde;]

Requête SQL (rien pour arrêter) : select * from articles

------------------------------------
id,nom,prix,stockactuel,stockminimum
------------------------------------

1 vélo 500,0000 10 5
2 pompe 10,0000 10 2
3 arc 610,0000 4 1
4 flèches - lot de 6 100,0000 12 20
5 combinaison de plongée 300,0000 8 2
6 Bouteilles d'oxygène 120,0000 10 5

[dbArticlesOdbcMySql5] bağlantı dizesiyle:

Chaîne de connexion à la base : [Driver={MySQL ODBC 3.51 Driver};Server=localhost;Database=dbarticles; User=root;Password=root;]

Requête SQL (rien pour arrêter) : select * from articles

------------------------------------
ID,NOM,PRIX,STOCKACTUEL,STOCKMINIMUM
------------------------------------

1 article1 100 10 1
2 article2 200 20 2
3 article3 300 30 3

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:

espace de noms
System.Data.OleDb
classe Connection
OleDbConnection
classe Command
OleDbCommand
classe DataReader
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:

Chaîne de connexion à la base : [Provider=Microsoft.Jet.OLEDB.4.0;Data Source=|DataDirectory|\dbarticles.mdb;]

Requête SQL (rien pour arrêter) : select * from articles

------------------------------------
id,nom,prix,stockactuel,stockminimum
------------------------------------

1 vélo 1202 5 2
2 arc 5000 10 2
3 canoé 1502 12 6
4 fusil 3000 10 2
5 skis nautiques 1800 5 2
6 essai3 3 3 3
7 cachalot 200000 1 0
8 léopard 500000 1 1
9 panthère 800000 1 1

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:

Name|Description|InvariantName|AssemblyQualifiedName|
---------------------------------------
Odbc Data Provider|.Net Framework Data Provider for Odbc|System.Data.Odbc|System.Data.Odbc.OdbcFactory, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089|
---------------------------------------
OleDb Data Provider|.Net Framework Data Provider for OleDb|System.Data.OleDb|System.Data.OleDb.OleDbFactory, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089|
---------------------------------------
OracleClient Data Provider|.Net Framework Data Provider for Oracle|System.Data.OracleClient|System.Data.OracleClient.OracleClientFactory, System.Data.OracleClient, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089|
---------------------------------------
SqlClient Data Provider|.Net Framework Data Provider for SqlServer|System.Data.SqlClient|System.Data.SqlClient.SqlClientFactory, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089|
---------------------------------------
Microsoft SQL Server Compact Data Provider|.NET Framework Data Provider for Microsoft SQL Server Compact|System.Data.SqlServerCe.3.5|System.Data.SqlServerCe.SqlCeProviderFactory, System.Data.SqlServerCe, Version=3.5.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91|
---------------------------------------
MySQL Data Provider|.Net Framework Data Provider for MySQL|MySql.Data.MySqlClient|MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Version=5.2.1.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d|
  • 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
Odbc Data Provider
System.Data.Odbc
OleDb Data Provider
System.Data.OleDb
OracleClient Data Provider
System.Data.OracleClient
SqlClient Data Provider
System.Data.SqlClient
Microsoft SQL Server Compact Data Provider
System.Data.SqlServerCe.3.5
MySQL Data Provider
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:

Provider factory : [MySql.Data.MySqlClient]
Chaîne de connexion à la base : [Server=localhost;Database=dbarticles;Uid=root;Pwd=root;]

Requête SQL (rien pour arrêter) : select * from articles

------------------------------------
ID,NOM,PRIX,STOCKACTUEL,STOCKMINIMUM
------------------------------------

1 article1 100 10 1
2 article2 200 20 2
3 article3 300 30 3

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:

Provider factory : [System.Data.SqlServerCe.3.5]
Chaîne de connexion à la base : [Data Source=|DataDirectory|\dbarticles.sdf;Password=dbarticles;]

Requête SQL (rien pour arrêter) : select * from articles

------------------------------------
ID,NOM,PRIX,STOCKACTUEL,STOCKMINIMUM
------------------------------------

1 vélo 500 10 5
2 pompe 10 10 2
3 arc 600 4 1
4 flèches - lot de 6 100 12 20
5 combinaison de plongée 300 8 2
6 bouteilles d'oxygène 120 10 5

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:

insert into articles(nom,prix,stockactuel,stockminimum) values(@nom,@prix,@sa,@sm)

MySQL5 ile aynı sorgu şu şekilde yazılır:

insert into articles(nom,prix,stockactuel,stockminimum) values(?,?,?,?)

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:

Parameters
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

insert into articles(nom,prix,stockactuel,stockminimum) values(@nom,@prix,@sa,@sm)

çünkü bu emirde adlandırılmış parametreler bulunmaktadır. ParameterName özelliği kullanılabilir.

IDataParameter türü, SQL

insert into articles(nom,prix,stockactuel,stockminimum) values(?,?,?,?)

çü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:

insert into articles(nom,prix,stockactuel,stockminimum) values(?,?,?,?)

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
BigInt
Int64
 
DateTime
DateTime
 
Decimal
Decimal
 
Float
Çift
 
Int
Int32
 
Money
Ondalık
 
NChar
Dize
sabit uzunluklu dize
NVarChar
Dize
değişken uzunlukta dize
Real
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:

insert into articles(nom,prix,stockactuel,stockminimum) values(@nom,@prix,@sa,@sm)

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:

Chaîne de connexion à la base : [Data Source=|DataDirectory|\dbarticles.sdf;Password=dbarticles;]

Il y a eu 5 ligne(s) modifiée(s)

------------------------------------
ID,NOM,PRIX,STOCKACTUEL,STOCKMINIMUM
------------------------------------


------------------------------------
ID,NOM,PRIX,STOCKACTUEL,STOCKMINIMUM
------------------------------------

117 article1 100 10 1
118 article2 200 20 2
119 article3 300 30 3
120 article4 400 40 4
121 article5 500 50 5
  • 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:

  1. 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.
  2. 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:

  1. U1 adlı bir kullanıcı, T tablosu üzerinde bir işlem başlatır
  2. U2 adlı bir kullanıcı aynı T tablosu üzerinde bir işlem başlatır
  3. U1 adlı kullanıcı, T tablosundaki satırları değiştirir ancak henüz onaylamaz
  4. U2 kullanıcısı bu değişiklikleri "görür" ve gördüklerine dayanarak kararlar alır
  5. kullanıcı, ROLLBACK komutuyla işlemini iptal eder
  1. 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:

  1. U1 adlı bir kullanıcı, T tablosu üzerinde bir işlem başlatır
  2. U2 adlı bir kullanıcı aynı T tablosunda bir işlem başlatır
  3. 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
  4. kullanıcı U1, T tablosunun C sütunundaki belirli değerleri değiştirir (UPDATE) ve bunları onaylar (COMMIT)
  5. 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:

BeginTransaction
M
bir işlemi başlatır.

Bu yöntemin iki imzası vardır:

  1. IDbTransaction BeginTransaction(): bir işlem başlatır ve bu işlemi kontrol etmeyi sağlayan IDbTransaction nesnesini döndürür
  2. 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:
ReadUncommitted
işlem, henüz onaylamadığı başka bir işlem tarafından yazılmış verileri okuyabilir – kaçınılmalıdır
ReadCommitted
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.
RepeatableRead
İş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.
Serializable
İş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.
Snapshot
İş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
Connection
P
IDbConnection bağlantısı, şu işlemi destekler:
Commit
M
işlemi onaylar - işlemde verilen SQL emirlerinin sonuçları veritabanına kopyalanır.
Rollback
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:

Chaîne de connexion à la base : [Data Source=|DataDirectory|\dbarticles.sdf;Password=dbarticles;]

Insertion sans transaction...
Il y a eu 0 ligne(s) modifiée(s)
Erreur d'accès à la base de données (A duplicate value cannot be inserted into a unique index. [ Table name = ARTICLES,Constraint name = UQ__ARTICLES__0000000000000010 ])

------------------------------------
ID,NOM,PRIX,STOCKACTUEL,STOCKMINIMUM
------------------------------------

126 article 100 10 1


Insertion dans une transaction...
Il y a eu 1 ligne(s) modifiée(s)
transaction invalidée...

------------------------------------
ID,NOM,PRIX,STOCKACTUEL,STOCKMINIMUM
------------------------------------
  • 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ı:

ExecuteScalar
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:

Chaîne de connexion à la base : [Data Source=|DataDirectory|\dbarticles.sdf;Password=dbarticles;]

Il y a eu 5 ligne(s) modifiée(s)
transaction validée...

------------------------------------
ID,NOM,PRIX,STOCKACTUEL,STOCKMINIMUM
------------------------------------

145 article1 100 10 1
146 article2 200 20 2
147 article3 300 30 3
148 article4 400 40 4
149 article5 500 50 5
Prix moyen des articles=300
Nombre d'articles=5
  1. 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);
            }
        }
    }
}
  1. 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:

Image

Testin çalıştırılması aşağıdaki sonuçları vermektedir:

1
2
3
4
5
6
7
4962:0:0
8382:0,068:291,09
14753:0,191:1322,92
23888:0,283:2668,39
38868:0,374:4846,98
47932:0,426:6883,66
0:0,481:9505,54

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.