Makale Özeti

Önceki makalede transaction tabanlı kilitleme yöntemi ele alınmış, ancak ölçeklenebilirliği ile karakterize, çok kullanıcılı uygulamalar için yarattığı soruna optimistik kilitlemenin çözüm olacağı sonucuna varılmıştı. Bu makalede ise, VB ve C# kullanılarak, optimistik kilitleme yöntemi ele alınmaktadır.

Makale

ADO .NET te Bağlantısız Veriler Üzerindeki Kilitleme İşlemleri

        ADO .NET TE TRANSACTION TABANLI İŞLEMLER - 2 başlıklı makalede, genel anlamı ile kilitleme işlemine uygulamada neden ihtiyaç duyulduğu ve ADO .NET te bağlantısız veriler üzerinde transaction tabanlı (pesimistik) kilitleme işlemlerinin nasıl gerçekleştirildiği üzerinde durulmuştu. Ancak makalenin bitiminde, bu yöntemin özellikle fazla sayıda kullanıcıya hizmet vermesi düşünülen uygulamalarda çok dikkat edilerek kullanılması, hatta mümkünse kaçınılması gereken bir yöntem olduğu sonucuna varılmıştı.

        Bunun nedeni; transaction tabanlı kilitlemenin (uygulandığı süreyle orantılı olarak) uygulamanın ölçeklenebilirliğini olumsuz yönde etkileyen bir işlem olmasıdır. Eğer bir kullanıcının elde ettiği veriler üzerinde uzun süreli bir kilitleme yapılıyorsa, bu; diğer kullanıcıların bu süre zarfında o verileri kullanamayacağı (değiştiremeyeceği) anlamına gelir. Buradaki sorun; doğal olarak kilitleme süresinin uzunluğudur.

        Kilitleme işlemi; uygulama için kaçınılmaz ise, transaction tabanlı (pesimistik) kilitleme yerine, optimistik tabanlı kilitleme yöntemi tercih edilerek, ölçeklenebilirlik sorunu bertaraf edilebilir. Pesimistik kilitleme yöntemi ile transaction mekanizması arasında var olan ilişkiye benzer bir ilişki optimistik kilitleme ile exception mekanizması arasında söz konusudur.

        Bu yöntemde şayet iki kullanıcı, herhangi bir t anında, aynı kayda ait aynı veya farklı alanlardaki verileri değiştirmek isterse; herhangi bir bekleme yaşanmaksızın, ikincinin yaptığı işlem veri tabanına yansıtılmaz ve bir exception üretilir. Üretilen exception, DBConcurrencyException sınıfı türünde bir nesne ile yakalanarak değerlendirilebilir.


Örnek : Aşağıdaki uygulamada her iki kullanıcı da aynı kaydı güncellemeye çalışmaktadır. Ancak sadece ilk kullanıcı başarılı olmaktadır. Diğer kullanıcı ise; başarısız olmakta ve bir exception üretilmektedir. Üretilen exception, DBConcurrencyException sınıfı türünde bir nesne ile değerlendirilmektedir.

[VB.NET]
Sub Main()
    Dim sSQL As String = "Select * From Employees"
    Dim cnStr As String = "data source=localhost;"
    cnStr &= "database=northwind;integrated security=sspi"
    Dim oCnn1 As New SqlConnection 
    Dim oCnn2 As New SqlConnection
    oCnn1.ConnectionString = cnStr
    oCnn2.ConnectionString = cnStr
    oCnn1.Open() : oCnn2.Open()

    Dim oAdp1 As SqlDataAdapter
    Dim oAdp2 As SqlDataAdapter
    oAdp1 = New SqlDataAdapter(sSQL, oCnn1)
    oAdp2 = New SqlDataAdapter(sSQL, oCnn2)

    Dim oCb1 As New SqlCommandBuilder(oAdp1)
    Dim oCb2 As New SqlCommandBuilder(oAdp2)

    Dim oDs1 As New DataSet
    Dim oDs2 As New DataSet
    oAdp1.Fill(oDs1)
    oAdp2.Fill(oDs2) 

    İlk kullanıcının yaptığı güncelleme
    oDs1.Tables(0).Rows(3)(1) = "User1"
    oAdp1.Update(oDs1)
 
    2. kullanıcının yaptığı güncelleme
    oDs2.Tables(0).Rows(3)(1) = "User2" 
    Try
        oAdp2.Update(oDs2)
    Catch ex As DBConcurrencyException
        Console.WriteLine(ex.Message) 
    End Try
End Sub 

Sekil 1 

[C#]
static void Main(string[] args)

    string sSQL = "Select * From Employees"; 
    string cnStr = "data source=localhost;"; 
    cnStr += "database=northwind;integrated security=sspi"; 
    SqlConnection oCnn1 = new SqlConnection(); 
    SqlConnection oCnn2 = new SqlConnection(); 
    oCnn1.ConnectionString = cnStr; 
    oCnn2.ConnectionString = cnStr; 
    oCnn1.Open(); 
    oCnn2.Open();

    SqlDataAdapter oAdp1; 
    SqlDataAdapter oAdp2; 
    oAdp1 = new SqlDataAdapter(sSQL, oCnn1); 
    oAdp2 = new SqlDataAdapter(sSQL, oCnn2);

    SqlCommandBuilder oCb1 = new SqlCommandBuilder(oAdp1); 
    SqlCommandBuilder oCb2 = new SqlCommandBuilder(oAdp2);

    DataSet oDs1 = new DataSet(); 
    DataSet oDs2 = new DataSet();

    oAdp1.Fill(oDs1); 
    oAdp2.Fill(oDs2);

    //İlk kullanıcının yaptığı güncelleme 
    oDs1.Tables[0].Rows[3][1] = "User1"; 
    oAdp1.Update(oDs1); 

    //2. kullanıcının yaptığı güncelleme 
    oDs2.Tables[0].Rows[3][1] = "User2";

    try 
    { 
        oAdp2.Update(oDs2); 
    } 
    catch (DBConcurrencyException ex) 
    { 
        Console.WriteLine(ex.Message); 
    }
}


         Eğer ilk kullanıcı bir güncelleme yapmamış olsaydı, bu kez herhangi bir exception oluşmadan yaptığı güncelleme işlemi başarılı olacaktı. Bu durum aşağıdaki örnekte gösterilmiştir.

[VB.NET]
Sub Main()
    Dim sSQL As String = "Select * From Employees"
    Dim cnStr As String = "data source=localhost;"
    cnStr &= "database=northwind;integrated security=sspi" 
    Dim oCnn1 As New SqlConnection
    Dim oCnn2 As New SqlConnection 
    oCnn1.ConnectionString = cnStr
    oCnn2.ConnectionString = cnStr
    oCnn1.Open() : oCnn2.Open()

    Dim oAdp1 As SqlDataAdapter
    Dim oAdp2 As SqlDataAdapter 
    oAdp1 = New SqlDataAdapter(sSQL, oCnn1)
    oAdp2 = New SqlDataAdapter(sSQL, oCnn2)
    Dim oCb1 As New SqlCommandBuilder(oAdp1)
    Dim oCb2 As New SqlCommandBuilder(oAdp2)
    Dim oDs1 As New DataSet
    Dim oDs2 As New DataSet
    oAdp1.Fill(oDs1)
    oAdp2.Fill(oDs2)

    2. kullanıcının yaptığı güncelleme
    oDs2.Tables(0).Rows(3)(1) = "User2"
    Try
       
oAdp2.Update(oDs2)
    Catch ex As DBConcurrencyException
        Console.WriteLine(ex.Message)
    End Try
End Sub

[C#]
static void Main(string[] args)
{
    string sSQL = "Select * From Employees";
    string cnStr = "data source=localhost;";
    cnStr += "database=northwind;integrated security=sspi";
    SqlConnection oCnn1 = new SqlConnection();
    SqlConnection oCnn2 = new SqlConnection();
    oCnn1.ConnectionString = cnStr;
    oCnn2.ConnectionString = cnStr;
    oCnn1.Open(); 
    oCnn2.Open();
   
    SqlDataAdapter oAdp1;
    SqlDataAdapter oAdp2; 
    oAdp1 = new SqlDataAdapter(sSQL, oCnn1);
    oAdp2 = new SqlDataAdapter(sSQL, oCnn2);
    SqlCommandBuilder oCb1 = new SqlCommandBuilder(oAdp1);
    SqlCommandBuilder oCb2 = new SqlCommandBuilder(oAdp2); 
    
    DataSet oDs1 = new DataSet();
    DataSet oDs2 = new DataSet();
    oAdp1.Fill(oDs1);
    oAdp2.Fill(oDs2); 

    //2. kullanıcının yaptığı güncelleme
    oDs2.Tables[0].Rows[3][1] = "User2";

    try
    {
        oAdp2.Update(oDs2); 
    }
    catch (DBConcurrencyException ex)
    {
        Console.WriteLine(ex.Message); 
    }
}

NOT : Şüphesiz ilk kullanıcı yaptığı güncelleme işleminden sonra, kullandığı bağlantıyı kapatmış olsaydı, ikinci kullanıcı da aynı kayıt üzerinde değişiklik yapabilecekti. Ancak ikincinin yaptığı değişiklik, ilk kullanıcının yaptığı değişikliğin üzerine yazılacaktı. Bu durum aşağıdaki örnekte gösterilmiştir;

Örnek :

[VB.NET]
Sub Main()
    Dim sSQL As String = "Select * From Employees"
    Dim cnStr As String = "data source=localhost;"
    cnStr &= "database=northwind;integrated security=sspi" 

    1.kullanıcı
    Dim oCnn1 As New SqlConnection
    oCnn1.ConnectionString = cnStr
    oCnn1.Open()
    Dim oAdp1 As SqlDataAdapter
    oAdp1 = New SqlDataAdapter(sSQL, oCnn1)
    Dim oCb1 As New SqlCommandBuilder(oAdp1)
    Dim oDs1 As New DataSet oAdp1.Fill(oDs1) 
    oDs1.Tables(0).Rows(3)(1) = "User1"
    oAdp1.Update(oDs1)
    oCnn1.Close()

    İkinci Kullanıcı
    Dim oCnn2 As New SqlConnection
    oCnn2.ConnectionString = cnStr
    oCnn2.Open()
    Dim oAdp2 As SqlDataAdapter
    oAdp2 = New SqlDataAdapter(sSQL, oCnn2)
    Dim oCb2 As New SqlCommandBuilder(oAdp2)
    Dim oDs2 As New DataSet
    oAdp2.Fill(oDs2) 
    2. kullanıcının yaptığı güncelleme
    oDs2.Tables(0).Rows(3)(1) = "User2"
    Try
        oAdp2.Update(oDs2)
    Catch ex As DBConcurrencyException
        Console.WriteLine(ex.Message)
    End Try
    oCnn2.Close()
End Sub

[C#]
static void Main(string[] args)
{
    string sSQL = "Select * From Employees"; 
    string cnStr = "data source=localhost;";
    cnStr += "database=northwind;integrated security=sspi";
    SqlConnection oCnn1 = new SqlConnection();
    oCnn1.ConnectionString = cnStr;

    //1. kullanıcı
    oCnn1.Open();
    SqlDataAdapter oAdp1;
    oAdp1 = new SqlDataAdapter(sSQL, oCnn1);
    SqlCommandBuilder oCb1 = new SqlCommandBuilder(oAdp1);
    DataSet oDs1 = new DataSet();
    oAdp1.Fill(oDs1);
    oDs1.Tables[0].Rows[3][1] = "User1"; 
    oAdp1.Update(oDs1);
    oCnn1.Close();

    //2. kullanıcı
    SqlConnection oCnn2 = new SqlConnection();
    oCnn2.ConnectionString = cnStr;
    oCnn2.Open();
    SqlDataAdapter oAdp2;
    oAdp2 = new SqlDataAdapter(sSQL, oCnn2);
    SqlCommandBuilder oCb2 = new SqlCommandBuilder(oAdp2); 
    DataSet oDs2 = new DataSet();
    oAdp2.Fill(oDs2);

    // 2. kullanıcının yaptığı güncelleme
    oDs2.Tables[0].Rows[3][1] = "User2";
    try
    { 
        oAdp2.Update(oDs2); 
    }
    catch (DBConcurrencyException ex)
    { 
        Console.WriteLine(ex.Message);
    }
    oCnn2.Close();
}


Aykut TAŞDELEN

aykutt@csdos.org