Makalemizin konusu olan MARS (Multiple Active Results Sets) ADO.NET 2.0 ile gelen yeni ve veritabanı konusunda önemli bir yapıdır. MARS (Multiple Active Results Sets) kısaca tek bağlantı (connection) üzerinden birden fazla sorguyu (command) işleyebilen ve/veya geriye sonuç döndürebilen bir yapıdır.
MARS yapısını daha iyi anlayabilmek için öncelikle ADO.NET 1.0 ve ADO.NET 1.1 içerisindeki connection command nesnelerinin işleyişini ve sorgu (query) işleme yapısını incelemek gereklidir.ADO.NET 1.1 ve öncesinde database ile ilgili bir işlem yapmak istediğimizde her sorgumuz için veritabanına bir bağlantı açmamız ve bu bağlantı üzerinden sorgumuzu işlememiz gerekiyordu ya da aynı bağlantıyı her sorgu için aktif hale getirip tekrar kapatmamız (close) gerekiyordu. Mesela bir console uygulaması yazdığımızı düşünelim. Bu uygulamada Northwind database ? i gibi employees ve orders isminde iki tablodan bir rapor almak istediğimizi varsayalım.
EmployeeID firstName lastName
1 Nancy Davolio
Bu bilginin altında da orders tablosundan bu çalışana bağlı Order ları listelemek isteyelim. Orders tablosundan OrderID alanını listeleyelim. Bu raporu ADO.NET 1.1 ve öncesinde yapabilmek için veritabanı ile birden fazla sayıda bağlantı kurmamız gerekirdi. İlk önce employees tablosu için bir command çalışmalı :
Select EmployeeID,firstName,lastName from employees
Sorgusunu çalıştırdıktan sonra buna bağlı başka bir sorgu daha çalıştırmalıyız:
Select OrderID from Orders Where EmployeeID = @EmpID
Bu sorgu sonuçlarını da ekrana detay bilgi gibi listelemek istediğimizi varsayalım:
Bu durumda iki ayrı bağlantı yardımı ile raporumuzu üretebiliyorduk.Fakat ADO.NET 2.0 ile artık iki ayrı datareader nesnemizi tek connection üzerinden koşturabileceğiz.Yine iki sorgu çalışacak ama tek fark bu iki sorgu tek connection nesnesi üzerinden eş zamanlı olarak çalışacak.
Örnek olarak bir Windows uygulaması yapalım ve Northwind database ? indeki Products, Categories, Employees, Orders, Shippers tablolarını okuyup formumuzdaki 5 adet listbox kontrolünde listeyelim. Bu işlemide tek baglantı üzerinden yapmaya çalışalım :
SqlConnection cn;
private void btnSorgu_Click(object sender, System.EventArgs e)
{
cn = new SqlConnection();
cn.ConnectionString = "data source=.;initial
catalog=Northwind;trusted_connection=SSPI;";
SqlCommand cmd1 = new SqlCommand();
cmd1.Connection=cn;
cmd1.CommandText = "Select * From Products";
SqlCommand cmd2 = new SqlCommand();
cmd2.Connection=cn;
cmd2.CommandText = "Select * From Categories";
SqlCommand cmd3 = new SqlCommand();
cmd3.Connection=cn;
cmd3.CommandText = "Select * From Employees";
SqlCommand cmd4 = new SqlCommand();
cmd4.Connection=cn;
cmd4.CommandText = "Select * From Orders";
SqlCommand cmd5 = new SqlCommand();
cmd5.Connection=cn;
cmd5.CommandText = "Select * From Shippers";
try
{
cn.Open();
SqlDataReader dr1 = cmd1.ExecuteReader();
SqlDataReader dr2 = cmd2.ExecuteReader();
SqlDataReader dr3 = cmd3.ExecuteReader();
SqlDataReader dr4 = cmd4.ExecuteReader();
SqlDataReader dr5 = cmd5.ExecuteReader();
lstProducts.Items.Clear();
while (dr1.Read())
{
lstProducts.Items.Add(dr1["ProductName"].ToString());
}
lstCategories.Items.Clear();
while (dr2.Read())
{
lstCategories.Items.Add(dr2["CategoryName"].ToString());
}
lstEmployees.Items.Clear();
while (dr3.Read())
{
lstEmployees.Items.Add(dr3["firstName"].ToString()+" "+dr3["lastName"].ToString());
}
lstOrders.Items.Clear();
while (dr4.Read())
{
lstOrders.Items.Add(dr4["OrderID"].ToString()+" "+dr4["OrderDate"].ToString());
}
lstShippers.Items.Clear();
while (dr5.Read())
{
lstShippers.Items.Add(dr5["companyName"].ToString());
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message,"Error",MessageBoxButtons.OK,MessageBoxIcon.Error);}
finally
{
cn.Close();
}
}
Bu örnek programı çalıştırdığımızda SQL Server tarafından istemci uygulamaya bir istisna (Exception) gönderilecektir. Hata mesajı :
?There is already an open DataReader associated with this Connection which must be closed first.?
Çünkü sqldatareader nesnesi kendi komutunun çalışması bitene kadar bağlantının açık kalmasını zorunlu koşar ve aynı bağlantıyı baksa bir nesnenin kullanmasına izin vermez.
Bu programdaki hatayı çözebilmek için ya tek connection nesnemiz olan cn ? i her sefer açıp kapatmamız gerekir ya da her sqldataReader nesnemiz için ayrı bir connection nesnesi oluşturmamız gerekir.Bu her iki çözümde de karşımıza bir hata mesajı gelmeyecek fakat bu tip kullanımlar bizim sistem kaynaklarımızı fazlasıyla tüketecektir.Şu anda verdiğimiz örnekte bu kaynak tüketimini tam anlamıyla hissedemeyiz fakat öyle bir program düşünün ki kayıt sayısı 5,000,000 dan fazla iki adet tabloyu sorgulamamız gerekmekte. Böyle bir durumda yaratacağımız her bağlantı nesnesi veritabanımızda ayrı bir bağlantı açacak ve sistem kaynaklarını fazlasıyla tüketmeye başlayacaklardır. Ayrıca bunlar eş zamanlı ilerleyemeyeceği için ilk gönderilen komut işlemini bitirmeden diğerine geçilmeyecek tüm komutlar teker teker birbirinin bitmesini bekleyecek ve biz bilgisayarın başında epey zaman harcamak zorunda kalacağız.
Fakat artık ADO.NET 2.0 bu ve bunun gibi ihtiyaçlar doğrultusunda yeni bir yapı getirmiştir.Bu yapının ismi MARS (Multiple Active Results Sets) (Asenkron komut yürütme de diyebiliriz). MARS açık bir bağlantı üzerinden birden fazla sql komutunun eş zamanlı olarak çalıştırılmasını sağlar. MARS yapısını kullanabilmemiz için veritabanı sistemimizin bu yapıyı destekliyor olması gerekmektedir.
Şimdide MARS yapısına dayanan, ADO.NET 2.0 ile SQL Server 2005 üzerinde çalışan bir program yazalım. Bu programda tek bağlantı nesnesi üzerinden farklı 2 adet komutu nasıl yürüteceğimizi göreceğiz :
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.SqlClient;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
namespace MARSOrnegi
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void btnSorgula_Click(object sender, EventArgs e)
{
try
{
SqlConnection con = new SqlConnection("data source=.;initial catalog=Calisma;uid=****;password=********;MultipleActiveResultSets=true");
SqlCommand cmd1 = new SqlCommand("Select * From Musteriler");
SqlCommand cmd2 = new SqlCommand("Select * From Urunler");
cmd1.Connection = con;
cmd2.Connection = con;
SqlDataReader dr1;
SqlDataReader dr2;
con.Open();
dr1 = cmd1.ExecuteReader();
dr2 = cmd2.ExecuteReader();
lstMusteriler.Items.Clear();
while (dr1.Read())
{
lstMusteriler.Items.Add(dr1["Ad"].ToString()+" "+dr1["Soyad"].ToString());
}
lstUrunler.Items.Clear();
while (dr2.Read())
{
lstUrunler.Items.Add(dr2["UrunID"].ToString()+" "+dr2["UrunAdi"].ToString());
}
con.Close();
}
catch (SqlException ex)
{
MessageBox.Show(ex.Message);
}
}
}
}
Yukarıda görmüş olduğunuz kod MARS yapısını kullanarak aynı database içerisindeki iki ayrı tabloyu sorgulayıp kullanıcının karşısına getirir. Bunu yaparken sql komutları birbirinin bitmesini beklemez ve ikiside aynı bağlantı üzerinde koşar. Böylece hem sistem kaynaklarında gereksiz bir tüketimi engellemiş oluruz hemde kullanıcıya istediği bilgiyi daha çabuk döndürmüş ve son olarak da bazen müşterilerimizden çıkan ?Bu program çok yavaş çalışıyor? şikayetinden bir ölçüde kurtulmuş oluruz.
Şimdi gelelim MARS?ın uygulanabilme şartlarına. MARS?ı kullanabilmek için sadece MARS yapısını destekleyen bir veritabanı sistemi ile çalışıyor olmak yeterli. Tabiki ADO.NET 2.0 ?ı unutmamak lazım Visual Studio 2005 bu aşamada sorununuzu çözecektir.Bunlar dışında MARS? tan faydalanabilmek için connectionstring property mize ?MultipleActiveResultSets=true? ifadesini eklemeyi unutmuyoruz. Bu ifade eklendiği takdirde veritabanı sistemimiz bizim MARS yapısına dayanan bir uygulama geliştirdiğimizi anlıyor ve komutlarımızı buna göre yorumlamaya başlıyor.
Makalemizin en başından beri birçok yerde eş zamanlı çalışmak şeklinde bir ifade geçti. Bunun ne anlama geldiğine bir sonraki makalemizde değiniyor olacağız. Şimdilik herkese iyi günler diliyorum, bir sonraki makalede görüşmek dileğiyle.
Not: Yukon içerisindeki Calisma isimli databasedeki tablolar
Musteriler :
[Ad] [varchar](50) COLLATE Turkish_CI_AS NULL
[Soyad] [varchar](50) COLLATE Turkish_CI_AS NULL
Urunler :
[UrunID] [int] IDENTITY(1,1) NOT NULL
[UrunAdi] [varchar](50) COLLATE Turkish_CI_AS NULL
Bora BURGUCUGİL
E-mail : bora.burgucugil@bilgeadam.com
BilgeAdam BTA
Bireysel Yazılım Geliştirme Eğitmeni
Beşiktaş