Master's Page

• 7/7/2007 - Singleton Pattern ve Inheritance ile MDI Formlarda Tek Child Fo

Kategori: Yazilim

Windows projelerini oluştururken genellikle bir MDI (multiple document interface) form (parent) ve onun içinde açılan çocuk (child) formlar şeklinde yapmaya çalışırız. Bu makalede bu şekilde bir uygulamada inheritance ve singleton patternin nasıl uygulanacağını göreceğiz.

 

Öncelikle yapmaya çalıştığımız projedeki ana formda (MDI) solda bir ağaç (tree), üstte bir menü ve altta da durum çubuğu (statusbar) olsun. Ağaç yapımızda şöyle olsun:

 

Buradaki her alt düğüm (node) bir ekranı (formu) temsil etmektedir ve bu küçük uygulamamızda 6 child fom bulunmaktadır. Tabi ki bu tip projelerde child form sayısı çok daha fazla olmaktadır. Ayrıca bu child formlar birbirlerine benzediklerinden (örneğin her child formda üstte menu olması, altta statusbar olması, ekranı kaplamış bir groupbox olması, zemin renginin yeşil olması gibi bir takım ayarlar...) her formu tek tek projemize ekleyip bu ayarları hepsinde tek tek yapmak hem zaman kaybı olacaktır hem de ileride bakımını ve yönetimini zorlaştıracaktır. Bu sebeple projemizde interitance kullanacağız. Inheritance temel olarak bir sınıfın özelliklerini kalıtsal olarak başka bir sınıfa aktarmaktan ibarettir. (Detaylı bilgi için bakınız.) Bunun için bir tane temel form yaratıp, bu forma her ekranda olmasını istediğimiz özellikleri vereceğiz ve diğer tüm formları bu temel formdan inherit edeceğiz. Bunun için projemize yeni bir form ekliyoruz istediğimiz özellikleri veriyoruz.

 

Yeni yaratacağımız formları ise Project > Add New Item > Inherited Form şeklinde oluşturacağız. Standart form eklemeden farklı olarak eklediğimiz formun hangi formdan türediğini soracak ve listeden temel formumuzu seçeceğiz. Peki ya formlarınız daha önceden eklenmişse ve siz sonradan bu şekilde bir yapıya geçiyorsanız ne olacak? O zaman da formunuzu tanımladığınız yerin hemen altında inherit kodunu yazmanız yeterli olacaktır:

Public Class FrmKategoriTanim

     Inherits FrmBase

 

Buraya kadar yaptığımız girişten sonra asıl makale konumuza geçelim. Ana ekranda gördüğümüz gibi her alt nodeda child formumuzu çağırıyoruz:

 

Dim frm As New FrmUrunTanim           frm.MdiParent = Me

frm.Show()

 

Örneğin ürün tanım ekranı açıkken, tekrar aynı ekranı açmak istediğimizde yeni bir ürün tanım ekranı açılacaktır. Bu şekilde her tıklamada yeni bir ekran açılacaktır, ancak biz istiyoruz ki child form hiç açılmamışsa bir kez açılsın, eğer açıksa bize açık ekranı getirsin. Bunu aslında mdi formun ActiveMdiChild propertysi (çağrıldığı andaki açık olan child formları verir) içinde basit bir for..next döngüsü kurup açmak istediğimiz form zaten açık mı değil mi diye bakarak anlayabiliriz. Ancak biz burada farklı bir yöntem uygulayacağız ve singleton design pattern ile inheritance karışımı bir çözüm üreteceğiz. Singleton’da genel prensip bir instance’nın (burada, “New FrmUrunTanim” oluyor)  daha önceden oluşturulup oluşturulmaıdığını tespit etmek, instance yoksa yenisini yaratmak varsa, eskisini döndürmek şeklindedir. Temel formumuz olan FrmBase üzerinde yaptığımız işlemlerin diğer tüm formları da etkilediğini söyledik. Bu anlamda örneğin her formun load eventi altında standart bir işlem yapıyorsak bunu sadece FrmBase formunun load eventina yazmamız yeterli olacaktır. Bu sebeple bizde singleton ile ilgili kodlarımızı her forma yazmaktansa FrmBase altına yazacağız:

Private _FormLoaded As Boolean

Private _instance As Form

 

Public Function GetInstance(ByVal willLoadedForm As Type) As Form

    If _FormLoaded = False Then

      _instance = CType(Activator.CreateInstance(willLoadedForm), Form)

      AddHandler Me._instance.Closed, AddressOf Me.WhenFormClosed

      _FormLoaded = True

    End If

 

    _instance.Focus()

    Return _instance

End Function

 

Private Sub WhenFormClosed(ByVal sender As Object, ByVal e As EventArgs)

     Dim closingForm As Form = CType(sender, Form)

     RemoveHandler closingForm.Closed, AddressOf Me.WhenFormClosed

     _FormLoaded = False

End Sub

 

Ana formda ise kodlarımızı şu şekilde düzenliyoruz:

 

Private Sub mainTree_DoubleClick(ByVal sender As Object, ByVal e As EventArgs)

    Dim nd As TreeNode = Me.mainTree.SelectedNode

    If ((Not nd Is Nothing) AndAlso (nd.Nodes.Count = 0)) Then

      Dim frm As Form = Nothing

      Select Case nd.Name

         Case "kisi"

             frm = FrmKisiTanim.GetInstance(GetType(FrmKisiTanim))

         Case "kategori"

             frm = FrmKategoriTanim.GetInstance(GetType(FrmKategoriTanim))

         Case "urun"

             frm = FrmUrunTanim.GetInstance(GetType(FrmUrunTanim))

         Case "satis"

             frm = FrmUrunSatis.GetInstance(GetType(FrmUrunSatis))

         Case "stok"

             frm = FrmStokDurumu.GetInstance(GetType(FrmStokDurumu))

         Case "satis_raporu"

             frm = FrmSatisRaporu.GetInstance(GetType(FrmSatisRaporu))

      End Select

 

      If (Not frm Is Nothing) Then

       frm.MdiParent = Me

       frm.Show()

       End If

   End If

End Sub

 

FrmBase altında yazdığımız kodlarda _FormLoaded ile formumuzun yüklenip yüklenmediği bilgisini, _instance ile de yüklenen form bilgisini tutuyoruz. Formlarımızın yeni bir instancenı almak için Activator sınıfından yararlandık. CreateInstance metodu ile Type cinsinden verilen bir nesnenin instance alınabilmektedir. Burada tüm formlarımızın ilave constructorı olmadığını varsaydık. Eğer parametre ile formlarımızı açmamız gerekiyorsa CreateInstace metoduna ikinci parametre olarak bu constructor parametrelerini array olarak geçebiliyoruz. Formumuzun kapatıldığını anlamak için de _instance’ın FormClosed eventini yakalayıp _FormLoaded = false yapmamız gerekmektedir. (Bu kodu doğrudan FrmBase’in FormClosed eventine yazamıyoruz, çünkü aynı formu tekrar açmak istediğimizde dispose edilmiş bir forma ulaşmaya çalıştığımıza dair bir hata alırız.)  Formlarımızı ana formdan çağırırken FrmBase yerine ilgili formdan çağırdık, sanki kodları oraya yazmışız gibi J

Yorum (1) :: Yorum yaz! :: Bağlantı

• 24/6/2007 - Connection Pooling İle İlgili Bir Sorun ve Çözümü

Kategori: Yazilim

Bütün veritabanı işlemlerinde bir connection tanımlar ve bunun vasıtasıyla veritabanımıza bağlanıp istediğimiz işlemleri yaparız. Farklı her connection tanımladığımızda bu connectionlar connection pool’da saklanır. Eğer tekrar aynı connectiona ihtiyaç duyarsak tekrar tanımlama yaparız, ama artık bu yeni bir connection olmayıp pool’dan bize atanır. Böylelikle aynı veritabanına defalarca bağlanırken (üstelik kullanıcı sayısının arttığını da düşünürsek) daha kısa sürede bağlantı yapmamızı sağlayacaktır. Bu da daha fazla performans demek.

 

Şimdi bu connection pooling ile karşılaşabileceğimiz bir sorunun senaryosunu hazırlayalım. Öncelikle aşağıdaki örnek kodumuzu ele alalım:

 

SqlConnection con = new SqlConnection("server=.SQLEXPRESS;" +

                "database=TEST; UId=User1; Pwd=123;");

SqlCommand cmd = new SqlCommand("SELECT Count(*) FROM TARIH", con);

con.Open();

int i = Convert.ToInt32(cmd.ExecuteScalar());

con.Close();

              

               

            //Bir takım işlemler....

 

 

SqlConnection con2 = new SqlConnection("server=.SQLEXPRESS;" +

             "database=master; UId=sa; Pwd=123; Pooling=False");

SqlCommand cmd2 = new SqlCommand("ALTER DATABASE TEST" +

             " SET READ_ONLY", con2);

try

{

   con2.Open();

   cmd2.ExecuteNonQuery();

}

catch (SqlException ex)

{

   Console.WriteLine(ex.Message);

}

finally

{

   con2.Close();

}

 

Burada yapılanı incelersek TEST veritabanına User1 kullanıcısı ile bağlanıp bir sorgu çalıştırılıyor. Burada ne yapıldığı önemli değil, sonuçta veritabanına bağlanıp bir işlem yapıldığını görmek yeterli. Senaryomuz gereği bu veritabanı üzerinde bir işlem yapmamız gereksin. Bu herhangi bir işlem olabilir, basit olması açısında veritabanını READ_ONLY yapmaya çalışacağız. (Önemli olan alter database edebilmek) Buraya kadar bir sorun yokken çalışma zamanında aşağıdaki hata ile karşılaşıyoruz.

 

Timeout expired.  The timeout period elapsed prior to completion of the operation or the server is not responding. ALTER DATABASE statement failed.

 

Peki, bu da nereden çıktı şimdi? Evet, connection poolingden… Öncelikle ilk connectionı yarattığımızda bu connection poola atıldı. Daha sonra sa ile sisteme bağlanıp TEST veritabanı üzerinde alter database yapmak istediğimizde ise bize yukarıdaki hatayı verdi. SQLexpress management studioyu açıp sp_who ile bağlı kullanıcılara bakalım.

 

 

Sonuçlara baktığımızda User1 kullanıcısının bağlı olduğunu ama “uyuduğunu” görüyoruz. Bu da bizim daha sonra sa ile bu veritabanı üzerinde işlem yapmamızı engelliyor. (SQL 2000 kullanıyorsanız kod tarafında “database currently in use” mesajını alırsınız.)

 

Gelelim çözümümüze. En basiti, ilk bağlantımızı tanımlarken Pooling=False özelliğini eklemek olabilir. Ancak bu durumda connection poolingin bize sağladığı nimetlerden faydalanamayacağız ki bu istediğimiz bir çözüm değil. Bu durumda 2. bağlantımızı çalıştırmadan hemen önce

           

SqlConnection.ClearPool(con);

komutunu işletebiliriz. Böylelikle bu connectionı pool’dan temizlemiş oluruz. Buradaki senaryomuzda aynı scope içinde işlem yaptık. Scope dışında isek aynı connectionı pooldan alıp temizleyebilmek için tekrar tanımlamamız gerekiyor. Yani:

 

SqlConnection con = new SqlConnection("server=.SQLEXPRESS;" +

                "database=TEST; UId=User1; Pwd=123;");

SqlConnection.ClearPool(con);

 

Hemen burada farklı bir sorunun senaryosunu çizmek istiyorum. Peki, pool bu şekilde değil de farklı bir biçim de temizlenirse ne olur? Mesela sql serverda sorun oldu ve restart oldu, ya da sql admin gitti bizim bu User1 bağlantımızı bize haber vermeden KILL etti. (Yukarıdaki sp_who tablosuna göre KILL 53 komutu ile bağlı kullanıcı düşürülebilir.) Bu durumda poolda engel olacak kullanıcı olmadığından bizim ALTER DATABASE işlemi başarıyla çalışacaktır. Ancak, oldu ya daha sonra yine bizim bu veritabanı üzerinde işlem yapmamız gerekti: 

 

SqlConnection con4 = new SqlConnection("server=.SQLEXPRESS;" +

                "database=TEST; UId=User1; Pwd=123;");

SqlCommand cmd4 = new SqlCommand("SELECT Count(*) FROM TARIH", con4);

  try

  {

      con4.Open();

      int i2 = Convert.ToInt32(cmd4.ExecuteScalar());

  }

  catch (SqlException ex)

      {

        Console.WriteLine(ex.Message);

      }

  finally

  {

      con4.Close();

  }

 

Bu seferde farklı bir hata ile karşılaşıyoruz:

 

A transport-level error has occurred when sending the request to the server. (provider: Shared Memory Provider, error: 0 - Borunun diğer ucunda işlem yok.)

 

Anlaşılacağı üzere “borunun diğer ucu” gitmiş.Yani, ADO.NET bunun daha önce poolda olduğunu zannediyor ve connectionı bu şekilde tanımlıyor ve işlem yapmaya çalışıyor; ancak başarılı olamıyor. Peki, bu durumda ne olacak? Bu durumda yapılacak şey yine exceptiona düştüğünde poolu temizlemek:

 

catch (SqlException ex)

  {

      if (ex.Number == 233)

      {

         SqlConnection.ClearAllPools();

      }

  }

 

Bu durumda, eğer, bu sorgu çok kritik bir sorgu ise tekrar çalıştırılması gerekecektir.

 

 

 

Yorum (yok) :: Yorum yaz! :: Bağlantı

• 10/6/2007 - String İşlemlerinde Performans

Kategori: Yazilim

Projelerimizde en çok kullandığımız değişken tipi string olsa gerek. String tipinin reference type ve immutuable olması, projelerde hayli yer alması bizim string değişkenlerle yaptığımız işlemlerde performansa ne kadar dikkat etmemiz gerektiğini gösteriyor.

 

String immutuable bir değişken tipidir, dedik. Bunun anlamı string bir değişkene verdiğimiz değeri (aslında değerin adresini) asla değiştiremeyiz. Bunu basit bir örnekle açıklamaya çalışalım.

 

           

string test = "Veri";

 

//Bir takım işler

 

test = "Data";

 

 

 

 

 

 

 

Burada yaptığımız heap’de bir yer ayırıp içine “veri”yi koymaktan ve bu yerin adresini test değişkenine vermekten başka bir şey değil. Daha sonra yapılan ise heap’de bir yer daha ayırıp içine “data”yı koymak ve test değişkenine “data”nın adresini vermek. Yani heap’de aslında iki tane yerimiz var (biri “veri”yi içeriyor diğeri “data”yi) ama test değişkenimiz sadece “data”nın bulunduğu adresi tutuyor. Bu şekilde düşündüğümüzde string üzerinde yaptığımız her yeni atamada aslında içindeki veriyi değil, adresini değiştirmiş oluyoruz ve hafızada (heap’de) belki de farkında olmadan fazladan yer tutuyoruz; ta ki garbage collector (GC) zamanı gelip de “veri” nin adresini tutan herhangi bir değişken bulamayıp temizlediği zamana kadar…

 

Şimdi de bir karşılaştırma örneğine bakalım.

 

string test1 = "veri";

string test2 = "data";

 

// Bir takım işlemler

 

if (test1.ToUpper() == test2.ToUpper())

   {

      // Bir takım işlemler

   }

 

 

 

 

 

 

 

 

 

 

 

 

Proje çıktısının ilgili yerini ILDASM ile inceleyelim:

 

 

 

 

Görüyoruz ki hafızada test1.ToUpper()’ın sonucu başka bir yerde, test2.ToUpper()’ın sonucu başka bir yerde tanımlanıp farklı değişkenlere alınıyor. (Heap’de “veri”, “data”, “VERİ”, “DATA”  yerleri mevcut ve stackda “VERİ”’nin ve “DATA”nın adresini tutan iki ayrı değişken tanımlanmış) Daha sonra da boolean bir değişken tanımlanıp string fonksiyonlarından Equality kullanılarak karşılaştırıldığını görüyoruz. Peki if bloğunu şu şekilde değiştirirsek ne olur acaba, bakalım:

 

 

if (string.Compare(test1,test2,true) == 0)

   {

      // Bir takım işlemler

   }

 

 

 

 

 

 

ILDASM ile proje çıktısının ilgili satırına bakarsak:

 

 

 

 

 

Fazladan hafızada yer ayırmalar ve değişken tanımlamalar olmamış. Bu da kodumuzun öncekine göre daha performanslı olmasına ve daha az hafıza kullanılmasına sebep olmuştur.

 

String işlemlerinde sıkça kullandığımız bir başka işlem de birleştirmelerdir. Basit bir örnekle bunu da analiz etmeye çalışalım.

 

 

string test = "veri1";

 

//Bir takım işlemler

 

test += " veri2";

 

//Bir takım işlemler

 

test += " veri3";

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ILDASM çıktısına ve önceki bilgilerimize göre ilk string birleştirmede aslında heapde bir yer ayrılıp içine “veri1 veri2” yazıldığını ve bunun adresini bizim değişkenimizin tuttuğunu görürüz. Benzer şekilde ikinci birleştirme de heap’de “veri1 veri2 veri3” yazan bir yer ayrıldığını ve bunun adresini değişkenimizin tuttuğunu rahatlıkla söyleyebiliriz. (Heap görüntüsü şöyledir: “veri”, veri1 veri2, “veri1 veri2 veri3” Yani hafızada 3 ayrı yer ayrılmıştır.)  Yani string değişkenimizin içeriğini aslında değiştiremiyoruz, çünkü kendisi immutuable’dır…

 

Bu duruma alternatif çözüm StringBuilder sınıfı ile gelmektedir:

 

 

StringBuilder sb = new StringBuilder();

 

sb.Append("veri1");

           

//Bir takım işlemler

 

sb.Append("veri2");

 

//Bir takım işlemler

 

sb.Append("veri3");

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Aslında her zaman StringBuilder sınıfını kullanmak istenen performansı vermeyecektir. Bu sebeple String birleştirme işlemi belirli bir sayıda ise + veya concat metodunu kullanmak daha faydalı iken, belirsiz ve fazla sayıda string birleştirmeler de StringBuilder sınıfını kullanmak daha iyi olacaktır. Bu tavsiyeyi MSDN’de de görüyoruz:

“The String class is preferable for a concatenation operation if a fixed number of String objects are concatenated. In that case, the individual concatenation operations might even be combined into a single operation by the compiler. A StringBuilder object is preferable for a concatenation operation if an arbitrary number of strings are concatenated; for example, if a loop concatenates a random number of strings of user input.”

 

Projelerimizin minimum kaynak tüketimi ile maksimum performansta çalışmalarını isteriz. Burada da buna nasıl katkı sağlayacağımızı birkaç string işlemi üzerinde incelemeye çalıştık. Belki onlarca satırdan kazanacağımız birkaç ms ve hafızadan tasarruf edebileceğimiz birkaç byte çok fazla bir fark ettirmeyecektir. Ancak unutulmamalıdır ki projelerdeki satır sayısı arttıkça ve yaptığımız işlemler daha da karmaşıklaştıkça bu tip küçük şeylerin aslında ne denli önemli olduğu ortaya çıkacaktır. Ayrıca .NET Framework mimarisinin nasıl çalıştığını anlamamıza bir o kadar da yardımcı olacaktır.

Yorum (yok) :: Yorum yaz! :: Bağlantı

• 6/6/2007 - SqlConnectionStringBuilder İle Connection String Oluşturma - Düz

Kategori: Yazilim

Çoğu zaman veritabani bağlantılarında connection string özelliğini dinamik olarak oluşturmamız gerekmektedir. Bunun için de string birleştirmeler kullanırız. Benzer şekilde de bir connection stringden bazen server, userId gibi bilgileri parçalayarak elde ederiz. Ancak hepimizin bildiği gibi connection stringler yazılırken farklı formatlarda yazılabilirler ve bu da örneğin bağlanılan database bilgisi yerine baska bir bilgi almamıza neden olurlar. İşte tüm bunların önüne geçmek için .NET 2.0 ile gelen bir sınıf var: SqlConnectionStringBuilder

 

SqlConnectionStringBuilder ile çok kolay bir şekilde connection stringler oluşturabiliyoruz; mevcut connection stringler üzerinden de işlemler yapabiliyoruz. SqlConnectionStringBuilder’ın iki tane constructorı vardır ve birisi connection string parameteresi alır.

  

Başlıca kullanacağımız özellikleri şunlardır:

 

ConnectionString = ConnectionString bilgisini tutar.

DataSource = Bağlanılacam olan ana bilgisayar (server) bilgisini tutar.

InitialCatalog = Ana bilgisayardaki bağlanılacak veritabanı bilgisini tutar.

IntegratedSecurity = Windows güvenliğini kullanarak mı yoksa Sql doğrulama ile mi bağlanacağını belirtir. Default değeri False’dur.

UserID = SQL doğrulama ile bağlanılacak kullanıcı adını tutar.

Password = SQL doğrulama ile bağlanılacak kullanıcının şifresini tutar.

 

Sınıfın tüm metodları ve özellikleri için bakınız.

 

 

Basit olarak aşağıdaki gibi Connection Stringi oluşturabiliyoruz.

 

 

Benzer şekilde elimizdeki bir connection string üzerinde de işlem yapmamız gerekebilir. Aşağıdaki örnekte sql doğrulama ile bağlantı sağlayabilecek bir connection stringin windows doğrulama sağlayacak hale nasıl getirildiğini görüyoruz.

  

 

Yorum (yok) :: Yorum yaz! :: Bağlantı

• 6/6/2007 - C#'da My Namespace'ni kullanmak

Kategori: Yazilim

VB.NET 2.0 ile gelen My Namespace bize birçok işi kolay yoldan yapmamızı sağlıyor. Aşağıdaki şekilde My Namespace içindeki sınıfları görüyoruz. Her bir sınıfla bilgisayarımız, uygulamamız hakkında çeşitli bilgiler alıyor ve bir takım işleri çok kolay bir şekilde yapıyoruz. Detaylı bilgi için bakınız.

 

 

 

 

  

Ancak Visual Basic'e ait bu namespace C# ortamında doğrudan yer almamaktadır. Tabi bu da C#'da kullanamayacağımız anlamına gelmiyor. Bunun için öncelikle yeni bir C# projesi açıyoruz ve Microsoft.VisualBasic.dll dosyasını referans olarak ekliyoruz:

 

 

 

 

Hangi sınıfları projemizde kullanacağımız bulmak için object browserda VisualBasic.DLL assembly’sini inceliyoruz.

 

 

 

 

 

Böylelikle projemize ekleyebileceğimiz sınıfları buluyoruz: (İhtiyacınız olmayanı eklemeyebilirsiniz.)

 

 

using Microsoft.VisualBasic.ApplicationServices;

using Microsoft.VisualBasic.Devices;

 

Sonrada istediğimiz sınıftan bir instance türeterek istediğimiz işlemleri yapabiliyoruz:

 

 

Yorum (2) :: Yorum yaz! :: Bağlantı

Hakkımda

Cogito ergo sum

Bağlantılar

Ana Sayfa
Profilim
Arşiv
Arkadaşlarım
e-posta
Blog RSS
Asıl Blogum
İmir Kalkancı

Kategoriler

Arkadaşlar

Sayfa Güncel Sayfa:1 Toplam:2
| Sonraki Sayfa