Fırat Esmer

Microsoft Yazılımları

DataTable'da Aggregate Fonksiyon Kullanma (Compute Metodu)

ADO.NET'in sunmuş olduğu başka bir güzellikle karşınızdayım. Bu sefer DataTable üzerinden Aggregate Function'ları kullanacağız. Tam olarak hangi işlemleri, nasıl ve neden yapalım? Şimdi öğreneceksiniz.

Senaryo : DataTable nesnesinin Compute metodunda kullanabileceğimiz aggregate fonksiyonları, Northwind veritabanının Products tablosundaki UnitsInStock ve UnitPrice kolonları üzerinde örneklendireceğim.

Bilinmesi Gerekenler :

  • Makaledeki örnek uygulama .NET Framework 4.0 (Windows Form) ve Visual Studio 2010 ile hazırlandı,
  • Makaledeki örnek uygulamada DataTable'ı doldurmak için Northwind'i kullandım. Eğer Northwind veritabanına sahip değilseniz, örnek uygulamanın çalışması için buradan indirebilirsiniz. Northwind veritabanını kullanma sebebim genelde yazılımcılarda mevcut olması. Projeye eklememe sebebim ise indireceğiniz örnek uygulamanın boyutunu küçük tutmak.

Neler Öğreneceksiniz :

  • DataTable nesnesinin Compute metodunu nasıl ve neden kullanmanız gerektiğini.

İlk olarak DataTable'ın Compute metoduyla hangi aggregate fonksiyonları kullanabiliyoruz ona bakalım.

  1. Sum - Toplam değer,
  2. Avg - Ortalama değer,
  3. Min - En küçük değer,
  4. Max - En büyük değer,
  5. Count - Kayıt sayısı,
  6. Var - Varyans değer,
  7. Stdev - Standart sapma değer.

Kullanımına gelirsek, bilmeniz gereken tek şey metot 2 tane parametre alıyor:

public object Compute(string expression, string filter);

İlk parametre kullanmak istediğiniz fonksiyonu, ikincisi ise filtre tanımı. Kullanmak istediğiniz fonksiyonun SQL'deki kullanımından hiçbir farkı yok. Filtre dediğim kısım ise "CategoryID='2'" yazdığımızda ID'si 2 olan verileri getirecektir. Örnek vereyim :

SqlDataAdapter adapter = new SqlDataAdapter("select UnitPrice,CategoryID from Products", "server=.; database=northwind; integrated security=sspi");

DataTable table = new DataTable();

adapter.Fill(table);

string result = table.Compute("SUM(UnitPrice)","CategoryID='2'").ToString();
MessageBox.Show(result);

adapter.Dispose();
table.Dispose();

Yukarıdaki örnekte Products tablosundan UnitPrice ve CategoryID verilerini çektik. Daha sonra CategoryID'si 2 olan verilerin UnitPrice'larını topladık. SQL'e sorgu yazmaktan bir farkı yok ve eğer denemek isterseniz aşağıdaki sonuçla karşılaşıcaksınız. Eğer Northwind veritabanında oynama yapmadıysanız :)

Sonuç

Eğer filtre uygulamak istemiyorsanız yapmanız gereken tek şey metodun ikinci parametresini boş bırakmak. Hepsi bu.

Gelelim ne gibi yerlerde kullanabiliriz? Eğer veritabanının bir tablosunu sayfanın/uygulamanın açılış anında dolduruyorsanız ve bu tablo üzerinden işlem yapıyorsanız bir kereliğe mahsus DataTable nesnesi üzerinde tutarsınız (Sınıf düzeyinde tanımlanmış bir DataTable) bu DataTable nesnesi üzerinden işlemleri gerçekleştirebilirsiniz. Böylece veritabanı ile bağlantı kurmaktan da kurtulursunuz.

Not :

1 - Eğer bilinmeyen bir aggregate fonksiyonu kullanırsanız aşağıdaki gibi hata alırsınız.

Hata

The expression contains undefined call X(). Hatanın Türkçesi : "İfade tanımlanmamış bir işlev çağrısı içeriyor: X()"

2 - Eğer mantıksal hata yaparsanız mesela ProductName kolonu üzerinde SUM gibi toplama fonksiyonu kullanmak isterseniz, hata alırsınız. Sebebi veri tipinin string olması ve toplayamaması. Alacağınız hata da şöyle olacaktır.

Hata

Invalid usage of aggregate function Sum() and Type: String. Hatanın Türkçesi : "Geçersiz Sum() toplam fonksiyonu ve Tür kullanımı: String."

İçerisinde tüm işlemleri barındıran ve filtrenin opsiyonel olduğu örnek uygulamamı indirmek isterseniz buraya tıklayın.

Internet Explorer ile FileUpload'da JPEG Sorunu

Internet Explorer 6, 7 veya 8 sürümlerinde FileUpload kontrolü ile yüklenmek istenen dosya jpeg uzantılı dosya ise dikkat etmeniz gereken noktalara değineceğim (Internet Explorer 6, 7 veya 8 diyorum çünkü 6'dan önceki sürümleri hesaba katmıyorum).

Senaryo : Bir FileUpload, bir Label ve bir Button'dan oluşan sayfamızda, FileUpload ile jpeg uzantılı dosya yüklemeye çalışacağız ve bu yüklenen dosyanın Internet Explorer 6, 7 veya 8 ile Internet Explorer 9 arasında çıkaracağı farklılığı gidereceğiz. Ayrıca yüklenmek istenen dosya türünün tarayıcı tarafından nasıl okunduğunu göreceksiniz (Label'da gösterilecek).

Bilinmesi Gerekenler :

  • Makaledeki örnek uygulama .NET Framework 4.0 (Web Application) ve Visual Studio 2010 ile hazırlanmıştır.

Neler Öğreneceksiniz :

  • Eğer FileUpload kontrolü ile jpeg uzantılı dosyaları yüklemeden önce dosyanın jpeg türü olup olmadığını kontrol edecekseniz, Internet Explorer sürüm farklılıklarında ne yapmanız gerekeceğini öğreneceksiniz.

FileUpload kontrolü ile yükleme yapılmak istenen dosyanın türünü öğrenmek için FileUpload.PostedFile.ContentType yazmamız yeterli. Ancak yüklenmek istenen dosyanın jpeg uzantılı olup olmadığını bir koşul ile kontrol ederken şunu sakın aklınızdan çıkarmayın. Internet Explorer bilmediğiniz bir şey yapıyor. O da jpeg uzantısını pjpeg olarak görmesidir. Nasıl mı? Şöyle :

if (fileUpload_jpeg.HasFile)
{
  lbl_fileContent.Text = String.Format("Upload ettiğiniz dosyanın türü : {0}", fileUpload_jpeg.PostedFile.ContentType);
  lbl_fileContent.ForeColor = Color.Green;
}

Yukarıdaki kod parçacığını Internet Explorer 9 aracılığıyla çalıştırdığımda çıkacak sonuç şöyledir.

Internet Explorer 9 ile Çıkan Sonuç

Yüklemek istediğimiz dosyanın türü image/jpeg olarak algılandı. Yani çalıştığınız yerde yetkili kişi size "sadece resim yüklenebilsin" der ve siz de önlem almak amacıyla fileUpload_jpeg.PostedFile.ContentType == "image/jpeg" koşulunu koyarsanız, Internet Explorer 6, 7 veya 8 ile hazırlamış olduğunuz siteye giren kişiler jpeg uzantılı dosya yükleyemeyecektir. Neden mi?

Internet Explorer 6, 7 veya 8 ile Çıkan Sonuç

Görmüş olduğunuz gibi Internet Explorer 6,7 veya 8 ile hazırlamış olduğunuz siteye giren kullanıcılar, türü jpeg olan dosyalardan yüklemek isterlerse başarısız olacaklardır. Çünkü yaptıkları işlem sizin belirlemiş olduğunuz "image/jpeg" koşuluna uymayacaktır. O yüzden "image/jpeg" koşuluna ek olarak "image/pjpeg" koşulunu da ekleyin. Şunu da unutmayın bu sıkıntı sadece Internet Explorer (6, 7, 8) için geçerlidir. Internet Explorer 9 için geçerli değildir.

Eğer hazırlamış olduğum örnek uygulamayı indirmek isterseniz buraya tıklayın.

Visual Studio "All-In-One Code Framework" Eklentisi

Microsoft All-In-One Code FrameworkMicrosoft All-In-One Code Framework, içerisinde çeşitli kriterlere göre gruplandırılmış kod örnekleri sunan bir uygulamadır. Çoğu yazılımcıların sıkıntısı konuyu bildikleri halde bildiklerini koda dökemezler ve örnek uygulama ararlar. Eğer siz de böyle sıkıntı ile karşılaşıyor ya da öğrenmek amacıyla farklı uygulamalar görmek istiyorsanız makalemi okumanızı tavsiye ederim.

All-In-One Code Framework'ün içerisinde barındırdığı örnek uygulamaların kategorilerini belirtmek istiyorum. Öncelikle Visual Studio 2008 ve Visual Studio 2010 destekli uygulamalar olduğunu belirteyim. Diller: C#, C++, VB ve Others. Teknolojiler ise ADO, ADO.NET, ADSI, ASP.NET, ATL, COM, Diagnostics, Entity Framework, File System, IE, IIS, LINQ, Library, MFC, Network, Office, Silverlight, SQL Server, TFS, VSX, WCF, WF, Windows 7, Windows Azure, Windows Forms, Windows General, Windows Phone, Windows Security, Windows Service, Windows Shell, Windows UI, WMI, WPF ve son olarak XML. İşin güzel kısmı her gün yeni uygulamalar ekleniyor çünkü uygulamaları Microsoft değil, kullanıcılar ekliyor (Şu an 679 adet uygulama var). Tabii ki gönderdiğiniz uygulamarın belli kriterlere sahip olması lazım. Son olarak ücretsiz olduğunu belirteyim.

Uygulamayı nasıl indireceğimize ve nasıl kullanıcağımıza bakalım. Eskiden All-In-One Code Framework uygulama değil, içerisinde uygulamarı barındıran bir klasörden ibaretti. Klasör mantığı çok yanlış ve zahmetliydi. Çünkü istediğiniz uygulamayı değil direkt olarak hepsini veya teknoloji bazında indiriyordunuz ve yeni gelen uygulamalar için klasörü tekrar tekrar indirmeniz gerekiyordu ve klasörün şu anki boyutu 55 MB. Şimdi ise tüm uygulamaları bir sunucuya koymuşlar ve hazırlamış oldukları program ile arama yaparak sadece istediğimiz uygulamaları programı yüklerken belirlemiş olduğumuz klasöre indirebiliyoruz. Hazırlamış oldukları programın ismi ise All-In-One Code Framework Sample Browser ve indirebileceğiniz 3 adres mevcut. Bunlardan ilki CodePlex sitesi, ikincisi Visual Studio Gallery ve üçüncüsü Visual Studio'daki Extension Manager'dan indirmek. Ben üçüncü yolu seçeceğim çünkü Visual Studio Extension Manager kullanımını da bilmeyen kişiler için faydalı olacaktır. Ayrıca Extension Manager kullanmaya alışsanız iyi olacaktır.

1 - İlk olarak Visual Studio programının menüsünden Tools -> Extension Manager... sekmesine tıklayın.

 Extension Manager

2 - Extension Manager sekmesine tıkladıktan sonra karşınıza çıkacak ekranda, sol taraftaki menüden Online Gallery'i seçin ve arama kısmına all-in yazın. Karşınıza 2 tane eklenti çıkacaktır. Üsttekini seçerek Download'a basın. 

Online Gallery

3 - Download'a bastıktan sonra yaklaşık 888 KB boyutunda eklentiyi indirip, kullanım koşullarını okumakla beraber yükledikten sonra sizden Visual Studio'yu yeniden başlatmanızı isteyecektir.

4 - Visual Studio yeniden başlatıldıktan sonra Tools -> Search Code Sample sekmesini tıklayarak uygulamayı başlatabilirsiniz. 

Search Code Sample

5 - Search Code Sample sekmesine tıkladığınızda bir kere mahsus karşınıza indirmek istediğiniz uygumaları nereye kaydetmek isteyeceğinizi soracak ekran gelecektir.

Select Folder

6 - Select Folder'a tıklayarak istediğiniz dizini seçerek Save' basın. Artık örnek uygulamalar indirilmeye hazır. All-In-One code Framework Sample Browser'ın son görüntüsüne bakmak isterseniz buraya tıklayın.

İndireceğiniz uygulamalar Visual Studio 2008 ve visual Studio 2010 dizinleri şeklinde seçtiğiniz dizinde arşivlenecektir. Umarım programdan yararlanırsınız.

ADO.NET Bağlantı Havuzu

Herhangi bir veritabanı ile ilgili bir işlem için açılan bağlantı, sunucu tarafında oturum açılmasına sebep olur. Açılan her bağlantı yeni bir istek, her istek yük anlamına gelir. Birazdan okuyacağınız makalede tüm isteklerin bir bağlantıdan gönderilmesi (connection pooling - bağlantı havuzu) ile her isteğin ayrı ayrı bağlantılar üzerinden gönderilmesi arasındaki farkı ve avantajları göreceksiniz.

Senaryo : Veritabanına, içerisinde pooling özelliği true ve false olan 2 farklı bağlantı açacağım. Bize ne kadarlık süreye mal olacağını ve SQL Server Profiler'da nasıl görüneceğini göstereceğim.

Bilinmesi Gerekenler :

  • Makaledeki örnek .NET Framework 4.0 (Windows Form) ve Visual Studio 2010 ile hazırlandı,
  • Makalede Northwind veritabanı üzerinden bağlantı açacağım. Eğer Northwind veritabanına sahip değilseniz hata verecektir. İndirmek için buraya tıklayın. Northwind veritabanı kullanmamın sebebi çoğu yazılımcının mevcut olması. Projenin boyutunu büyütmemek adına veritabanını projeme eklemedim.

Neler Öğreneceksiniz :

  • Tanımlanan bağlantı üzerindeki pooling (havuz) mantığını kavrayacaksınız,
  • Min Pool Size ve Max Pool Size kavramlarını öğreneceksiniz.

Önceklikle şunu belirtmeliyim ki eğer tanımladığınız bağlantı cümlesine "pooling" özelliğini eklemezseniz, varsayılan olarak true olacaktır. Yani oluşturulmuş bağlantı havuzu üzerinden işlemlerinizi gerçekleştirecekseniz bir şey yapmanıza gerek yok. Ancak her istek için ayrı bir bağlantı tanımlamak istiyorsanız aşağıdaki gibi bağlantı tanımlayabilirsiniz.

SqlConnection connection = new SqlConnection("server=.; database=northwind; integrated security=sspi; pooling=false");

İlk olarak veritabanı ile bağlan kuralım ve SQL Server Profiler'dan veritabanında neler olduğuna bakalım.

// 1 numaralı bağlantım.
using (SqlConnection connection = new SqlConnection("server=.; database=northwind; integrated security=sspi;"))
{
  connection.Open();
}

// 2 numaralı bağlantım.
using (SqlConnection connection = new SqlConnection("server=.; database=ReportServer; integrated security=sspi;"))
{
  connection.Open();
}

// 1 numaralı bağlantım ile aynı olduğu için 3. bağlantıyı açmayıp, 1 numaralı bağlantı üzerinden işlemi gerçekleştirecek.
using (SqlConnection connection = new SqlConnection("server=.; database=northwind; integrated security=sspi;"))
{
  connection.Open();
}

Ve sonuç aşağıdaki gibi olacaktır. 

SQL Server Profiler

Gördüğünüz gibi tek bir havuzdan yönetilen bağlantılar sonucu, aynı bağlantılari çin tekrar tekrar oturum açılmadı. Gelin şimdi her istek için ayrı bir bağlantı açalım ve bu işlemin ne kadar süreceğine bakalım.

SqlConnection connection;

DateTime start = DateTime.Now;

// 1000 kere bağlantıyı açıp kapatacağız.
for (int i = 0; i < 1000; i++)
{
  connection = new SqlConnection("server=.; database=northwind; integrated security=sspi; pooling=false");

  connection.Open();
  connection.Close();
  connection.Dispose();
}

DateTime end = DateTime.Now;

// Geçen zamanın sonucunu alacağız.
TimeSpan result = end - start;

MessageBox.Show(String.Format("Açılan bağlantıların işlem süresi : {0}", result.ToString()));

Yukarıdaki işlemin sonucu tam olarak 00:00:07.3951136 saniye sürdü. Anlattığım kadarıyla "pooling=false" dediğimiz için bin ayrı oturum açılmış olması lazım. 

SQL Server Profiler

Evet liste böylece akıp gidiyor aşağıya doğru. Şimdi... Şimdi "pooling=true" yapacağız. Yani tek bir bağlantıyı açık tutacağız ve işlemleri bellekteki bağlantıdan yürüteceğiz. Gelin bunun ne kadar zaman alacağına ve SQL Server Profiler'da nasıl gözükeceğine bakalım.

SqlConnection connection;

DateTime start = DateTime.Now;

for (int i = 0; i < 1000; i++)
{
  connection = new SqlConnection("server=.; database=northwind; integrated security=sspi; pooling=true");

  connection.Open();
  connection.Close();
  connection.Dispose();
}

DateTime end = DateTime.Now;

TimeSpan result = end - start;

MessageBox.Show(String.Format("Açılan bağlantıların işlem süresi : {0}", result.ToString()));

Yukarıdaki işlemin sonucu tam olarak 00:00:00.2275035 saniye sürdü. Buradaki asıl mantık şu; açılan ilk bağlantı bellekte tutulur ve geri kalan bağlantılar için kullanılır. Gelin bir de SQL Server Profiler'da nasıl göründüğüne bakalım.

SQL Server Profiler

Dediğim gibi, tek bir bağlantı açıldı ve geri kalan bağlantılar bellekten kullanıldı. Gördüğünüz gibi inanılmaz performans artışı sağlandı. Peki bağlantı havuzundaki bağlantı sayımıza sınır koyma şansımız var mı? Evet.

Max Pool Size : Bağlantı havuzumuzdaki saklanacak en fazla bağlantı sayısını belirtir. Varsayılan olarak 100'dür.

Min Pool Size : Bağlantı havuzumuzdaki saklanacak en az bağlantı sayısını belirtir. Varsayılan olarak 0'dır.

Eğer Max Pool Size'ı 50 olarak belirtir ve 50'den fazla bağlantı açarsanız (kapatmadan), şöyle bir hata ile karşılacaksınız.

Hata

"Timeout expired.  The timeout period elapsed prior to obtaining a connection from the pool.  This may have occurred because all pooled connections were in use and max pool size was reached." Diyor ki : Zaman aşımı süresi doldu. Havuza bağlantı elde edilemeden zaman aşımı süresi doldu. Bu, tüm havuz bağlantıları kullanıldığı ve en büyük havuz boyutuna erişildiği için oluşmuş olabilir. Bağlantınızı kapatmayı asla unutmayın! (Bağlantılı sınıflarda tabii)

Son olarak SqlConnection üzerinden kullanabileceğiniz 2 adet statik metottan bahsetmek istiyorum.

  1. SqlConnection.ClearAllPools() ile tüm havuzları temizleyebilir,
  2. SqlConnection.ClearPool(SqlConnection) ile belirli bağlantı havuzunu temizleyebilirsiniz.

Makaledeki örnek uygulamayı indirmek isterseniz buraya tıklayın.

DataTableReader Nesnesi

Bu makalemizde DataTableReader nesnesinin ne olduğunu ve nasıl kullanılacağını öğreneceğiz.

Senaryo : SqlDataAdapter nesnesi ile verileri veritabanından çekip bir DataTable'ı dolduracağım. Daha sonra DataTableReader ile bu DataTable üzerinden verileri okuyacağım.

Bilinmesi Gerekenler :

  • Makaledeki örnek .NET Framework 4.0 ve Visual Studio 2010 ile hazırlandı,
  • Makale, Northwind veritabanı gerektirmektedir. Northwind veritabanını indirmek istiyorsanız buraya tıklayın. Northwind veritabanı kullanmamın sebebi çoğu yazılımcının mevcut olması. Projenin boyutunu büyütmemek adına veritabanını projeme eklemedim.

Neler Öğreneceksiniz :

  • DataTableReader nesnesinin ne olduğunu, nerede ve nasıl kullanacağımızı öğreneceğiz.

DataTableReader ilk olarak aklınıza SqlDataReader'ı getirebilir ki haksız değildir. Çok benzer yapıya sahiptirler. DataTableReader da SqlDataReader gibi DbDataReader sınıfından türemiştir. İkisi de read-only ve forward-only yapıya sahiptir. Madem ki DataTableReader ile SqlDataReader bir çok ortak noktaya sahip, farkı neresinde? SQL veritabanı için nasıl ki SqlDataReader kullanılıyor, DataTable veya DataSet için de DataTableReader kullanılıyor ve bağlantının açık kalması gibi bir durum söz konusu değil. Çünkü bağlantısız nesneler (disconnected mimari) üzerinden çalışıyor. 

SqlDataAdapter adapter = new SqlDataAdapter("Select FirstName,LastName from Employees", "server=.; database=northwind; integrated security=sspi");

DataTable table = new DataTable();

adapter.Fill(table);

//DataTableReader bu satırda yaratılıyor.
DataTableReader tableReader = table.CreateDataReader();

if (tableReader.HasRows)
{
  while (tableReader.Read())
  {
     lst_employees.Items.Add(String.Format("{0} {1}", tableReader[0], tableReader[1]));
  }
}

adapter.Dispose();
table.Dispose();
tableReader.Dispose();

DataSet nesnesi üzerinden DataTableReader yaratmak için:

DataSet dataSet = new DataSet();

adapter.Fill(dataSet);

DataTableReader tableReader = dataSet.CreateDataReader();

Birden fazla DataTable üzerinde çalışmak için NextResult metodunu kullanabilirsiniz. Dediğim gibi, SqlDataReader'dan pek farkı yok ama kullanım alanında farklılık yaratabilir.

Makale ile ilgili örnek uygulamayı indirmek isterseniz buraya tıklayın.