https://buraksenyurt.com/Burak Selim Şenyurt - Ado.net 2.02024-03-14T10:05:46+00:00Matematik Mühendisi Bir Bilgisayar Programcısının NotlarıBurak Selim SenyurtBlogEngine.Net Syndication Generatorhttps://buraksenyurt.com/opml.axdBurak Selim SenyurtMatematik Mühendisi Bir Bilgisayar Programcısının Notlarıtr-TRBurak Selim Şenyurt0.0000000.000000https://buraksenyurt.com/post/Data-Access-Application-Block-Nedir-(-Net-2-0-icin)-bsenyurt-com-danData Access Application Block Nedir? (.Net 2.0 için)2006-05-05T12:00:00+00:00bsenyurt<p>Değerli Okurlarım Merhabalar,</p>
<p>Microsoft tarafından serbest olarak dağıtılan Data Access Application Block (Veri Erişimi Uygulama Bloğu) özellikle n katmanlı mimarilerde, Data Access Layer (veri erişim katmanı) için gerekli işlevselliği sağlayan, performans ve bellek yönetimi konusunda iyi sonuçlar veren bir Enterprise Solution Pattern' dir. Bu block sayesinde, özellikle Business Layer (iş katmanındaki) katmanındaki işimiz oldukça kolaylaşmaktadır. Özellikle Sql sunucusu üzerinde uzmanlaşmış olan bu block' un .Net 2.0 için olan sürümü Ocak ayı içerisinde yayınlandı.</p>
<p>Data Access Application Block (Veri Erişimi Uygulama Bloğu) ve diğer Enterprise Solution Pattern' lerini bu adresten <a href="http://www.microsoft.com/downloads/details.aspx?familyid=5A14E870-406B-4F2A-B723-97BA84AE80B5&displaylang=en">indirebilirsiniz</a>. Data Access Application Block (Veri Erişimi Uygulama Bloğu), diğer blocklar gibi bir solution olarak gelmektedir. Dolayısıyla ilk kullanımından önce mutlaka bu solution' ı açıp derlememiz gerekir. Böylece kullanılabilir assembly dosyalarımızı (dll' leri) elde etmiş oluruz. Bunun sonucu olarakta herhangibir projede, Data Access Application Block (Veri Erişimi Uygulama Bloğu) için oluşturulan assembly' ımızı referans edebilir ve kullanmaya başlayabiliriz. Aynı uygulama mantığı diğer block' lar içinde geçerlidir.</p>
<p>Data Access Application Block (Veri Erişimi Uygulama Bloğu)' unu kısaca inceleyeceğimiz bu makalede örneklere geçmeden önce, Data Access Layer 'ın (veri erişim katmanı) sağladığı avantajlardan kısaca bahsetmekte fayda olacağı kanısındayım. Katlı mimariler, özellike Enterprise Solution' ların olmazsa olmaz parçalarından birisidir. Temel olarak en basit mimari model üç katmandan oluşmaktadır.</p>
<p><img src="/makale/images/mk160_3.gif" alt="" width="553" height="68" border="0" /></p>
<p>Bu modelde veri ile ilgili temel işlemleri üstlenen bir veri erişim katmanı (Data Access Layer), uygulamanın mantığını üstlenen bir iş katmanı (Business Layer) ve uygulama arabiriminin tutulduğu bir sunum katmanı (Presentation Layer) mevcuttur. Data Access Layer (veri erişim katmanı) genellikle bağlantı oluşturma, sql komutlarını çalıştırma gibi temel yapılar için gerekli kodları, diğer katmanlardan soyutlayan bir görev üstlenir. Net tarafından baktığımızda, DataSet, DataTable, Xml, DataReader gibi veri türlerini veya kendi veri türlerimizi geri döndüren işlemler ve daha bir çoğu Data Access Layer (veri erişim katmanı) içerisindeki metodlarda toplanmaktadır. Eğer elinizde hazır bulunan bir Data Access Layer (veri erişim katmanı) yoksa veya tembellik edip yazmaya üşeniyorsanız (ya da var olan bir tanesini inceleyip en azından vizyonunuzu geliştirmek istiyorsanız), Data Access Application Block (Veri Erişimi Uygulama Bloğu) gerçekten büyük bir fırsattır. Özelliklede ücretsiz olarak dağıtıldığı düşünülürse.</p>
<p>Normal şartlar altında özellikle Business (İş) katmanında yer alan metodlarımız içerisinde, Data Access Layer (veri erişim katmanı) içerisindeki metodlar sıklıkla kullanılmaktadır. Örneğin web tabanlı bir hizmet programını ele alalım ve sayfalar üzerindeki GridView kontrollerinin doldurulması gibi temel bir işlem için gerekli olan materyalleri düşünelim. İlk olarak Data Access Layer (veri erişim katmanı) üzerinde gerekli sorguları çalıştırıp geriye DataSet döndürecek aşırı yüklenmiş (overload) metodlar yazılması gerekir. Aşırı yüklenmiş bu versiyonlarda text bazlı query çalıştıracak, birden fazla sayıda parametre alacak yada bir stored procedure' ü ele alacak hatta bunların transaction bazlı versiyonlarınıda tutacak tipte seçenekler yer alabilir. Üstelik bu metodun kullanacağı bağlantı nesnesinide bilmesi, gerekirse oluşturması, açması ve hatta işi bitince bellek yönetiminide gerçeleştirerek kaynakları en iyi şekilde idare etmesi beklenir. Sonuç itibariyle uygulamanın iş mantığını kapsayan Business (İş) katmanının, buradaki kod kalabalığından etkilenmemesi amaçlanmaktadır. Öyleki veri erişim katmanı bir kez yazıpı pek çok projede kullanılabilirken, iş katmanı projeden projeye farklılık gösterecektir. İşte bu nedenle bu tip karmaşık ve kendi içerisinde dallanarak modülleşebilen işlemler, pek çok projede kullanılabilmeleri amacıyla Data Access Layer (veri erişim katmanı) içerisinde tutulurlar.</p>
<p>Data Access Application Block (Veri Erişimi Uygulama Bloğu)' un sağladığı etkinlikleri görebilmek amacıyla aşağıdaki kod parçasını göz önüne alabiliriz. Bu kod parçası pek çok yerde kullanılabilecek tipte bir metod olarak veri erişim katmanında yer alacak nitelikte bir yapıya sahiptir.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using (SqlConnection con = new SqlConnection(conStr)
{
using (SqlCommand cmd = new SqlCommand(queryString, con))
{
SqlDataAdapter da = new SqlDataAdapter(cmd);
DataSet ds = new DataSet();
da.Fill(ds);
}
}</pre>
<p>Kodumuz parametre olarak gelecek sorgunun sonuçlarını bir DataSet içerisine aktarmaktayız. Bu işlemi gerçekleştirmek için ihtiyacımız olan tüm materyal kod satırlarında yer almaktadır. Bağlantıyı oluşturmak için bir SqlConnection nesne örneği, komutu çalıştırmak için bir SqlCommand nesne örneği ve bu komutu alıp DataSet' i dolduran bir SqlDataAdapter nesne örneği. Aslında yapılan iş son derece basittir. Bir sorgu sonucu elde edilen veri kümesinin DataSet nesne örneğine aktarılması.</p>
<p>Çeşitli uygulamların pek çok noktasında belirli bir sorgu ve bağlantı ile çalışıp geriye DataSet döndürecek metodlarımız olacaktır. Bunları her seferinde tekrardan yazmak nesne yönelimli bir dilin imkanları göz önüne alındığında doğal olarak yanlış olacaktır. Dolayısıyla bu ve benzeri işlevselliklere sahip kod parçaları, bir katman dahilinde toplanabilir ve yönetimin daha kolay olması sağlanabilir. Üstelik bu, verileri hazırlamak için kullanılan kod karmaşasınında diğer katmanlardan soyutlanması anlamına gelmektedir. İşte bu nedenlede, uygulamaların mantığı ile görsel arabirimlerinden ayrıştırılmış ve temel olarak veri erişim tekniklerini ele almış bir katman söz konusudur ki biz bunu Data Access Layer (veri erişim katmanı) olarak nitelendiriyoruz. Microsoft buradaki ihtiyacı ele alaraktan herkesin kolayca kullanabileceği bir veri erişim katmanı çözümü üretmiştir. Data Access Application Block (Veri Erişimi Uygulama Bloğu).</p>
<p><img src="/makale/images/mk160_4.gif" alt="" width="553" height="68" border="0" /></p>
<p>Eğer sisteminize Data Access Application Block (Veri Erişimi Uygulama Bloğu)' u başarılı bir şekilde yüklediyseniz, kendi Data Access Layer (veri erişim katmanı) kütüphanenize kolayca sahip olmuşsunuz demektir. Örneğin yukarıdaki kodun yer aldığı bir Data Access Layer (veri erişim katmanı) metodunun, Data Access Application Block (Veri Erişimi Uygulama Bloğu)' taki karşılığını ele almaya çalışalım. Ancak öncesinde, uygulamamıza Data Access Application Block (Veri Erişimi Uygulama Bloğu)' tan gerekli referansları almamız gerekecektir. <em>(Eğer .Net 2.0 için indirdiğiniz Enterprise Library' yi standart olarak kurduysanız, Enterprise Solution' unuzu derledikten sonra oluşan assembly' lara C:\Program Files\Microsoft Enterprise Library January 2006\bin klasöründen erişebilirsiniz.)</em></p>
<p><img src="/makale/images/mk160_1.gif" alt="" width="303" height="214" border="0" /></p>
<p>Bu referansları aldıktan sonra yukarıdaki kod parçasını aşağıdaki haliyle yazabiliriz. Önce bizim için gerekli referanslar;</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using Microsoft.Practices.EnterpriseLibrary.Common;
using Microsoft.Practices.EnterpriseLibrary.Data.Sql;
using Microsoft.Practices.EnterpriseLibrary.Data;</pre>
<p>Kodumuzun aynısını ve hatta daha iyisinide yapan Data Access Application Block (Veri Erişimi Uygulama Bloğu) karşılıkları,</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">Database db = DatabaseFactory.CreateDatabase("conn");
db.ExecuteDataSet(CommandType.Text, "Select * From Production.Product");</pre>
<p>Görüldüğü gibi Data Access Application Block (Veri Erişimi Uygulama Bloğu)' un kullanımı son derece kolay ve esnektir. Koda dikkat edecek olursanız, connection nesnesinin nasıl oluşturulduğunu, dataSet nesnesinin nasıl doldurulduğunu görmemekteyiz. Bu işlemler Data Access Layer (veri erişim katmanı) içerisinde kapsüllenerek bizden soyutlandırılmış durumdalar. Benim en çok beyendiğim özelliklerden birisi, kilit nesnelerin oluşturulmasında çeşitli factory nesnelerinin görev alıyor olması. Örneğimizde, Database nesnesini oluşturmak için DatabaseFactory isimli başka bir fabrika nesnesi kullanılmıştır. Özellikle CreateDatabase metodunun bu versiyonu varsayılan olarak uygulamanın konfigurasyon dosyasına bakıp uyun connectionString node' unu kullanmıştır.</p>
<p>.Net 2.0 için geliştirilen bu versiyonda önceki Enterprise Block' lara göre bir takım temel farklılıklarda mevcuttur. Örneğin, burada oluşturduğumuz Database nesne örneğini çeşitli metodları yürütmek (execute) için kullanmaktayız. Ancak bunu oluştururken App.config dosyasında tuttuğumuz connection string bilgisini doğrudan kullanıyoruz. Bu ve benzeri pek çok yenilik var. Pek çok isim alanı tamamen değişmiş ve daha iyi ayrıştırılmış durumda. Ama ilk göze çarpanlar arasında Ado.Net 2.0 olan tam destek ve sınıfların biraz daha derlenip bir önceki versiyona göre karmaşıklıktan uzaklaştırılmış olması var.</p>
<p>App.config içeriği;</p>
<pre class="brush:xml;auto-links:false;toolbar:false" contenteditable="false"><?xml version="1.0" encoding="utf-8" ?>
<configuration>
<connectionStrings>
<add name="conn" connectionString="Data source=manchester;database=AdventureWorks;integrated security=sspi" providerName="System.Data.SqlClient"/>
</connectionStrings>
</configuration></pre>
<p>CreateDatabase static metodu konfigurasyon dosyası içerisindeki conn isimli anahtarı nasıl bulacağını bilmektedir. Biz bununla da uğraşmamaktayız. Buda uygulamanın yeniden derlemeye gerek duymayacak teknikleri (ki burada xml tabanlı bir konfigurasyon dosyası bunu karşılıyor) kolayca uygulayabilecek bir katmana sahip olduğu anlamına gelmektedir. Şimdi gelin başka bir senaryoyu ele alalım. Örneğin, sistemde yer alan parametrik bir stored procedure' ün sonuçlarını ortama bir DataReader vasıtasıyla almak istediğimiz bir işlevselliği Data Access Layer (veri erişim katmanı) içerisine katmak istediğimizi düşünelim. Bu senaryo için Sql Server 2005 üzerinde, AdventureWorks veritabanında yer alacak aşağıda oluşturma script' i verilen sp' yi kullanabiliriz.</p>
<p>Kullandığımız Stored Procedure;</p>
<pre class="brush:sql;auto-links:false;toolbar:false" contenteditable="false">USE [AdventureWorks]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[GetStoreByPersonID]
(
@PersonID int
)
AS
SELECT * FROM Sales.Store WHERE SalesPersonID=@PersonID
RETURN</pre>
<p>Şimdi bu sp' yi kullanacak örnek bir kod parçasını kendi DAL' ımız içerisinde aşağıdaki gibi yazdığımızı düşünelim.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using (SqlConnection con = new SqlConnection(conStr))
{
using (SqlCommand cmd = new SqlCommand(queryStr, con))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue(prmName, prmValue);
con.Open();
SqlDataReader dr = cmd.ExecuteReader();
}
}</pre>
<p>Elbette buraki kod yazım şekli yazılımcıdan yazılımcıya farklılıkta gösterebilir. Oysaki hangi yol ile olursa olsun, Data Access Application Block (Veri Erişimi Uygulama Bloğu)' un sunduğu çizgi bellidir. Bu da aslında, büyük çaplı projelerde farklı yazılımcıların aynı standart üzerinde geliştirme yapabilmesini olanaklı kılmaktadır. Dolayısıyla tek yapmamız gereken eğer bir stored procedure' ü, konfigurasyon dosyasında tutulan bir bağlantı bilgisi üzerinden çalıştırmak ve geriye bir DataReader almak ise, Business (İş) katmanında kullanacağımız metod sadece aşağıdaki iki satır kod parçasını içerecektir.</p>
<p><img src="/makale/images/mk160_2.gif" alt="" width="599" height="84" border="0" /></p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">Database db = DatabaseFactory.CreateDatabase("conn");
db.ExecuteReader("GetStoreByPersonID", 277);</pre>
<p>Görüldüğü gibi, ExecuteReader metodunun kullandığımız versiyonunda ilk parametre olarak stored procedure' ün adı verilmiştir. İkinci parametre olarak ise, params anahtar sözcüğü kullanılarak n sayıda object tipinden değer girilebilmesi sağlanmıştır. Buna göre ExecuteReader metodu gelen değer sayısına göre içeride parametre oluşturacak ve sp' ye gönderecektir<em>(Bu işlemlerin Data Access Application Block (Veri Erişimi Uygulama Bloğu) içerisindeki kodlarda nasıl yapıldığını görmek için, ExecuteReader satırına Breakpoint koymanızı ve F11 ile adım adım ilerlemenizi öneririm.)</em> Böylece sadece iki kod satırı ile ne kadar çok kod kalabılığından soyutlandığımızı daha kolay görebilir ve veri erişim katmanının faydalarını fark edebilirsiniz. İşte Data Access Layer (veri erişim katmanı) ' ın faydası burada bir kez daha ortaya çıkmaktadır. Tüm kod kalabalığını iş mantığımızdan soyutlamıştır. Ayrıca yönetiminide kendi içerisinde gerçekleştirmektedir.</p>
<p>Başka bir örnek daha ele alalım. Bazen uygulamalarımız n-katlı mimariye sahip olabilir. Örneğin veritabanındaki çeşitli tiplerin sınıfsal karşılığının tutulduğu bir entity katmanımız olduğunu düşünelim. Bu katmanda pekala veri erişim katmanını aktif olarak kullanacaktır. Örneğin, Urun isimli aşağıdaki tipi ele alalım.</p>
<p><img src="/makale/images/mk160_5.gif" alt="" width="209" height="265" border="0" /></p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">public class Urun
{
private int _id;
private string _ad;
private double _fiyat;
public int Id
{
get { return _id; }
set { _id = value; }
}
public string Ad
{
get { return _ad; }
set { _ad = value; }
}
public double Fiyat
{
get { return _fiyat; }
set { _fiyat = value; }
}
public Urun()
{
}
public void Load(int id)
{
Database db = DatabaseFactory.CreateDatabase("conn");
IDataReader dr = db.ExecuteReader("GetProductById", id);
dr.Read();
_id = Convert.ToInt32(dr["ProductID"]);
_ad = dr["Name"].ToString();
_fiyat = Convert.ToDouble(dr["StandardCost"]);
}
}</pre>
<p>Bu kod parçasında örnek olarak Urun isimli bir entitiy' nin çalışma zamanında karşılık geleceği herhangibir veri satırı için yüklenebilmesini (Load) sağlarken veri erişim katmanından nasıl yararlanabildiğimizi görmektesiniz.</p>
<p>Data Access Application Block (Veri Erişimi Uygulama Bloğu)' un sunduğu imkanlara ait pek çok örnek geliştirebiliriz. Bir diğer örnek ile makalemize devam edelim. Web tabanlı uygulamalarda veri bağlama işlemleri sırasında Business (İş) katmanının veri erişim katmanından nasıl yararlanabileceğini göreceğimiz bir örneğe bakalım.</p>
<p>Business (İş) sınıfımız;</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">public class OurLogic
{
private Database _db;
public OurLogic()
{
_db = DatabaseFactory.CreateDatabase("conn");
}
// Ürünleri parametre olarak gelen GridView kontrolüne yükleyen iş katmanı metodu.
public void BindProducts(GridView dg)
{
dg.DataSource = _db.ExecuteDataSet(CommandType.Text, "Select * From Production.Product");
dg.DataBind();
}
// Ürün kategorilerini parametre olarak gelen DropDownList kontrolüne bağlayan, ve value ile text özelliklerinide ayarlayan bir iş katmanı metodu. Burada ListControl kullanılmasının tek sebebi, sayfalarda yer alan DropDownList ve ListBox kontrollerine de destek verilmesini sağlamaktır. Bu yine ListControl' un polimorfik yapısının sağladığı bir avantajdır.
public void BindPrdCategories(ListControl dl)
{
dl.DataSource=_db.ExecuteReader(CommandType.Text,"SELECT ProductCategoryID, Name FROM Production.ProductCategory");
dl.DataValueField = "ProductCategoryID";
dl.DataTextField = "Name";
dl.DataBind();
}
// Ürünlerin sayısını int tipinde döndüren bir iş katmanı metodu. Bu metodda generic bir yapıda kullanılabilir. Böylece presentation katmanından çağırılırken herhangibir tipe karşılık gelecek şekilde ele alınabilir.
public int GetCategoryCount()
{
return (int)_db.ExecuteScalar(CommandType.Text,"SELECT COUNT(*) AS CategoryCount FROM Production.ProductCategory");
}
// Urun entity tipini verilen id' ye göre yükleyen ve geri döndüren bir iş katmanı metodu.
public Urun GetUrun(int id)
{
Urun urn = new Urun();
urn.Load(id);
return urn;
}
}</pre>
<p>Şimdi iş katmanının basit olarak sunum katmanından nasıl kullanıldığına bakalım.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">public partial class _Default : System.Web.UI.Page
{
protected OurLogic _ol;
protected void Page_Load(object sender, EventArgs e)
{
_ol = new OurLogic();
}
protected void btnProducts_Click(object sender, EventArgs e)
{
_ol.BindProducts(grdProducts);
}
protected void btnCategories_Click(object sender, EventArgs e)
{
_ol.BindPrdCategories(ddlProductCategories);
}
protected void btnGetCount_Click(object sender, EventArgs e)
{
lblCatCount.Text=_ol.GetCategoryCount().ToString();
}
protected void btnGet_Click(object sender, EventArgs e)
{
Urun urn = _ol.GetUrun(Convert.ToInt32(txtId.Text));
lstLoad.Items.Add(urn.Ad);
lstLoad.Items.Add(urn.Fiyat.ToString());
}
}</pre>
<p><img src="/makale/images/mk160_6.gif" alt="" width="377" height="434" border="0" /></p>
<p>Görüldüğü gibi Data Access Application Block (Veri Erişimi Uygulama Bloğu) sayesinde uygulamalarımızı geliştirirken sadece iş katmanını ve sunum katmanını düşünmemiz yeterli olmaktadır. Bunun bir avantaj olup olmadığı düşünülebilir. Sonuç itibariyle yazılım mühendisliğine yeni başlayan arkadaşlar için, veri erişim katmanının nasıl olduğunun farkına varmak veya nasıl yazıldığını anlamak için Data Access Application Block (Veri Erişimi Uygulama Bloğu)' u incelemek bile vizyonumuzu geliştirecek önemli bir etkendir. Özellikle kendi projelerimizde, projenin büyüklüğü ile orantılı olacak şekilde kendi veri erişim katmanlarımızı yazmayı tercih edebiliriz. Ancak performans, bellek yönetimi, tutarlılık gibi kriterlerin önemi göz önüne alındığında, Data Access Application Block (Veri Erişimi Uygulama Bloğu)' un en azından incelenmesi gerektiği kanısındayım. Bu makalemizde kısaca Microsoft' un ücretsiz olarak sunduğu enterprise çözüm desenlerinden birisi olan Data Access Application Block (Veri Erişimi Uygulama Bloğu)' un ne olduğunu ve hangi amaçlar ile kullanılabildiğini temel düzeyde incelemeye çalıştık ve geldik bir makalemizin daha sonuna. Bir sonraki makalemizde görüşünceye dek hepinize mutlu günler dilerim.</p>2006-05-05T12:00:00+00:00enterprise applicationsenterprise architecturescross cuttingsbsenyurtMicrosoft tarafından serbest olarak dağıtılan Data Access Application Block (Veri Erişimi Uygulama Bloğu) özellikle n katmanlı mimarilerde, Data Access Layer (veri erişim katmanı) için gerekli işlevselliği sağlayan, performans ve bellek yönetimi konusunda iyi sonuçlar veren bir Enterprise Solution Pattern' dir. Bu block sayesinde, özellikle Business Layer (iş katmanındaki) katmanındaki işimiz oldukça kolaylaşmaktadır. Özellikle Sql sunucusu üzerinde uzmanlaşmış olan bu block' un .Net 2.0 için olan sürümü Ocak ayı içerisinde yayınlandı.https://buraksenyurt.com/pingback.axdhttps://buraksenyurt.com/post.aspx?id=0ea0bffd-db80-4991-803b-d5977401bdc30https://buraksenyurt.com/trackback.axd?id=0ea0bffd-db80-4991-803b-d5977401bdc3https://buraksenyurt.com/post/Data-Access-Application-Block-Nedir-(-Net-2-0-icin)-bsenyurt-com-dan#commenthttps://buraksenyurt.com/syndication.axd?post=0ea0bffd-db80-4991-803b-d5977401bdc3https://buraksenyurt.com/post/Connection-Pooling-in-Onemi-bsenyurt-com-danConnection Pooling' in Önemi2006-02-27T08:00:00+00:00bsenyurt<p>Değerli Okurlarım Merhabalar,</p>
<p>Connectilon Pooling veritabanı programcılığında, uygulamaların performansını doğrudan etkiliyen unsurlardan birisidir. Bağlantıların bir havuza atılarak buradan kullanılmalarını sağlamaktaki en büyük amaç, çok sayıda kullanıcının bağlı olduğu veri tabanlı uygulamalarda, aynı özelliklere sahip bağlantı bilgilerinin defalarca oluşturulmasınının önüne geçmek bu sayede var olan açık bağlantıların kullanılabilmesini sağlamaktır. Temel mantık son derece basittir. Bir kullanıcı uygulaması içerisinden bir verikaynağına bağlanmak istediğinde, geçerli bir Connection nesnesi oluşturmak zorundadır. Bu Connection nesnesi eğer ilk kez talep edilmişse, veritabanı tarafında bir bağlantı havuzunun içine atılacaktır.</p>
<p>Ki başka bir kullanıcı aynı veri kaynağına aynı bağlantı bilgisi ile bağlanmak istediğinde, havuzdaki bağlantıyı kullanabilecektir. Burada aynı bağlantı bilgisine başvuran birden fazla kullanıcı olduğunu düşündüğümüzde bu mimarinin önemi ortaya çıkmaktadır. Özellikle web tabanlı uygulamalarda bu fark büyük performans kazanımı anlamına gelmektedir. Biz bu makalemizde Connection Pooling' in mimarisini incelemektense onu kullanırken dikkat etmemiz gereken noktalara değineceğiz. Ama herşeyden önce connection pooling' i kullanmanın faydasını göreceğimiz basit bir örnek ile yola çıkmakta fayda olacağı kanısındayım. Bunun için, Vs.2005' de aşağıdaki kodlara sahip basit bir console uygulaması yazarak işe başlayalım.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System;
using System.Collections.Generic;
using System.Text;
using System.Data.SqlClient;
namespace UsingPooling
{
class Program
{
static void Main(string[] args)
{
SqlConnection con = new SqlConnection("data source=MANCHESTER;database=AdventureWorks;integrated security=SSPI;Pooling=false");
DateTime dtBaslangic = DateTime.Now;
for (int i = 0; i < 10000; i++)
{
con.Open();
con.Close();
}
DateTime dtBitis = DateTime.Now;
TimeSpan tsGecerSure = dtBitis - dtBaslangic;
Console.WriteLine("Geçen süre {0} milisaniyedir.", tsGecerSure.TotalMilliseconds.ToString());
}
}
}</pre>
<p>Öncelikle bu uygulamada ne yapıyoruz kısaca bunu açıklayalım. Uygulamamız Sql Server 2005 üzerinde yer alan AdventureWorks isimli veritabanına, 10000 defa bir bağlantı açıp kapatıyor. Burada döngü içerisinde gerçekleşen olayları, sisteme bağlanan 10000 farklı kullanıcının kod kısmı olarakta düşünebilirisiniz. Aynı bağlantı bilgisi için defalarca açma ve kapatma işlemini yapıyoruz ve bu işlemler sonrası oluşan süre farkına bakıyoruz. Burada bağlantı bilgisine dikkat ederseniz Pooling özelliğinin bilerekten false olarak atandığını görürsünüz. Yani ilgili connection bilgisinin herhangibir şekilde havuza atılmayacağını (Connection Pool' da tutulmayacağını) belirtmiş oluyoruz. Bu haliyle uygulamamızı çalıştırdığımızda aşağıdaki gibi (ya da kullanıdığınız çevresel parametrelere göre bu sonuca yakın) bir süre farkı elde ederiz.</p>
<p><img src="/makale/images/mk149_1.gif" alt="" width="404" height="131" border="0" /></p>
<p>Ancak Pooling=false özelliğini kaldırırsak (ki varsayılan hali true' dur ve pooling' in aktif olmasını sağlar) süre farkı çok daha az olacaktır.</p>
<p><img src="/makale/images/mk149_2.gif" alt="" width="420" height="119" border="0" /></p>
<p>Görüldüğü gibi, her iki uygulamanın çalışma süreleri arasında belirgin ve aynı zamanda dikkate değer bir zaman farkı oluşmuştur. Bu, Connection Pooling' in herhangibir Connection nesnesine ait bağlantı bilgisi içerisinde hiç bir şey belirtilmesse zaten aktif olacağına şükredebileceğimiz bir durumdur. Ancak kodlama farklarından dolayı Pooling zaman zaman başımızı derde sokabilir. Bizim için sancı yaratacak iki farklı durum vardır. Şimdi kısaca bu durumlar inceleyeceğiz. İlk olarak aşağıdaki kodlara sahip olan ve Vs.2005 üzerinde geliştirilmiş basit bir Windows Uygulamasını göz önüne alalım.</p>
<p><img src="/makale/images/mk149_4.gif" alt="" width="275" height="157" border="0" /></p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">private void btnExecute_Click(object sender, EventArgs e)
{
try
{
SqlConnection con = new SqlConnection("data source=MANCHESTER;database=AdventureWorks;integrated security=SSPI;Min Pool Size=10;Max Pool Size=15");
SqlCommand cmd = new SqlCommand("SELECT Count(*) FROM Person.Contat", con);
con.Open();
int kontakSayisi=Convert.ToInt32(cmd.ExecuteScalar());
con.Close();
}
catch(Exception err)
{
lstExceptions.Items.Add(err.ToString());
}
}</pre>
<p>Burada button kontrolüne basıldığında çalışan kodlarda, Person.Contat isimli tablodaki kayıt sayısını öğrenebileceğimiz bir sorgunun çalıştırılmasını görüyoruz. Burada kod, try/catch bloğuna alındığından, sql komutlarının yürütülmesi yada bağlantının açılması sırasında oluşacak hatalara karşı programı koruma altına aldığımızı düşünebiliriz. Ancak Contat(Contact olması gerekirdir) tablosu AdventureWorks isimli veritabanında mevcut değildir. Bu yüzden kod çalışma zamanında bir SqlException verecektir. Ancak bu kodu birden fazla sayıda kullanıcının tetiklediğini düşünürsek (örneğimizde butona defalarca basarak bu durumu canlandırabiliriz) başımıza korkunç işler gelebilir.</p>
<p>Öyleki, SqlCommand sınıfından olan cmd nesnesi execute edilirken oluşan hata, con nesnesinin kapatılmasını engellemektedir. Bu sunucu üzerindeki bağlantı havuzunda açık kalan bir bağlantı demektir. Button kontrolümüz tetiklendikçe, açık bağlantı sayısı artmaya ve bir süre sonrada bağlantı havuzundaki maksimum bağlantı sayısını aşmaya başlayacaktır. Bu durum böyle devam ederekten sonuçta uygulamanın SqlException istisnasından vazgeçerek zaman aşımına bağlı olaraktan InvalidOperationException istisnasını fırlatmasına neden olacaktır. Şu aşamada istisnanın tipinden ziyade açık kalan bağlantıları sürekli olarak artması çok tehlikeli bir durumdur. Aşağıdaki ekran görüntüsü, yukarıdaki uygulamada button kontrolüne defalarca basılaraktan elde edilmiştir.</p>
<p><img src="/makale/images/mk149_3.gif" alt="" width="635" height="571" border="0" /></p>
<p>Bu görüntüde yer alan NumberOfReclaimedConnections sayacı (Counter), .Net 2.0 ile birlikte gelen yeni performans ölçüm değerlerinden birisidir ve havuzda yer alıpta kapatılamayan bağlantı sayılarına ilişkin bilgileri vermektedir. Grafiktende görüleceği üzere açık bağlantılar sürekli artmıştır. Uygulama bunun sonucunda SqlException' dan çıkarak zaman aşımı (Timeout) nedeni ile InvalidOperationException' a sürüklenmiştir.</p>
<table id="table30" style="border-collapse: collapse; width: 100%;" border="1" cellspacing="0" cellpadding="5" bgcolor="#ffffff">
<tbody>
<tr>
<td valign="top" width="53"><img src="/makale/images/dikkat.gif" alt="" width="53" height="53" border="0" /></td>
<td><em> NumberOfReclaimedConnections, .Net 2.0 ile birlikte gelen Performans sayaçlarından (Performance Counter) birisidir. Bu sayaç .Net Data Provider For Sql Server performans nesnesi (Performance Object) altında yer almaktadır.</em>
<p><img src="/makale/images/mk149_5.gif" alt="" width="384" height="379" border="0" /></p>
</td>
</tr>
</tbody>
</table>
<p>Aslında hatanın nedeni son derece basittir. Connection sınıfına ait nesne örneğinin kapatılması garanti altına alınmamıştır. Bu gerçekten önemli bir hatadır. Bu durumu düzeltmek için ya finally bloğu eklenmeli ya da işlemler aşağıdaki kod parçasında olduğu gibi using blokları içerisinde gerçekleştirilmelidir. Nitekim using bloğu doğal olaraktan kullandığı nesnenin dispose edilmesini garanti altına alır.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">private void btnExecute_Click(object sender, EventArgs e)
{
try
{
using (SqlConnection con = new SqlConnection("data source=MANCHESTER;database=AdventureWorks;integrated security=SSPI;Min Pool Size=10;Max Pool Size=15"))
{
using (SqlCommand cmd = new SqlCommand("SELECT Count(*) FROM Person.Contat", con))
{
con.Open();
int kontakSayisi = Convert.ToInt32(cmd.ExecuteScalar());
}
}
}
catch (Exception err)
{
lstExceptions.Items.Add(err.ToString());
}
}</pre>
<p><img src="/makale/images/mk149_6.gif" alt="" width="596" height="484" border="0" /></p>
<p>Görüldüğü gibi bu kez havuzda açık kalan hiç bir bağlantı yoktur. Bunu görmek için uygulamayı test ettiğimizde, yukarıdaki ekran görüntüsünde olduğu gibi NumberOfRecalimedConnections sayacının (Counter) sıfır olarak seyrettiğini görürüz. Dolayısıyla uygulamadaki bağlantılar için maksimum havuz boyutunun aşılması ve nihayetinde InvalidOperationException istisnasına sürüklenilmesi engellenmiştir. Herşeyden önemlisi ne kadar çok kullanıcı bu kodu çalışıtırırsa çalıştırsın, havuzda açık kalan herhangibir bağlantı olmayacaktır. Şimdi gelelim connection pooling ile ilgili diğer önemli noktaya. Aşağıdaki örnek console uygulaması bu durumu canlandırmak için geliştirilmiştir.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System;
using System.Collections.Generic;
using System.Text;
using System.Data.SqlClient;
namespace Pooling_3
{
class Program
{
static void Main(string[] args)
{
try
{
for (int i = 1; i < 100; i++)
{
KontakSayisiniBul();
}
}
catch (SqlException err)
{
Console.WriteLine(err.Message.ToString());
}
Console.ReadLine();
}
private static void KontakSayisiniBul()
{
using (SqlConnection con = new SqlConnection("data source=MANCHESTER;database=AdventureWorks;integrated security=SSPI;Min Pool Size=5;Max Pool Size=15"))
{
SqlCommand cmd = new SqlCommand("Select Count(*) From Person.Contact", con);
con.Open();
int kontakSayisi = Convert.ToInt32(cmd.ExecuteScalar());
System.Threading.Thread.Sleep(1500);
}
}
}
}</pre>
<p>Bu örnekte son derece anlamsız bir şekilde 100 defa Contact tablosundaki eleman sayısını hesaplatmaktayız. Bu işlemleri yaparken durumu daha iyi analiz edebilmek içinde kodu 1,5 saniye kadar duraksatıyoruz. Bu işlem sonradan Sql Server Servisini restart etmemizde bize zaman kazandıracaktır. Kodu, aynı işlemi sunucuya doğru gerçekleştiren n sayıda kullanıcı ekranına ait bir uygulamanın parçası olarakta düşünebiliriz. N sayıda kullanıcı sunucuya bağlanıp sorguyu çalıştırdıkları sürece, sunucunun başına çeşitli haller gelebilir.</p>
<p>Örneğin, Sql Server Servisi bir şekilde baştan başlatılmış (Restart) olabilir. (Bu çoğunlukla sql sunucusunu barındıran bilgisayarın istem dışı restart olması halinde gerçekleşebilecek bir durumdur.) Eğer böyle bir durum söz konusu olursa, havuzda duran bağlantı bilgileri servis yeniden çalışsa bile erişilemez hale gelecektir. Örneğimizi çalıştırdıktan sonra Sql Server servisini baştan başlatacak olursak, servis durup yeniden çalışmaya başladığında ilgili uygulama ortama bir SqlException istisnası fırlatacaktır.</p>
<p><img src="/makale/images/mk149_7.gif" alt="" width="610" height="466" border="0" /></p>
<p>Bu istisnanın nedeni artık havuzda duran bağlantı bilgilerinin yapısının, servisin yeniden başlatılması nedeni ile bozulmuş olmasıdır. Bu sorunu çözebilmek için yapılabileceklerden bir tanesi ve en kolayı, havuzdaki bağlantıları sıfırlamak bir başka deyişle havuzu boşaltmaktır. Ado.Net 2.0' da Connection sınıflarına bu işlemleri kolay bir şekilde yapabilmek için iki yeni metod eklenmiştir. Bu metodların prototipleri aşağıdaki gibidir.</p>
<table id="table34" style="border-collapse: collapse; width: 100%;" border="1" cellpadding="5" bgcolor="#336699">
<tbody>
<tr>
<td style="width: 367px; background-color: #ffffff;" width="367"><strong> Metod</strong></td>
<td><strong>Kısa Açıklama</strong></td>
</tr>
<tr>
<td bgcolor="#ffffff" width="367"><span class="keyword">public <span class="keyword">static void <span class="identifier">ClearAllPools ()</span></span></span></td>
<td bgcolor="#ffffff"><span class="identifier">Havudaki tüm bağlantıları boşaltır.</span></td>
</tr>
<tr>
<td bgcolor="#ffffff" width="367">public static void ClearPool (SqlConnection connection)</td>
<td bgcolor="#ffffff"><span class="identifier">Parametre olarak verilen bağlantıya ait havuzu boşaltır.</span></td>
</tr>
</tbody>
</table>
<p>Bu metodların uygulanması halinde yukarıdaki çalışma zamanı hatası ile başedebiliriz. Aslında bu metodlar, bağlantı havuzunu boşaltırken ilgili bağlantı nesnelerini kapatmazlar. Sadece bunların artık kullanılmayacağını belirtirler. Yukarıdaki örnek uygulamamızı aşağıdaki gibi değiştirmemiz etkili bir çalışma zamanı çözümü olacaktır.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">try
{
for (int i = 1; i < 100; i++)
{
KontakSayisiniBul();
}
}
catch (SqlException err)
{
if (err.Number == 233)
{
SqlConnection.ClearAllPools();
}
}</pre>
<p><br /> Uygulamamızı bu haliyle çalıştırırsak çalışma zamanında herhangibir istisna almayız. Az önce değindiğimiz gibi bu sorunun daha zor olan ama daha güçlü olan bir çözümü daha vardır. Bu Failover Partner adı verilen gene bir çözümdür. Kısaca, sql sunucusunun yanında ayna(Mirror) görevi gören bir sunucu ve birde tanık(Witness) görevi gören başka bir sunucu vardır. Bu sistemin konusu makalemizin sınırlarını aşmaktadır. Bu konuya ilerleyen zamanlarda ayrıca vakit ayırmayı düşünüyorum. Görüldüğü gibi bağlantı havuzlarını kullanırken başımıza gelebilecek iki önemli tehlike üzerinde durmaya çalıştık. İlki kesin olarak kapatılmayan bağlantı nesnelerinin yol açtığı sorundu. Bunun çözümü için en etkili yöntem olarak using bloklarına başvurduk. Diğer sorunumuz ise, çalışma zamanında veritabanı sunucusunun herhangibir neden ile sıfırlanmasıyıdı. Bu sorunu ise, hata kodunu ele alıp bağlanyı havuzlarını boşaltarak çözdük. Böylece geldik bir makalemizin daha sonuna. Bir sonraki makalemizde görüşünceye dek hepinize mutlu günler dilerim.</p>2006-02-27T08:00:00+00:00ado.netconnection poolingbsenyurtConnectilon Pooling veritabanı programcılığında, uygulamaların performansını doğrudan etkiliyen unsurlardan birisidir. Bağlantıların bir havuza atılarak buradan kullanılmalarını sağlamaktaki en büyük amaç, çok sayıda kullanıcının bağlı olduğu veri tabanlı uygulamalarda, aynı özelliklere sahip bağlantı bilgilerinin defalarca oluşturulmasınının önüne geçmek bu sayede var olan açık bağlantıların kullanılabilmesini sağlamaktır...https://buraksenyurt.com/pingback.axdhttps://buraksenyurt.com/post.aspx?id=d64195d6-b432-418f-930a-5c68ae9f15dd11https://buraksenyurt.com/trackback.axd?id=d64195d6-b432-418f-930a-5c68ae9f15ddhttps://buraksenyurt.com/post/Connection-Pooling-in-Onemi-bsenyurt-com-dan#commenthttps://buraksenyurt.com/syndication.axd?post=d64195d6-b432-418f-930a-5c68ae9f15ddhttps://buraksenyurt.com/post/Ado-Net-2-0(Beta-2)-Connection-String-Security-(Baglantc4b1-Katarc4b1-icin-Guvenlik)-bsenyurt-com-danAdo.Net 2.0(Beta 2) - Connection String Security (Bağlantı Katarı için Güvenlik)2005-10-17T09:00:00+00:00bsenyurt<p>Değerli Okurlarım Merhabalar,</p>
<p>Güvenlik günümüz uygulamalarında çok önemli bir yere sahiptir. Özellikle veritabanı kullanımını içeren uygulamalarda güvenliğin ayrı bir önemi vardır. Veritabanına gönderilen sorguların korunması, özellikle web servislerinden dönen veri kümelerinin etkin olarak şifrelenmesi gibi durumlar söz konusudur. Güvenlik prosedürü içerisinde yer alan unsurlardan biriside bağlantı bilgilerinin saklanmasıdır. Biz bu makalemizde, .Net 2.0 ile birlikte gelen yeni tekniklerden birisine değinerek, bağlantı bilgisinin (çoğunlukla connection string olarakda ifade edebiliriz) kolay bir şekilde nasıl korunabileceğini incelemeye çalışacağız.</p>
<p>Bildiğiniz gibi, .Net tabanlı uygulamalar özellikle XML tabanlı konfigurasyon dosyalarını yoğun olarak kullanır. Varsayılan olarak windows uygulamalarında app.config veya web uygulamalarında yer alan web.config dosyaları, genel bilgilerin yer aldığı kaynaklardır. Çoğunlukla proje genelinde kullanılan bağlantı bilgilerini bu dosyalarda key-value (anahtar-değer) çiftleri şeklinde tutmayı tercih ederiz. Aslında bağlantı katarı(connection string) bilgilerini kod içerisinde de global seviyede tanımlayıp kullanabiliriz. Ancak bunun bir takım dezavantajları vardır.</p>
<table id="table16" style="width: 100%;" border="1" cellspacing="0" cellpadding="5" bgcolor="#ffffff">
<tbody>
<tr>
<td width="53"><img src="/makale/images/dikkat.gif" alt="" width="53" height="53" border="0" /></td>
<td><em>Bağlantı katarı gibi sonradan (özellikle ürün canlı yayına başladıktan sonra) sıkça değişebilecek bilgileri kod içerisinde (hard-coded) tutmak güvenlik ve yönetilebilirlik açısından dezavantajlara sahiptir.</em></td>
</tr>
</tbody>
</table>
<p><br />Birincisi, yazılan kodlar sonuç olarak IL dilini kullandığından ters çevrilerek açığa çıkartılabilir. (Disassembly araçları) Bu nedenle bağlantı katarı bilgiside öğrenilebilinir. Diğer yandan, uygulamalar canlı hayata geçtikten sonra, kullandıkları veri tabanı sunucularının ip bilgilerinin, kullanıcı tanımlamalarının sıkça değişmesi nedeni ile bağlantı katarı bilgilerinin sıkça güncellenmesi gerekebilir. İşte bu iki nedenden ötürü bu tip bilgiler xml tabanlı konfigurasyon dosyalarında, özel şifreleme teknikleri ile tutulur.</p>
<p>Aşağıdaki kod parçalarında bir web uygulaması ve bir console uygulaması için kullandığımız standart konfigurasyon dosyalarında yer alan bağlantı katarı tanımlamalarını ve bu bağlantı katarları içerisindeki bilgilerin kullanımını gösteren iki örnek yer almaktadır.</p>
<p><em>Sıradan bir console uygulaması için app.config içeriği;</em></p>
<pre class="brush:xml;auto-links:false;toolbar:false" contenteditable="false"><?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="conStr" value="data source=localhost;database=AdventureWorks2000;user id=sa;password="></add>
</appSettings>
</configuration></pre>
<p><em>conStr isimli anahtarın uygulama içerisinden kullanımı;</em></p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System;
using System.Data.SqlClient;
namespace UsingConnectionString
{
class Class1
{
[STAThread]
static void Main(string[] args)
{
string conStr=System.Configuration.ConfigurationSettings.AppSettings["conStr"].ToString();
using(SqlConnection con=new SqlConnection(conStr))
{
con.Open();
}
}
}
}</pre>
<p><em>Bir web uygulamasında web.config içerisinde bağlantı katarı (connection string) için anahatar-değer (key-value) kullanımı.</em></p>
<pre class="brush:xml;auto-links:false;toolbar:false" contenteditable="false"><?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="conStr" value="data source=localhost;database=AdventureWorks2000;user id=sa;password="></add>
</appSettings>
<system.web>
<!-- Diğer ayarlar-->
</system.web>
</configuration></pre>
<p><em>Web.Config içerisindeki bağlantı katarı bilgisinin default.aspx içerisinde kullanımı;</em></p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">private void Page_Load(object sender, System.EventArgs e)
{
string conStr=System.Configuration.ConfigurationSettings.AppSettings["conStr"].ToString();
using(SqlConnection con=new SqlConnection(conStr))
{
con.Open();
}
}</pre>
<p>Gelelim .Net 2.0' a. Yeni versiyonda özellikle bağlantı katarlarına yönelik olarak geliştirilmiş özel bir güvenlik tekniği mevcuttur. Bu tekniğin kilit noktaları olarak, konfigurasyon ayarlarına eklenen iki yeni nitelik söz konusudur.Bu nitelikler onfigurasyon <strong>protectedData</strong> ve <strong>protectedDataSections</strong>' dır. ProtectedDataSections niteliği altında sonradan şifrelenmesi düşünülen anahatar bilgileri tutulmaktadır. Bağlantı katarı için kullanacağımız anahtar bilgisini burada tutabiliriz. ProtectedData kısmında ise, ProtectedDataSections içerisinde yer alan anahtar bilgisini şifreleyecek (encryption) ve deşifre edecek (decryption) provider nesnesine ait bilgileri yer alır. Şu an için <strong>RSAProtectedConfigurationProvider</strong> ve <strong>DPAPIProtectedConfigurationProvider</strong> adında iki adet şifreleme provider' ı mevcuttur. Bu providerlar, ilgili verinin şifrelenmesinden ve özellikle çalışma zamanında kullanılırken deşifre edilmesinden sorumludur. İlk olarak configurasyon dosyası içerisinde bu yapıları kullanarak gerekli düzenlemeleri yapmamız gerekmektedir.</p>
<p><em>.Net 2.0 için bir console uygulamasına ait app.config konfigurasyon dosyasının örnek içeriği.</em></p>
<pre class="brush:xml;auto-links:false;toolbar:false" contenteditable="false"><?xml version="1.0" encoding="utf-8" ?>
<configuration>
<connectionStrings>
<EncryptedData/>
</connectionStrings>
<protectedData>
<providers>
<add name="prvCon" type="System.Configuration.RsaProtectedConfigurationProvider" keyContainerName="AnahtarDeger" useMachineContainer="true"/>
</providers>
<protectedDataSections>
<add name="connectionStrings" provider="prvCon" inheritedByChildren="false"/>
</protectedDataSections>
</protectedData>
</configuration></pre>
<p>Dikkat ederseniz protectedDataSections sekmesi, protectedData altında yer alan bir bölümdür. Providers kısmında, şifreleme işlemini yapacak olan tipe ait bilgiler yer almaktadır. Bu tipin takma adı name özelliği ile, tipin kendisi type özelliği ile, kullanılacak şifreleme anahtarının adı ise keyContaionerName özellikleri ile tutulur. ProtectedDataSections kısmında, şifrelenecek olan anahtar bilgisi yer alır. Ancak dikkat ederseniz burada bağlantı katarına ilişkin herhangibir bilgi yoktur. Sadece şifrelemeyi yapacak provider' ın takma adını alan name özelliği vardır. Bu özellik ile, provider sekmesinde yer alan tipi işaret ederiz. Peki bağlantı katarımız nerede yer almaktadır?</p>
<p>Dikkat ederseniz dosyanın başında <strong> connectionStrings</strong> isimli takılar arasında yer alan <strong>EncryptedData</strong> isimli bir sekme yer almaktadır. ConnectionStrings konfigurasyon dosyalarına eklenen yeni niteliklerden birisidir ve sadece bağlantı katarı bilgisi ile ilgilidir. Biz biraz sonra yazacağımız kodları çalıştırdığımızda, bağlantı katarımıza ait bilgiler şifrelenerek bu kısım içerisine eklenecektir. Bir başka deyişle EncryptedData kısmı şifrelenen verinin taşıyıcısıdır. Burada dikkat edilmesi gereken noktalardan birisi isim uyumluluğudur. Öyleki connectionStrings takısının ismi, protectedDataSections kısmındaki anahtarın ismi ile aynı olmak zorundadır.</p>
<p>Bağlantı katarının connectionStrings isimli anahtara eklenmesi için ekstra kod yazmamız gerekmektedir. .Net 2.0' da System.Configuration isim alanına bir takım yeni sınıflar eklenmiştir. Biz bu sınıflar yardımıyla, konfigurasyon dosyamıza müdahale ederek, connectionStrings isimli anahtara hem değer ataması yapabilir hemde şifreleme işlemini başlatabiliriz. Bu amaçla aşağıdaki gibi bir kod yazmamız gerekmektedir. Örnek olarak bir console uygulaması göz önüne alınmıştır.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System;
using System.Configuration;
using System.Collections.Generic;
using System.Text;
namespace UsingAppSettings
{
class Program
{
static void Main(string[] args)
{
Configuration cnfg = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None, "");
cnfg.ConnectionStrings.ConnectionStrings.Add(new ConnectionStringSettings("conStr","data source=localhost;database=AdventureWorks;integrated security=SSPI"));
cnfg.Save();
}
}
}</pre>
<p>Çok fazla detaya girmeden yukarıdaki kodlar ile ne yaptığımızdan kısaca bahsetmek istiyorum. İlk olarak bir configuration nesnesine atama yapıyoruz. Bu atamada <strong> ConfigurationManager</strong> sınıfının statik <strong>OpenExeConfiguration</strong> metodunu kullanmaktayız. Bu metod aslında belirtilen configurasyon dosyasını bir Configuration nesnesi olarak atamak için kullanılmaktadır. İkinci parametre konfigurasyon dosyasının yolunu belirtir. Biz aynı uygulama içerisinde olduğumuzdan burayı boş bıraktık. Daha sonra bu nesne üzernde hareket ederek ConnectionStrings sınıfının Add metodunu çağırıyor ve bağlantı katarımızı conStr ismi ile ilgili dosyaya ekliyoruz. Tabiki bu ekleme işlemi sırasında provider' ımız devreye giriyor ve bilgiyi şifreleyerek uygulamaya ait konfigurasyon dosyasına yazıyor. Son olarak Save metodu ile konfigurasyon dosyamızı kaydediyoruz.</p>
<p>Normalde bu tip işlemleri 1.1 versiyonunda yapmak için, yani xml tabanlı konfigurasyon dosyasına kod bazında müdahale etmek için XmlDocument sınıfını kullanarak ayrıştırma işlemlerini (parsing) yapmamız gerekirdi. Yeni versiyonda gördüğünüz gibi bu tarz işlemleri nesne bazında kolayca yapabilmekteyiz.</p>
<table id="table22" style="width: 100%;" border="1" cellspacing="0" cellpadding="5" bgcolor="#ffffff">
<tbody>
<tr>
<td width="53"><img src="/makale/images/dikkat.gif" alt="" width="53" height="53" border="0" /></td>
<td><em>Uygulamayı denediğim .Net 2.0 Beta versiyonunda, Configuration sınıfına erişebilmek için, System.Configuration isim alanını projenin referanslarına Add Reference tekniği ile açıkça eklemem gerekti. Aksi takdirde, System.Configuration üzerinden Configuration ve ConfigurationManager gibi sınıflara direkt olarak erişilemiyor.</em></td>
</tr>
</tbody>
</table>
<p><br />Yazdığımız Console uygulamasını ilk çalıştırdığımızda konfigurasyon dosyalarında her hangibir değişiklik olmadığını görürüz. Özellikle uygulamanın debug klasöründe yer alan ve exe dosyamız ile aynı isme sahip konfigurasyon dosyalarında bir değişiklik olmayacaktır. Bunun sebebi, providerın şifreleme için kullanacağı bir anahtar değerin bulunmayışıdır. Hatırlarsanız provider tanımında <strong>keyContaionerName</strong> isimli bir özellik belirlemiştik. KeyContainerName özelliğine atadığımız AnahtarDeger , RS şifrelemesi için gerekli anahtar kodu taşımak için kullanılacaktır. Peki böyle bir anahtar değerini nasıl elde edebiliriz? Bunun için aspnet_regiis aracını kullabiliriz. Aşağıdaki komut satırı yardımıyla bu işlemi gerçekleştirmekteyiz.</p>
<p><img src="/makale/images/mk139_1.gif" alt="" width="589" height="91" border="0" /></p>
<p>Bu işlemi yaptığımız takdirde uygulamamızın exe kodunun bulunduğu klasörde yer alan aynı isimli konfigurasyon dosyası(UsingAppSettings.EXE.xml) içeriğinin aşağıdaki gibi yazıldığını görürüz. Özellikle EncryptedData ve EncryptedKey kısımlarına dikkat ediniz.</p>
<pre class="brush:xml;auto-links:false;toolbar:false" contenteditable="false"><?xml version="1.0" encoding="utf-8" ?>
<configuration>
<connectionStrings>
<EncryptedData Type="http://www.w3.org/2001/04/xmlenc#Element" xmlns="http://www.w3.org/2001/04/xmlenc#">
<EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#tripledes-cbc" />
<KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
<EncryptedKey Recipient="" xmlns="http://www.w3.org/2001/04/xmlenc#">
<EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5" />
<KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
<KeyName>Rsa Key</KeyName>
</KeyInfo>
<CipherData> <CipherValue>EIskDt2vsIN6xk5qC1S6slkWX97Ua28KGGJTkHx500UsfMbed1TnZpQLYJaJW5XJXQPe
Y3IWm7iKdEZ+idx7THKlAx8eLf/HHDSi/HWp7sSJe6/4vcS6uQ+OyigdLpO6X3LEqoGYhwCNPoZq7e/e
B/P+w9pzLwivdczR1sOYMlQ=</CipherValue>
</CipherData>
</EncryptedKey>
</KeyInfo>
<CipherData> <CipherValue>fKJrwr1tdHNyVH9OYKlFeK7B7C1tzxp2ikrgu+KbCNBHM9fffFGt
EIRc9n0edC8poSskU7+APE18O6SRFqXD3OG0NX+9b65OIIHAXhEHdMYAbejU
VvqGoNw4xkisDfk/CgANrO9kST5f15g/0bH+5Cv83ptAV8mzIvzbvAzd+kpWdk
Q0T99jmiymcwqICBExmvUhT1wSUemYMC2Rzz3
JbJISvyqOZH9AYIxCesaeWwcyOvQfzLyHEw==</CipherValue>
</CipherData>
</EncryptedData>
</connectionStrings>
<protectedData>
<providers>
<add name="prvCon" type="System.Configuration.RsaProtectedConfigurationProvider" keyContainerName="AnahtarDeger" useMachineContainer="true"/>
</providers>
<protectedDataSections>
<add name="connectionStrings" provider="prvCon" inheritedByChildren="false"/>
</protectedDataSections>
</protectedData>
</configuration></pre>
<p><br />Gördüğünüz üzere, içeride şifrelenmiş bir takım veriler bulunmaktadır. Bu veriler aslında, programatik olarak atadığımız bağlantı katarına ilişkin bilgilerdir. Peki bu bilgiyi uygulamamızda nasıl kullanabiliriz? ConfigurationManager sınıfı yardımıyla bu bağlantı bilgisini kod içerisinden okuyabilir ve ilgili bağlantı nesneleri için atayabiliriz. Aşağıdaki örnek kod parçası bu işlemin örnek olarak nasıl yapılabileceğini göstermektedir.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">string conStr=ConfigurationManager.ConnectionStrings["conStr"].ConnectionString.ToString();
using (SqlConnection con = new SqlConnection(conStr))
{
// Bir takım kodlar
}</pre>
<p><br />Yapmış olduklarımızı özetlersek; .Net 2.0, Configuration isim alanı altında yeni özelliklere sahip pek çok tip sunmaktadır. Buradaki sınıfları ve konfigurasyon dosyaları için geliştirilen yeni nitelikleri kullanarak, config dosyalarında tutulan bağlantı katarı bilgilerini yabancı gözlerden saklayabiliriz. Elbetteki, kod içerisinde ConfigurationManager üzerinde ilgili bağlantı katarı bilgisini okuyabilirsiniz. Ancak herkesin görebildiği config dosyalarından bağlantı katarı bilgisini okuyamassınız. Tabi ne yaparsanız yapın yinede güvenliği tam olarak sağlamak söz konusu olamaz. Her zaman için en azından %1 ihtimal ile tüm sistemler güvensidir. Böylece geldik bir makalemizin daha sonuna. Bir sonraki makalemizde görüşünceye dek hepinize mutlu günler dilerim.</p>2005-10-17T09:00:00+00:00ado.net 2.0 betaconnection string securitybsenyurtGüvenlik günümüz uygulamalarında çok önemli bir yere sahiptir. Özellikle veritabanı kullanımını içeren uygulamalarda güvenliğin ayrı bir önemi vardır. Veritabanına gönderilen sorguların korunması, özellikle web servislerinden dönen veri kümelerinin etkin olarak şifrelenmesi gibi durumlar söz konusudur. Güvenlik prosedürü içerisinde yer alan unsurlardan biriside bağlantı bilgilerinin saklanmasıdır. Biz bu makalemizde, .Net 2.0 ile birlikte gelen yeni tekniklerden birisine değinerek, bağlantı bilgisinin (çoğunlukla connection string olarakda ifade edebiliriz) kolay bir şekilde nasıl korunabileceğini incelemeye çalışacağız.https://buraksenyurt.com/pingback.axdhttps://buraksenyurt.com/post.aspx?id=23a01292-fe02-4b06-851b-6c553fac44060https://buraksenyurt.com/trackback.axd?id=23a01292-fe02-4b06-851b-6c553fac4406https://buraksenyurt.com/post/Ado-Net-2-0(Beta-2)-Connection-String-Security-(Baglantc4b1-Katarc4b1-icin-Guvenlik)-bsenyurt-com-dan#commenthttps://buraksenyurt.com/syndication.axd?post=23a01292-fe02-4b06-851b-6c553fac4406https://buraksenyurt.com/post/Ado-Net-2-0-ve-Data-Provider-Independent-Mimari-bsenyurt-com-danAdo.Net 2.0 ve Data Provider-Independent Mimari2005-04-24T12:00:00+00:00bsenyurt<p>Değerli Okurlarım, Merhabalar.</p>
<p>Veritabanı uygulamalarında başımızı ağrıtan noktalardan bir tanesi farklı tipte veritabanı sistemleri kullanan uygulamaların geliştirilmesi sırasında ortaya çıkar. Çoğu zaman geliştirdiğimiz bir ürün Sql sunucları üzerinde yüksek performans gösterecek şekilde çalışmak zorunda iken, aynı ürünün Oracle üzerinde çalıştırılması da istenebilir. Bu durumda ortak bir çözüm olarak OleDb isim alanı altındaki sınıfları kullanmak oldukça mantıklıdır. Çünkü OleDb üzerinden her iki veri sunucusu için gerekli olan veri sağlayıcılarını kullanabiliriz.</p>
<p>Diğer yandan böyle bir yol izlendiğinde direkt olarak OracleClient veya SqlClient isim alanını kullanarak kazanılacak performans avantajı ortadan kaybolacaktır. Peki .Net Framework için geliştirilen başka bir veri sağlayıcı (data-provider) işin içine girerse ne olacaktır. Bu durumda uygulama kodunda ilgili veri sağlayıcısına destek verecek şekilde düzenlemeler yapmamız gerekecektir. Öyleki, SqlClient isim alanı için gerekli Connection nesnesi ile OracleClient isim alanı için gerekli Connection nesnelerinin isimleri farklıdır. Aynı durum Command nesnelerinden tutun da DataReader nesnelerine kadar geçerlidir. İşte bu durum kodlarımızı her veri sağlayıcı için ayrı şekilde düzenlememizi gerektirebilir.</p>
<p>Diğer yandan System.Data.Common isim alanındaki sınıfları kullanarak farklı veri sağlayıcılarına destek verebilecek katmanları geliştirebiliriz. Bu Ado.Net 1.1' de çok esnek olmayan bir mimari üzerinde gerçekleştirilmektedir. Oysa ki Ado.Net 2.0, System.Data.Common isim alanına bir takım yeni özellikler eklemiştir. Bu özellikler arasında yeni eklenen iki sınıf büyük öneme sahiptir. DbProviderFactories ve DbProviderFactory sınıfları. Ado.Net 2.0' da bu sınıfların System.Data.Common isim alanına eklenmelerinin en büyük nedeni, ilgili sistemlerde yüklü olan veri sağlayıcılarının öğrenilebilmesi ve seçilen herhangi bir veri sağlayıcısına özel Command, Connection, DataAdapter gibi veri üzerinde iş yapmamızı sağlayacak sınıfların tekil isimler altında örneklendirilebilmesidir.</p>
<p>Yeni eklenen özelliklerin sağladığı bu imkanlar sayesinde, geliştirilmiş olan bir ürünün farklı veri sağlayıcılar için destek verebilecek şekilde inşa edilmesi daha da kolaylaştırılmıştır. İşin güzel yanı, bu mimarinin performans olarak asıl veri sağlayıcılarına oranla oldukça iyi sonuçlar veriyor olmasıdır. İşte bu makalemizde System.Data.Common isim alanına eklenen bu yeni sınıflar ile neler gerçekleştirebileceğimizi incelemeye çalışacağız. Yeni eklenen sınıfların önemini daha iyi anlayabilmek için aşağıdaki şekli göz önüne alabiliriz.</p>
<p><img src="/makale/images/mk121_3.gif" alt="" width="373" height="481" border="0" /></p>
<p>Aslında uygulamamıza katacağımız veri sağlayıcıdan bağımsız mimari için, yukarıdaki yolu izlememiz yeterli olacaktır. İlk olarak sistemde yüklü olan veri sağlayıcılarını elde edebiliriz. Ya da bunun seçimini kullanıcıya bırakabiliriz. Hangisinin kullanılacağına biz, kullanıcı veya sistemin kendisi karar verebilir. Son olarak seçilen veri sağlayıcı üzerinden gerçekleştireceğimiz veritabanı işlemleri için (örneğin kaynağa bağlantı açmak, komut yürütmek gibi) gerekli olan üreticiyi ( ki bu DbProviderFactory sınıfıdır )hazırlarız. Daha sonra bu yeni sınıf yardımıyla gerekli olan nesneleri üretip kullanırız. Dilerseniz System.Data.Common isim alanına eklenen bu yeni iki sınıfı incelemekle işe başlayalım.</p>
<p>Öncelikle <strong>DbProviderFactories</strong> sınıfnı ele alalım. Bu sınıf sistemde yüklü olan veri sağlayıcılarını elde etmemizi sağlayan <strong>GetFactoryClasses</strong> isimli static bir metoda sahiptir. Bu sınıfın diğer bir static metodu ise <strong>GetFactory</strong>' dir. GetFactory metodu geriye <strong>DbProviderFactory</strong> tipinden bir nesne örneğini döndürür. DbProviderFactory tipinden nesne örnekleri yoluyla, <strong>DbConnection</strong>, <strong>DbCommand</strong> gibi nesneleri elde edebiliriz. Dolayısıyla DbProviderFactories sınıfı yardımıyla bir sistemdeki veri sağlayıcılarını elde edebilir ve seçilen bir veri sağlayacısı için gerekli nesneleri üretmemizi sağlayacak DbProviderFactory sınıfını örnekleyebiliriz. İlk olarak aşağıdaki kod parçasını ele alalım.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System.Data;
using System.Data.Common;
#endregion
namespace UsingDbProviderFactories
{
class Program
{
static void Main(string[] args)
{
DataTable dtProviders = new DataTable();
dtProviders = DbProviderFactories.GetFactoryClasses();
foreach (DataRow drProvider in dtProviders.Rows)
{
for (int i = 0; i < dtProviders.Columns.Count; i++)
{
Console.WriteLine(drProvider[i].ToString());
}
Console.WriteLine("----------");
}
Console.ReadLine();
}
}
}</pre>
<p><img src="/makale/images/mk121_1.gif" alt="" width="588" height="475" border="0" /></p>
<p>Bu kod ile, sistemimizde yüklü olan veri sağlayıcılarını elde etmiş olduk. Dikkat ederseniz GetFactoryClasses metodundan dönen değer <strong>DataTable</strong> tipinden bir nesne örneğidir. Aynı örneği bir windows uygulamasında ele aldığımızda bu geri dönüş tipinden yararlanarak sonuçları veri bağlı kontroller üzerinde (datagridview gibi) gösterebiliriz.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">DataTable dtProviders = new DataTable();
dtProviders = DbProviderFactories.GetFactoryClasses();
grdProviders.DataSource=dtProviders;</pre>
<p><img src="/makale/images/mk121_2.gif" alt="" width="617" height="352" border="0" /></p>
<p>Peki sistemdeki veri sağlayıcılarının elde edilmesinin bize sağlayacağı avantajlar neler olabilir? Herşeyden önce ürünümüzün yüklendiği sistemlerdeki veri sağlayıcılarını görmek ve bunlardan seçişi olan ile uygulamayı çalıştırmak steyebiliriz. Böyle bir durumda GetFactoryClasses metodu işimizi oldukça kolaylaştıracaktır. Diğer yandan, ürünümüzü sisteme yüklerken kurulan herhangi bir konfigurasyon ayarı ile de bir veri sağlayıcıyı seçebiliriz. Çoğunlukla bunu xml içerikli konfigürasyon dosyalarında belirtiriz. (Örneğin app.config dosyası içinde). Uygulamanın hangi veri sağlayıcısını baz alarak devam edeceğine bu dosyadaki ilgili konfigürasyon ayarından karar verebiliriz.</p>
<p>Elbette böyle bir durumda uygulamanın yüklendiği sistemde seçilen veri sağlayıcısının olup olmadığına bakmak için yine yukarıdaki teknik ile elde edilen DataTable nesnesinden faydalanabiliriz. Bu sayede sistemde yüklü olmayan bir veri sağlayıcısı ile devam edilmesini de henüz kurulum aşamasında engellemiş oluruz. Bunun sonrasında kullanıcıya kullanabileceği veri sağlayıcıları alternatif olarak sunabiliriz ve uygun olanı ile devam etmesini sağlayabiliriz. Gelelim, DbProviderFactory sınıfına. Bu sınıf, veritabanına bağlantı açma, sql komutu çalıştırmak gibi işlemleri yürütmemizi sağlayacak DbConnection, DbCommand gibi sınıfların üretilmesini sağlar. Bu sınıfın prototipi aşağıdaki gibidir.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">public abstract class DbProviderFactory</pre>
<p>Görüldüğü gibi DbProviderFactory abstract (soyut) bir sınıftır. Dolayısıyla bu sınıfa ait bir nesne örneğini üretemeyiz. Ancak DbProviderFactories sınıfımıza ait olan <strong>GetFactory</strong> static metodu bizim kullanabileceğimiz bir DbProviderFactory nesnesini sağlayacaktır. GetFactory metodunun aşırı yüklenmiş(overload) iki versiyonu vardır.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">public static DbProviderFactory GetFactory(DataRow data-provider için dataRow);
public static DbProviderFactory GetFactory(string data-provider için invariant name);</pre>
<p>Bu metodlardan ilki DataRow tipinden bir nesne örneğini alır. Bu DataRow nesnesinin temsil ettiği satır aslında DbProviderFactories sınıfının GetFactoryClasses metodundan dönen DataTable üzerindeki satırlardan herhangi biridir. Diğer yandan ikinci versiyonda string tipinden parametrenin alacağı değer, ilgili veri sağlayıcısının sistemdeki sabit adıdır (invariant-name).</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">DbProviderFactory fakto = DbProviderFactories.GetFactory("System.Data.SqlClient");</pre>
<p>Elde ettiğimiz DbProviderFactory nesnesi vasıtasıyla artık veritabanı uygulamamız için gerekli nesneleri örnekleyebiliriz. Örneğin aşağıdaki kod parçası ile bir DbConnection nesnesi elde edilmektedir. DbConnection nesnesi tahmin edeceğiniz gibi ilgili veri kaynağına doğru bağlantı hattı tesis etmemizi sağlar.</p>
<table id="table12" style="width: 100%;" border="1" cellspacing="0" cellpadding="5" bgcolor="#ffffff">
<tbody>
<tr>
<td width="53"><img src="/makale/images/dikkat.gif" alt="" width="53" height="53" border="0" /></td>
<td><strong>DbConnection, DbCommand, DbDataAdapter vb. abstract sınıflardır. Yani aslında bu sınıflara ait nesne örneklerini new operatörü yardımıyla oluşturamayız. Bu sınıflardan faydalanabilmek için DbProviderFactories sınıfının ilgili Create metodlarını kullanırız.</strong></td>
</tr>
</tbody>
</table>
<p><br />Kod parçamız;</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">DbProviderFactory fakto = DbProviderFactories.GetFactory("System.Data.SqlClient");
DbConnection con = fakto.CreateConnection();
con.ConnectionString="data source=localhost;database=AdventureWorks;integrated security=SSPI";
con.Open();
con.Close();
Console.Read();
...</pre>
<p>Bu kod parçasında SqlClient veri sağlayıcısını kullanacak şekilde bir DbConnection nesnesi elde edilmektedir. DbConnection nesnesini elde edebilmek için DbProviderFactory sınıfına ait CreateConnection metodu kullanılmaktadır. Ne yazık ki veri sağlayıcı bağımsız mimarinin de içinden şu an için çıkamayacağı sorunlar var. Bunlardan birisi ConnectionString' in bir veri sağlayıcıdan ötekine farklılık göstermesidir.</p>
<p>Yani Sql sunucularına SqlConnection nesnesi ile bağlantı kurarken kullandığımız Connection String ifadesi, OleDbConnection için olandan farklıdır. Bu sorunu çözmek için DbConnectionStringBuilder sınıfı kullanılmaktadır. Bu sınıf bir connection string içine yazılan özellikleri anahtar-değer (key-value) çiftleri şeklinde temsil eder. Böylece uygun Connection String elde edilebilir. Ancak tabiki öncesinde seçilen veri sağlayıcısının her durumda kontrol edilmesi gerekecektir. Aşağıdaki kod parçası hem SqlClient hem de OleDb için gerekli DbConnection nesnesinin doğru bir şekilde elde edilebilmesini sağlamaktadır. <em>(Burada veri sağlayıcısının seçimi için app.config dosyasını kullandığımıza dikkat edin.)</em></p>
<p><img src="/makale/images/mk121_4.gif" alt="" width="591" height="170" border="0" /></p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">#region Using directives
using System;
using System.Collections.Generic;
using System.Text;
using System.Data.Common;
using System.Configuration;
#endregion
namespace UsingDbProviderFactory
{
class Program
{
static void Main(string[] args)
{
DbProviderFactory fakto;
DbConnection con;
DbConnectionStringBuilder conStr = new DbConnectionStringBuilder();
string secilenProvider = ConfigurationSettings.AppSettings["ProviderTipi"];
fakto = DbProviderFactories.GetFactory(secilenProvider);
con = fakto.CreateConnection();
if (secilenProvider == "System.Data.SqlClient")
{
conStr.Add("data source", "localhost");
conStr.Add("database", "AdventureWorks");
conStr.Add("integrated security", "SSPI");
}
else if (secilenProvider == "System.Data.OleDb")
{
conStr.Add("Provider", "SqlOleDb");
conStr.Add("data source", "localhost");
conStr.Add("database", "AdventureWorks");
conStr.Add("integrated security", "SSPI");
}
else
{
Console.WriteLine("Doğru provider seçilmedi...");
}
con.ConnectionString = conStr.ConnectionString;
try
{
con.Open();
Console.WriteLine("Bağlantı açıldı...");
Console.ReadLine();
}
catch(Exception hata)
{
Console.WriteLine(hata.Message.ToString());
}
finally
{
con.Close();
}
}
}
}</pre>
<p>Uygulamamızda başlangıç olarak veri sağlayıcısını Sql sunucusuna direkt erişim sağlayan SqlClient olarak belirledik. If koşullarında app.config dosyasına eklediğimiz ProviderTipi anahtarının değerine bakarak uygun Connection String ifadesinin oluşturulmasını sağlıyoruz. Eğer OleDb kaynağını kullanarak erişim sağlamak istersek tek yapmamız gereken app.config dosyasında ProviderTipi anahtarının değerini System.Data.OleDb olarak değiştirmek olacaktır.</p>
<table id="table13" style="width: 100%;" border="1" cellspacing="0" cellpadding="5" bgcolor="#ffffff">
<tbody>
<tr>
<td width="53"><img src="/makale/images/dikkat.gif" alt="" width="53" height="53" border="0" /></td>
<td><strong>Seçilen veri sağlayıcısının (Data-Provider) ismi sistem de yüklü olan sabit ismidir. (Invariant Name)</strong></td>
</tr>
</tbody>
</table>
<p><br />Buradaki anahtarların değerlerinin sistemde tanımlı olan invariant-name değerleri olduğunu hatırlatalım. Aslında sistemde yüklü olan veri sağlayıcılarının özellikleri elde edilirken machine.config dosyasındaki ayarlara bakılır. Eğer D:\WINDOWS\Microsoft.NET\Framework\v2.0.40607\CONFIG (Windows 2003 için) adresinden <strong>machine.config</strong> dosyasına bakılırsa sistemde yüklü olan veri sağlayıcılarının listesinin <strong>DbProviderFactories</strong> takısında yer aldığını görebiliriz. İşte veri sağlayıcılarının sabit isimleri buradan alınmaktadır.</p>
<p><img src="/makale/images/mk121_5.gif" alt="" width="626" height="558" border="0" /></p>
<p>DbConnection nesnesi gibi <strong>DbCommand</strong> nesneside veri sağlayıcıdan bağımsız komut setlerinin yürütülmesine imkan sağlamaktadır. Bir DbCommand nesnesinin oluşturuluş biçimi DbConnection nesnesinde olduğu gibidir. Yani bu nesneyi elde edebilmek için DbProviderFactory sınıfının ilgili Create metodunu aşağıdaki gibi kullanırız.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">DbProviderFactory fakto;
DbConnection con;
DbCommand cmd;
DbConnectionStringBuilder conStr = new DbConnectionStringBuilder();
string secilenProvider = ConfigurationSettings.AppSettings["ProviderTipi"];
fakto = DbProviderFactories.GetFactory(secilenProvider);
con = fakto.CreateConnection();
cmd = fakto.CreateCommand();
if (secilenProvider == "System.Data.SqlClient")
{
conStr.Add("data source", "localhost");
conStr.Add("database", "AdventureWorks");
conStr.Add("integrated security", "SSPI");
}
else if (secilenProvider == "System.Data.OleDb")
{
conStr.Add("Provider", "SqlOleDb");
conStr.Add("data source", "localhost");
conStr.Add("database", "AdventureWorks");
conStr.Add("integrated security", "SSPI");
}
else
{
Console.WriteLine("Doğru provider seçilmedi...");
}
con.ConnectionString = conStr.ConnectionString;
try
{
con.Open();
cmd.Connection = con;
cmd.CommandText = "INSERT INTO Personel (AD,SOYAD,MAIL) VALUES ('Burak Selim','Şenyurt','selim(at)buraksenyurt.com')";
int eklenen=cmd.ExecuteNonQuery();
Console.WriteLine("Bağlantı açıldı...");
Console.WriteLine(eklenen + " SATIR EKLENDI");
Console.ReadLine();
}
catch(Exception hata)
{
Console.WriteLine(hata.Message.ToString());
}
finally
{
con.Close();
}</pre>
<p>Görüldüğü gibi tek yapmamız gereken DbProviderFactory sınıfının <strong>CreateCommand</strong> metodunu kullanarak bir DbCommand nesnesini elde etmektir. Daha sonra bu komut nesnesinin kullanacağı sorgu ve bağlantı her zamanki yöntemlerimiz ile belirlenmiştir. Elbetteki, DbConnection nesnesi oluşturulurken yaşanan problemin benzeri burada da söz konusudur. Bu kez Command nesnelerinin parametre isimlendirmeleri bir veri sağlayıcıdan ötekine farklılık göstermektedir.</p>
<p>Örneğin SqlCommand nesnesinde kullanılan parametreler @ ile başlarken, OleDbCommand nesnelerinde sorgu cümleciğindeki parametreler ? ile tanımlanmak zorundadır. Bu sorunu yine yukarıdaki if yapısı ile halledebiliriz. Söz konusu olan parametreleri DbParameter sınıfı ile tanımlayabiliriz. Dolayısıyla yukarıdaki örneği aşağıdaki gibi yazarsak parametre farklılığını bu örnekte geçerli olan veri sağlayıcıları için çözmüş oluruz.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">static void Main(string[] args)
{
DbProviderFactory fakto;
DbConnection con;
DbCommand cmd;
DbParameter prmAd;
DbParameter prmSoyad;
DbParameter prmMail;
DbConnectionStringBuilder conStr = new DbConnectionStringBuilder();
string secilenProvider = ConfigurationSettings.AppSettings["ProviderTipi"];
string cmdQuery;
fakto = DbProviderFactories.GetFactory(secilenProvider);
con = fakto.CreateConnection();
cmd = fakto.CreateCommand();
prmAd = fakto.CreateParameter();
prmSoyad = fakto.CreateParameter();
prmMail = fakto.CreateParameter();
if (secilenProvider == "System.Data.SqlClient")
{
conStr.Add("data source", "localhost");
conStr.Add("database", "AdventureWorks");
conStr.Add("integrated security", "SSPI");
cmdQuery = "INSERT INTO Personel (AD,SOYAD,MAIL) VALUES (@AD,@SOYAD,@MAIL)";
prmAd.ParameterName = "@AD";
prmAd.DbType = DbType.String;
prmAd.Size = 50;
cmd.Parameters.Add(prmAd);
prmSoyad.ParameterName = "@SOYAD";
prmSoyad.DbType = DbType.String;
prmSoyad.Size = 50;
cmd.Parameters.Add(prmSoyad);
prmMail.ParameterName = "@MAIL";
prmMail.DbType = DbType.String;
prmMail.Size = 50;
cmd.Parameters.Add(prmMail);
cmd.Connection = con;
cmd.CommandText = cmdQuery;
}
else if (secilenProvider == "System.Data.OleDb")
{
conStr.Add("Provider", "SqlOleDb");
conStr.Add("data source", "localhost");
conStr.Add("database", "AdventureWorks");
conStr.Add("integrated security", "SSPI");
cmdQuery = "INSERT INTO Personel (AD,SOYAD,MAIL) VALUES (?,?,?)";
prmAd.DbType = DbType.String;
prmAd.Size = 50;
cmd.Parameters.Add(prmAd);
prmSoyad.DbType = DbType.String;
prmSoyad.Size = 50;
cmd.Parameters.Add(prmSoyad);
prmMail.DbType = DbType.String;
prmMail.Size = 50;
cmd.Parameters.Add(prmMail);
cmd.Connection = con;
cmd.CommandText = cmdQuery;
}
else
{
Console.WriteLine("Doğru provider seçilmedi...");
}
con.ConnectionString = conStr.ConnectionString;
try
{
con.Open();
prmAd.Value = "Burak Selim";
prmSoyad.Value = "Şenyurt";
prmMail.Value = "selim(at)buraksenyurt.com";
int eklenen=cmd.ExecuteNonQuery();
Console.WriteLine("Bağlantı açıldı...");
Console.WriteLine(eklenen + " SATIR EKLENDI");
Console.ReadLine();
}
catch(Exception hata)
{
Console.WriteLine(hata.Message.ToString());
}
finally
{
con.Close();
}
}</pre>
<p>DbProviderFactory sınıfı ayrıca DbDataAdapter nesnelerini elde edebilmemizi sağlayan <strong>CreatedDataAdapter</strong> isimli bir metoda sahiptir. Bu sayede bağlantısız katman nesneleri ile veri kaynağı arasındaki veri taşıma işlemlerini gerçekleştirebileceğimiz DataAdapter nesne örneklerini veri sağlaycısından bağımsız olacak şekilde elde edebiliriz. Aşağıdaki kod parçası ile bu işlemin nasıl gerçekleştirilebileceğini görmektesiniz. Bu örnekte kullanıcının seçtiği veri sağlayıcısının sistemde yüklü olup olmadığınıda kontrol ediyoruz. Örneğimizde veri sağlayıcılarını OleDb ve SqlClient ile sınırladık.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">#region Using directives
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.Common;
using System.Drawing;
using System.Windows.Forms;
#endregion
namespace UsingDbDataAdapter
{
partial class Form1 : Form
{
private DbProviderFactory fakto;
private DbConnection con;
private DbConnectionStringBuilder conStr;
private DbCommand cmd;
private DbDataAdapter da;
private DataTable dt;
public Form1()
{
InitializeComponent();
}
private bool VeriSaglayiciKontrol(string invariantName)
{
int varmi = DbProviderFactories.GetFactoryClasses().Select("InvariantName='" + invariantName + "'").Length;
if (varmi == 0)
return false;
else
return true;
}
private void DbConnectionOlustur(DbConnectionStringBuilder connectionString)
{
con = fakto.CreateConnection();
con.ConnectionString = connectionString.ConnectionString;
}
private void DbCommandOlustur(string sqlQuery)
{
cmd = fakto.CreateCommand();
cmd.Connection = con;
cmd.CommandText = sqlQuery;
}
private void FactoryOlustur(string veritabaniAdi)
{
if (radOleDb.Checked == true)
{
if (VeriSaglayiciKontrol("System.Data.OleDb"))
{
fakto = DbProviderFactories.GetFactory("System.Data.OleDb");
conStr = new DbConnectionStringBuilder();
conStr.Add("provider", "SqlOleDb");
conStr.Add("data source", "localhost");
conStr.Add("database", veritabaniAdi);
conStr.Add("integrated security", "SSPI");
DbConnectionOlustur(conStr);
}
}
if (radSql.Checked == true)
{
if (VeriSaglayiciKontrol("System.Data.SqlClient"))
{
fakto = DbProviderFactories.GetFactory("System.Data.SqlClient");
conStr = new DbConnectionStringBuilder();
conStr.Add("data source", "localhost");
conStr.Add("database", veritabaniAdi);
conStr.Add("integrated security", "SSPI");
DbConnectionOlustur(conStr);
}
}
}
private void DbDataAdapterOlustur()
{
da = fakto.CreateDataAdapter();
da.SelectCommand = cmd;
}
private void btnVeriCek_Click(object sender, EventArgs e)
{
FactoryOlustur("AdventureWorks");
DbCommandOlustur("SELECT TOP 5 FirstName,MiddleName,LastName FROM Person.Contact");
DbDataAdapterOlustur();
dt = new DataTable();
da.Fill(dt);
grdVeriler.DataSource = dt;
}
}
}</pre>
<p><img src="/makale/images/mk121_6.gif" alt="" width="495" height="294" border="0" /></p>
<p>Uygulamamızı çalıştırdığımızda ister Sql veri sağlayıcısını ister OleDb veri sağlayıcısını seçelim aynı DbConnection, DbCommand ve DbDataAdapter nesneleri üzerinden işlem yapıyor olacağız. İşte bu yeni mimarinin bize sağladığı en büyük avantajdır.</p>
<p>Son olarak bir DataReader nesnesini veri sağlayıcıdan bağımsız mimaride nasıl kullanabileceğimizi inceleyeceğiz. Aslında şu an için bu mimaride geliştirilmiş bir DbDataReader sınıfı yok. Bunun yerine IDataReader arayüzünü kullanacağız. Dolayısıyla arayüzü kullanacağımız yere kadar yaptığımız işlemler yukarıdaki örnektekinden farksız olacak.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">private void btnIDataReaderIleCek_Click(object sender, EventArgs e)
{
FactoryOlustur("AdventureWorks");
DbCommandOlustur("SELECT TOP 5 FirstName,MiddleName,LastName FROM Person.Contact");
using (con)
{
con.Open();
using (IDataReader dr = cmd.ExecuteReader())
{
while (dr.Read())
{
//bir takım okuma işlemleri.
}
}
}
}</pre>
<p>Ado.Net 2.0 için veri sağlayıcıdan bağımsız mimariyi kullanmak oldukça kolay ve yararlı görünüyor. Özellikle yeni eklenen sınıflar bu tarz yapıları oluşturmamızı son derece kolaylaştırmış. Burada kafamızı kurcalayan tek konu performans farklılıkları. Microsoft kaynaklarına ve otoritelerine göre çok büyük performans kayıpları olmadığı(olmayacağı) söyleniyor. Açıkçası bu konunun ilerleyen zamanlarda daha da netleşeceği görüşündeyim. Yine de yeni kolaylıkların özellikle farklı veri sunucularına bağlanmamızı gerektiren durumlarda çok başarılı olacağını söyleyebilirim.</p>
<p>Herşeyden önemlisi, hangi veri sağlayıcısı olursa olsun aynı DbConnection, DbCommand veya DbDataAdapater nesnelerini kullanmak son derece büyük bir avantajdır. Mimarinin şu an için sadece başında olduğumuzu düşünüyorum. System.Data.Common isim alanına katılacak daha bir çok özellik olabilir. Örneğin buraya özel bir DbDataReader sınıfının oluşturulması gibi. Şunu da hatırlatmakta fayda var. Buradaki kod örnekleri ve kullanılan sınıflar, beta sürümüne aittir. Ürün piyasaya çıktığında bu sınıfların isimlerinde veya kullanılış şekillerinde değişiklikler olabilir. Bir sonraki makalemizde görüşünceye dek hepinize mutlu günler dilerim.</p>2005-04-24T12:00:00+00:00ado.net 2.0data providersdbconnectiondbcommanddbdataadapterdbproviderfactorybsenyurtVeritabanı uygulamalarında başımızı ağrıtan noktalardan bir tanesi farklı tipte veritabanı sistemleri kullanan uygulamaların geliştirilmesi sırasında ortaya çıkar. Çoğu zaman geliştirdiğimiz bir ürün Sql sunucları üzerinde yüksek performans gösterecek şekilde çalışmak zorunda iken, aynı ürünün Oracle üzerinde çalıştırılması da istenebilir. Bu durumda ortak bir çözüm olarak OleDb isim alanı altındaki sınıfları kullanmak oldukça mantıklıdır. Çünkü OleDb üzerinden her iki veri sunucusu için gerekli olan veri sağlayıcılarını kullanabiliriz.https://buraksenyurt.com/pingback.axdhttps://buraksenyurt.com/post.aspx?id=f4679e74-a12d-48ff-a7be-b698d62a1ed70https://buraksenyurt.com/trackback.axd?id=f4679e74-a12d-48ff-a7be-b698d62a1ed7https://buraksenyurt.com/post/Ado-Net-2-0-ve-Data-Provider-Independent-Mimari-bsenyurt-com-dan#commenthttps://buraksenyurt.com/syndication.axd?post=f4679e74-a12d-48ff-a7be-b698d62a1ed7https://buraksenyurt.com/post/Ado-Net-2-0-da-Transaction-Kavramc4b1-bsenyurt-com-danAdo.Net 2.0' da Transaction Kavramı2005-04-17T12:00:00+00:00bsenyurt<p>Değerli Okurlarım, Merhabalar.</p>
<p>Transaction kavramı ve kullanımı veritabanı programcılığının olmazsa olmaz temellerinden birisidir. Veritabanına doğru gerçekleştirilen işlemlerin tamamının onaylanması veya içlerinden birisinde meydana gelecek bir istisna sonrası o ana kadar yapılan tüm işlerin geri alınması veri bütünlüğünü korumak açısından son derece önemlidir. Ado.Net 1.0/1.1 için transactionların kullanımı, seçilen veri sağlayıcısına göre farklı sınıfların kullanılmasını gerektirir.</p>
<p>Örneğin SqlClient isim alanındaki sınıfları kullandığınız bir veritabanı uygulamanız var ise, SqlTransaction sınıfını kullanırsınız. Oysa Ado.Net 2.0' da transaction mimarisi Ado.Net' ten ayrıştırılmış, bir başka deyişle provider' lardan bağımsız hale getirilmiştir. Aslında en büyük değişiklik transaction işlemlerinin artık System.Transactions isim alanı altında yer alan sınıflar ile gerçekleştirilecek olmasıdır. Bir diğer büyük değişiklik transaction' ların yazım tekniği ile ilgilidir. Ado.Net 2.0 da transaction oluşturacak ve kullanacak kodları çok daha basit biçimde yazabilirsiniz. Bunu ilerleyen paragraflarda sizde göreceksiniz.</p>
<p>Ado.Net 2.0' da transaction' ların kullanımı ile ilgili belkide en önemli özellik dağıtık (distributed) transaction' ların uygulanış biçimidir. Normal şartlarda Ado.Net 1.0/1.1 için dağıtık transaction' ları kullanırken System.EnterpriseServices isim alanını kullanan COM+ nesnelerini oluşturur ve ContextUtil sınıfına ait metodlar yardımıyla dağıtık transaction' ları kontrol altına alırız. Bu yapı özellikle yazımı ve oluşturulması itibariyle karmaşık olup kullanımı da zor olan bir yapıdır. Oysa ki Ado.Net 2.0 olayı çok daha akıllı bir şekilde ele alır. Ado.Net 2.0' a göre, iki tip transaction olabilir. Tek veri kaynağı üzerinde çalışan <strong>LightWeight Transaction</strong>' lar ve dağıtık transactionlar gibi davranan <strong>OleTx Transaction</strong>' lar.</p>
<p>LightWeight Transaction' lar tek bir uygulama alanında (application-domain) çalışan iş parçalarıdır. OleTx tipi Transaction' lar ise birden fazla uygulama alanında (application-domain) veya aynı uygulama alanında iseler de farklı veri kaynaklarını kullanan transaction' lar dır. Dolayısıyla OleTx tipi transaction' ları Distributed Transaction' lara benzetebiliriz. Ancak arada önemli farklarda vardır. İlk olarak Ado.Net 1.0/1.1' de tek veri kaynağı üzerinde çalışan bir transaction' ın nasıl uygulandığını hatırlayalım.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">SqlConnection con = new SqlConnection(connectionString);
con.Open();
SqlCommand cmd = new SqlCommand(sqlSorgusu,con);
SqlTransaction trans;
trans = con.BeginTransaction();
cmd.Transaction = trans;
try
{
cmd.ExecuteNonQuery();
trans.Commit();
}
catch(Exception e)
{
trans.Rollback();
}
finally
{
con.Close();
}</pre>
<p>Yukarıdaki örnekte yerel (local) makine üzerinde tekil olarak çalışan bir transaction için gerekli kodlar yer almaktadır. Eğer komutun çalıştırılması sırasında herhangi bir aksilik olursa, catch bloğu devreye girer ve transaction geri çekilerek (RollBack) o ana kadar yapılmış olan tüm işlemler iptal edilir. Tam tersine bir aksilik olmaz ise transaction nesnesinin Commit metodu kullanılarak işlemler onaylanır ve veritabanına yazılır. Gelelim bu tarz bir örneğin Ado.Net 2.0' da nasıl gerçekleştirileceğine. Her şeyden önce bu sefer <strong>System.Transactions</strong> isim alanını kullanmamız gerekiyor. Şu anki versiyonda bu isim alanını uygulamamıza harici olarak referans etmemiz gerekmekte.</p>
<p><img src="/makale/images/mk120_1.gif" alt="" width="467" height="375" border="0" /></p>
<p>Daha sonra ise aşağıdaki kodlara sahip olan Console uygulamasını oluşturalım.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">#region Using directives
using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Data.SqlClient;
using System.Transactions;
#endregion
namespace Transactions
{
class Program
{
static void Main(string[] args)
{
using (TransactionScope tsScope = new TransactionScope())
{
using (SqlConnection con = new SqlConnection("data source=localhost;database=AdventureWorks;integrated security=SSPI"))
{
SqlCommand cmd = new SqlCommand("INSERT INTO Personel (AD,SOYAD,MAIL) VALUES ('Burak Selim','ŞENYURT','selim(at)buraksenyurt.com')", con);
con.Open();
cmd.ExecuteNonQuery();
tsScope.Complete();
}
}
}
}
}</pre>
<p>Görüldüğü gibi Ado.Net 1.0/1.1' e göre oldukça farklı bir yapı kullanılmıştır. <strong>TransactionScope</strong> sınıfına ait nesne örnekleri kendisinden sonra açılan bağlantıları otomatik olarak bir transaction scope(faaliyet alanı) içerisine alır. Buradaki temel mantık bir veya daha fazla transaction' ı kullanacak olan bir scope (faaliyet alanı) oluşturmak ve hepsi için gereki bir takım özelliklerin ortak olarak belirlenmesini sağlamaktır. TransactionScope sınıfına ait nesne örneklerini oluşturabileceğimiz pek çok aşırı yüklenmiş (overload) yapıcı (constructor) metod mevcuttur.</p>
<p>Bu yapıcı metotlar yardımıyla, scope (faaliyet alanı) için pek çok özellik tanımlayabilirisiniz. TransactionScope, <strong>IDisposable</strong> arayüzünü (interface) uygulayan bir sınıftır. Bir TransactionScope nesnesi oluşturululduğunda ve bu nesnenin oluşturduğu faaliyet alanına ilk transaction eklendiğinde devam eden komutlara ilişkin transaction' larda otomatik olarak var olan bu scope (faaliyet alanı) içerisinde gerçekleşmektedir. Bu elbetteki varsayılan durumdur. Ancak dilerseniz <strong>TransactionScopeOption</strong> numaralandırıcısı (enumerator) yardımıyla, yeni açılan transaction' ların var olan scope' a (faaliyet alanına) dahil edilip edilmeyeceğini belirleyebilirsiniz.</p>
<p>Eğer veritabanına doğru çalışan komutlarda herhangi bir aksaklık olursa uygulama otomatik olarak using bloğunu terk edecektir. Bu durumda son satırdaki Complete metodu çağırılabilir hale gelecektir. Bu da transaction içerisindeki işlemlerin commit edilebileceği anlamına gelir. Bu yeni teknik, eskisine göre özellikle kod yazımını hem kolaylaştırmış hem de profesyonelleştirmiştir. Bununla birlikte var olan alışkanlıklarımızdan birisi meydana gelecek aksaklık nedeni ile kullanıcının bir istisna mekanizması ile uyarılabilmesini sağlamak veya başka işlemleri yaptırmaktır. Dolayısıyla aynı örneği aşağıdaki haliyle de yazabiliriz.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using (TransactionScope tsScope = new TransactionScope())
{
SqlConnection con = new SqlConnection("data source=localhost;database=AdventureWorks;integrated security=SSPI");
SqlCommand cmd = new SqlCommand("INSERT INTO Personel (AD,SOYAD,MAIL) VALUES ('Burak Selim','ŞENYURT','selim(at)buraksenyurt.com')", con);
try
{
con.Open();
cmd.ExecuteNonQuery();
tsScope.Complete();
}
catch (TransactionException hata)
{
Console.WriteLine(hata.Message.ToString());
}
finally
{
con.Close();
}
}</pre>
<p>Burada oluşabilecek istisnayı yakalamak istediğimiz için bir try-catch-finally bloğunu kullandık. Ancak dikkat ederseniz TransactionScope nesnemiz yine using bloğu içerisinde kullanılmıştır ve transaction' ı commit etmek için Complete metodu çağırılmıştır. Her iki örnekte LightWeight Transaction tipindedir. Çünkü tek bir connection ve yine tek bir application domain mevcuttur. Elbette birden fazla komutun yer aldığı transaction' larda aynı teknik kullanılarak oluşturulabilir. Ancak farklı veritabanlarına bağlanan aksiyonlar söz konusu ise Transaction' ların oluşturulması ve arka planda gerçekleşen olaylar biraz farklıdır. Şimdi bu durumu örneklemek için aşağıdaki Console uygulamasını oluşturalım.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using (TransactionScope tsScope = new TransactionScope())
{
using (SqlConnection conAdventureWorks = new SqlConnection("data source=localhost;database=AdventureWorks;integrated security=SSPI"))
{
SqlCommand cmdAdvPersonel = new SqlCommand("INSERT INTO Personel (AD,SOYAD,MAIL) VALUES ('Burak Selim','ŞENYURT','selim(at)buraksenyurt.com')", conAdventureWorks);
conAdventureWorks.Open();
cmdAdvPersonel.ExecuteNonQuery();
using (SqlConnection conNorthwind = new SqlConnection("data source=localhost;database=Northwind;integrated security=SSPI"))
{
conNorthwind.Open();
SqlCommand cmdNrtPersonel = new SqlCommand("UPDATE Personel SET AD='Gustavo' WHERE ID=1", conNorthwind);
cmdNrtPersonel.ExecuteNonQuery();
}
}
tsScope.Complete();
}</pre>
<p>Görüldüğü gibi ilk yazdığımız örnekten pek farkı yok. Sadece iç içe geçmiş (nested) bir yapı var. Aynı application domain' e ait iki farklı database bağlantısı ihtiyacı olduğu için burada bir distributed transaction kullanılması gerekiyor. Normalde <strong>DTC (Distributed Transaction Coordinator)</strong> kontrolünde ele alınacak bu transaction' ları oluşturmak için Ado.Net 1.0/1.1' de bayağı uğraşmamız gerekecekti. Oysa ki Ado.Net 2.0' da herhangi bir şey yapmamıza gerek yok. Çünkü Ado.Net 2.0 otomatik olarak OleTx tipinde bir transaction oluşturacaktır. Nasıl mı? Bunu gözlemlemenin en iyi yolu, uygulama koduna <strong>breakpoint</strong> koymak ve <strong>Administrative Tool->Component Services</strong>' dan açılacak olan transaction' ları izlemekle olacaktır. İlk olarak kodumuza bir breakpoint koyalım ve adım adım uygulamamızda ilerleyelim.</p>
<p><img src="/makale/images/mk120_3.gif" alt="" width="566" height="357" border="0" /></p>
<p>Sarı noktaya gelinceye kadar ve conNorthwind isimli bağlantı Open metodu ile açılıncaya kadar aktif olan tek bir Connection nesnesi vardır. Buraya kadar transaction' ımız LightWeight tipindedir. Ancak ikinci bağlantıda açıldıktan sonra bu bağlantı TransactionScope nesnemize otomatik olarak eklenecektir. İşte sarı noktada iken Administrative Tool->Component Services' a bakarsak, <strong>TransactionList</strong> içerisinde, DTC kontrolü altında kullanıcı tanımlı bir transaction' ın otomatik olarak açıldığını görürüz.</p>
<p><img src="/makale/images/mk120_2.gif" alt="" width="576" height="259" border="0" /></p>
<p>Artık her iki connection üzerinde çalışan komutlar DTC altında oluşturulan bu Connection' ın kontrolü altındadır. İşlemler başarılı bir şekilde tamamlanırsa TransactionScope nesnesine ait using bloğunun sonundaki kod satırı çalışacaktır. Yani Complete metodu yürütülecektir. Bu sayede işlemler Commit edilir ve böylece tüm işlemler onaylanarak veritabanlarına yazılır. Using bloğundan çıkıldıktan sonra ise, DTC kontrolü altındaki bu transaction otomatik olarak kaldırılır. DTC kontrolü altında oluşturulan transaction' lar her zaman unique bir ID değerine sahip olur. Böylece sunucu üzerinde aynı anda çalışan birden fazla distributed transaction var ise, bunların birbirlerinden ayırt edilmeleri ve uygun olan application domain' ler tarafından ele alınmaları sağlanmış olur.</p>
<p>TransactionScope nesnesinin belirlediği scope (faaliyet alanı) altında açılan transaction' lar bir takım özelliklere sahiptir. Örneğin eskiden olduğu gibi IsolationLevel değerleri veya TimeOut süreleri vardır. Dilersek oluşturulacak bir TransactionScope nesnesinin ilgili değerlerini önceden manuel olarak ayarlayabilir böylece bu scope (faaliyet alanı) içindeki transaction' ların ortak özelliklerini belirleyebiliriz. Bunun için <strong>TransactionOptions</strong> sınıfına ait nesne örnekleri kullanılır.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">TransactionOptions trOptions = new TransactionOptions();
trOptions.IsolationLevel = System.Transactions.IsolationLevel.ReadCommitted;
trOptions.Timeout = new TimeSpan(0, 0, 30);
using (TransactionScope tsScope = new TransactionScope(TransactionScopeOption.RequiresNew,trOptions))
{
using (SqlConnection con = new SqlConnection("data source=localhost;database=AdventureWorks;integrated security=SSPI"))
{
SqlCommand cmd = new SqlCommand("INSERT INTO Personel (AD,SOYAD,MAIL) VALUES ('Burak Selim','ŞENYURT','selim(at)buraksenyurt.com')", con);
con.Open();
cmd.ExecuteNonQuery();
tsScope.Complete();
}
}</pre>
<p>Yukarıdaki örnekte, oluşturulacak olan transaction' ın izolasyon seviyesi ve zaman aşımı süreleri belirlenmiş ve TransactionScope nesnemiz bu opsiyonlar çerçevesinde aşağıdaki overload metot versiyonu ile oluşturulmuştur.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">TransactionScope tsScope = new TransactionScope(TransactionScopeOption.RequiresNew,trOptions)</pre>
<p>Burada ilk parametre birden fazla TransactionScope nesnesinin yer aldığı iç içe geçmiş yapılarda büyük önem arzetmektedir. Bu seçenek ile yeni açılan transaction scope' un (faaliyet alanının) var olan önceki bir transaction faaliyet alanına katılıp katılmayacağı gibi seçenekler belirlenir. Örneğin aşağıdaki basit yapıyı ele alalım. Burada ilk using bloğu ile bir Transaction Scope oluşturulur. İkinci using ifadesine gelindiğinde yeni transaction scope' un önceki transaction scope' a ilave edileceği TransactionScopeOption parametresi ile belirlenir. Nitekim <strong>Required</strong> değeri, yeni scope' u var olan önceki scope' a ekler. Eğer var olan bir scope yok ise yeni bir tane oluşturur. Elbetteki burada akla gelen soru scope içindeki transaction' ların kimin tarafından onaylanacağıdır. Burada root scope kim ise ona ait Complete metodu devreye girecektir.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using(TransactionScope faaliyetAlani1 = new TransactionScope())
{
...
using(TransactionScope faaliyetAlani2 = new TransactionScope(TransactionScopeOption.Required))
{
...
}
}</pre>
<p><img src="/makale/images/mk120_4.gif" alt="" width="391" height="366" border="0" /></p>
<p>Şimdi yukarıdaki nested scope yapısı içine üçüncü bir scope daha ilave edelim. Ancak yeni TransactionScope için TransactionScopeOption değerini <strong>RequiresNew</strong> olarak belirleyelim.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using(TransactionScope faaliyetAlani1 = new TransactionScope())
{
...
using(TransactionScope faaliyetAlani2 = new TransactionScope(TransactionScopeOption.Required))
{
...
}
using(TransactionScope faaliyetAlani3 = new TransactionScope(TransactionScopeOption.RequiresNew))
{
...
}
}</pre>
<p>Bu durumda yapımız aşağıdaki gibi olacaktır.</p>
<p><img src="/makale/images/mk120_5.gif" alt="" width="391" height="436" border="0" /></p>
<p>Dilersek transaction' ları açıkça(explicit) kendimizde manüel olarak oluşturabiliriz. Şu ana kadar yaptığımız örneklerde implicit bir yaklaşım izledik. Yani ilgili transaction ve bunlara ait kaynakların otomatik olarak oluşturulmasını sağladık. Örneğin aşağıdaki kodlarda transaction' lar manuel olarak oluşturulmuştur. <em>(Örnek .Net 2.0 Beta sürümünde denenmiştir.)</em></p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">ICommittableTransaction trans = Transaction.Create();
try
{
using (SqlConnection conNorthwind = new SqlConnection("data source=localhost;database=Northwind;integrated security=SSPI"))
{
SqlCommand cmdInsert = new SqlCommand("INSERT INTO Personel (AD,SOYAD) VALUES ('Burak Selim','Şenyurt')", conNorthwind);
conNorthwind.Open();
conNorthwind.EnlistTransaction(trans);
cmdInsert.ExecuteNonQuery();
}
using (SqlConnection conAdv = new SqlConnection("data source=localhost;database=AdventureWorks;integrated security=SSPI"))
{
SqlCommand cmdInsert = new SqlCommand("INSERT INTO Personel (AD,SOYAD,MAIL) VALUES ('Cimi','Keri','cimi@keri.com')", conAdv);
conAdv.Open();
conAdv.EnlistTransaction(trans);
cmdInsert.ExecuteNonQuery();
}
trans.Commit();
}
catch
{
trans.Rollback();
}</pre>
<p><strong>ICommittableTransaction </strong>arayüzü bir Transaction Scope' un oluşturulmasını sağlar. Bunun için Transaction sınıfına ait Create metodu kullanılır. Create metodu varsayılan ayarları ile birlikte bir Scope oluşturacaktır. Eğer bu Scope' a transaction' lar eklemek istersek, ilgili bağlantıları temsil eden Connection nesnelerinin <strong>EnlistTransaction</strong> metodunu kullanırız. EnlistTransaction metodu parametre olarak transaction Scope' u temsil eden <strong>ICommittableTransaction </strong>arayüzü tipinden nesne örneğini alır. Elbette arayüze eklenen transaction' lara ait işlemlerin onaylanmasını sağlamak için arayüze ait <strong>Commit</strong> metodu kullanılır. Tam tersine bir sorun çıkar ve veritabanına doğru yapılan işlemlerden birisi gerçekleştirilemez ise o ana kadar yapılan işlemlerin geri alınması <strong> ICommittableTransaction</strong> arayüzüne ait <strong>RollBack</strong> metodu ile sağlanmış olur.</p>
<p>Bu makalemizde Transaction mimarisinin Ado.Net 2.0' daki yüzünü incelemeye çalıştık. Görüldüğü gibi kod yazımının basitleştirilmesinin yanında, özellikle EnterpriceServices bağımlılığından kurtularak Distributed Transaction' ların otomatik hale getirilmesi ve Transaction Scope kavramının getirilmesi göze çarpan önemli özellikler. Burada bahsedilen özellikler teorik olarak fazla bir değişikliğe uğramayacaktır. <em>Ancak bazı üyelerin isimlerin değişiklik beklenmektedir.</em> Örneğin ICommittableTransaction arayüzü yerine CommittableTransaction sınıfının geleceği düşünülmektedir. Bir sonraki makalemizde görüşmek dileğiyle hepinize mutlu günler dilerim.</p>2005-04-17T12:00:00+00:00ado.net 2.0transactionoletx transactionlightweight transactionbsenyurtTransaction kavramı ve kullanımı veritabanı programcılığının olmazsa olmaz temellerinden birisidir. Veritabanına doğru gerçekleştirilen işlemlerin tamamının onaylanması veya içlerinden birisinde meydana gelecek bir istisna sonrası o ana kadar yapılan tüm işlerin geri alınması veri bütünlüğünü korumak açısından son derece önemlidir. Ado.Net 1.0/1.1 için transactionların kullanımı, seçilen veri sağlayıcısına göre farklı sınıfların kullanılmasını gerektirir.https://buraksenyurt.com/pingback.axdhttps://buraksenyurt.com/post.aspx?id=88501199-3648-46cb-9285-8c6247e754190https://buraksenyurt.com/trackback.axd?id=88501199-3648-46cb-9285-8c6247e75419https://buraksenyurt.com/post/Ado-Net-2-0-da-Transaction-Kavramc4b1-bsenyurt-com-dan#commenthttps://buraksenyurt.com/syndication.axd?post=88501199-3648-46cb-9285-8c6247e75419