Makale Özeti

Bu makalemizde smart device projelerinde yoğun olarak ihtiyaç duyduğumuz database erişimi için örnek bir data access class hazırlayacağız.

Makale

Merhaba, Microsoft her ne kadar Windows Phone 7 ile VS 2010'da smart device project seçeneğini kaldırmış olsa dahi, endüstriyel çalışma ortamları için geliştirilen uygulamaların çoğalması ile daha uzunca bir süre Visual Studio 2008 'de SmartDevice projeleri ile uygulama geliştirileceğini düşünüyorum. Bu vesile ile bu makalemizde yoğun olarak ihtiyaç duyduğumuz bir smart device projesinde kullanılan SqlServerCe veritabanımıza erişim için örnek bir data access class (veri erişim sınıfı) hazırlayacağız.

Pda uygulamamızın her hangi bir sınıfı içinden veritabanımıza erişmek, veri kaydetmek, silmek gibi işlemlere ihtiyaç duyulmaktadır. Tüm bu işlemlerimiz için her defasında SqlCeCommand, SqlCeDataAdapter, SqlCeParameter gibi nesnelerin tekrar tekrar oluşturulması hem daha fazla satır kod yazmamıza hem de bellek güçleri zaten belirli bir seviyede olan pda'ler için bir dezavantaj sağlayacaktır.

Bir Pda içinde çalışan uygulamamızdan veritabanına erişim gerçekleştirirken çağrılarımızın senkron yapıldığını ve çoğunlukla aynı anda db'ye bir kullanıcının erişim isteğinde bulunduğunu göz önünde bulundurursak static method'lardan da faydalanarak örnek sınıfımızı geliştirmeye başlayabiliriz.

 Sınıfımızı static olarak SqlCompactDatabase isminde tanımlayacağız.

  public static class SqlCompactDatabase

Static sınıfımıza ait constractor'ümüzü de static tanımlayacağız. Böylece yalnızca bir kez, uygulama ayağa kalktığında (çalışmaya başladığında) constractor içindeki kodlar çalışacaktır. Uygulamamızdan veri tabanına erişirken sürekli olarak SqlCeParameter nesnesi oluşturmamak için öncelikle kendimize ait bir parametre nesnesi oluşturalım ve bu parameter nesnesi ile SqlCeParameter karşılıklarını burada tanımlayabiliriz.

Ayrıca, SqlCeParameter ile tek tek bir parametremizin tipini, string ise uzunluğunu girmek fazla sayıda parametre gerektiren işlemlerimizde oldukça zaman kaybedici olacaktır bunun yerine kullanacağımız MyCeParameter isimli sınıfımızı aşağıdaki gibi tanımlayalım.

C#

public class MyCeParameter {

 

    public MyCeParameter(string name, object value, ParameterDirection direction) {

      Value = value;

      Name = name;

      Direction = direction;

     if (value == null) {

        Value = DBNull.Value;

        return;

      }

     Type = value.GetType();

      if (Type == typeof(string)) {

        Size = ((string)value).Length;

      }

      else if (Type == typeof(byte[])) {

        Size = ((byte[])value).Length;

      }

      else {

        Size = 0;

      }

    }

    public Type Type { get; set; }

    public int Size { get; set; }

    public object Value { get; set; }

    public string Name { get; set; }

    public ParameterDirection Direction { get; set; }

 }

Şimdi de SqlCompactDatabase sınıfımıza ait static constractor' ümüzü yazalım. MyCeParameter oluşturuken ctor'da parametre olarak vereceğimiz tip için Type kullanıyoruz. C# Type'lara karşılık gelen SqlDbType'larımızı bir dictionary'ye ekleyebiliriz.

C#

    private static readonly Dictionary<Type, SqlDbType> DictTypeToSql;

   

    static SqlCompactDatabase() {

      DictTypeToSql = new Dictionary<Type, SqlDbType>();

 

      DictTypeToSql.Add(typeof(Byte[]), SqlDbType.Image);

      DictTypeToSql.Add(typeof(String), SqlDbType.NVarChar);

      DictTypeToSql.Add(typeof(Char), SqlDbType.Char);

      DictTypeToSql.Add(typeof(DateTime), SqlDbType.DateTime);

      DictTypeToSql.Add(typeof(Double), SqlDbType.Float);

      DictTypeToSql.Add(typeof(Decimal), SqlDbType.Decimal);

      DictTypeToSql.Add(typeof(Int16), SqlDbType.SmallInt);

      DictTypeToSql.Add(typeof(Int32), SqlDbType.Int);

      DictTypeToSql.Add(typeof(Int64), SqlDbType.BigInt);

  } 

SqlCompactDatabase sınıfımıza ait olacak SqlCeConnection nesnemizi de static constractor de oluşturabiliriz. Connection nesnemizi oluşturmak için uygulamamızın bağlanacağı veritabanı dosya yolunu da constractör içinde elde ediyoruz. Constractor'ümüzün ve private değişkenkerimizin son hali aşağıdaki gibi olsun.

C#

  

   private static SqlCeConnection conn;

    private static readonly Dictionary<Type, SqlDbType> DictTypeToSql;

 

   //Static Contractor  

    static SqlCompactDatabase() {

      string currentDirectory = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase);

 

 

      dbPath = string.Concat(currentDirectory, "\\urundb.sdf");

 

      if (conn == null) {

        conn = new SqlCeConnection("Data Source =" + dbPath + ";password='1234567'");

      }

 

      DictTypeToSql = new Dictionary<Type, SqlDbType>();

      DictTypeToSql.Add(typeof(Byte[]), SqlDbType.Image);

      DictTypeToSql.Add(typeof(String), SqlDbType.NVarChar);

      DictTypeToSql.Add(typeof(Char), SqlDbType.Char);

      DictTypeToSql.Add(typeof(DateTime), SqlDbType.DateTime);

      DictTypeToSql.Add(typeof(Double), SqlDbType.Float);

      DictTypeToSql.Add(typeof(Decimal), SqlDbType.Decimal);

      DictTypeToSql.Add(typeof(Int16), SqlDbType.SmallInt);

      DictTypeToSql.Add(typeof(Int32), SqlDbType.Int);

      DictTypeToSql.Add(typeof(Int64), SqlDbType.BigInt);

   }

 

Şimde de SqlCompactDatabase sınıfımızın public ve private olarak kullanılacak methodlarını tanımlayalım. Tabii public ile uygulamamızın herhangi bir yerinden yapacağımız Insert, Updata, Delete ve Select işlemlerini yapabileceğimiz methodlar olacak. Private methodlarımız içinde ise SqlCompactDatabase sınıfımız içinde gerekli olacak methodlarımızı yazacağız. private ve public olarak tanımlanacak method isimlerimizi de aşağıdaki gibi yazalım.

public static void OpenDb() Uygulamamız ilk ayağa kalkarken db connection açmak için kullanacığız. Aynı anda bir kullanıcı db üzerinde erişim yapacağı için bir defa açmamız yeterli olacak.
public static void CloseDb() Uygulamamızı kapanırken db connection'ımızı kapatmak için kullanacağız.
public static void CreateDb() Local SqlCe db oluşturmak için kullanacağız.
public static void ShowErrors(SqlCeException e) Oluşabilecek SqlCeException'ları detaylı görebilmek için kullanacağız.
public static int ExecuteNonQuery(string sql, params MyCeParameter[] parameters) Insert, Update ve Delete Sql Query'lerimizi çalıştırmak için kullanacağız. Sql string query'mizi ve var ise sırasıyla parametrelerimizi ekleyeceğiz.
public static object ExecuteScalerText(string sql, BirimCeParameter[] parameters) Geriye yalnızca bir object dönen sorgular için kullanacağız.
public static DataTable GetDataTable(string sql, params BirimCeParameter[] parameters) Geriye DataTable nesnesi dönmesi gereken sorgularda kullanacağız.
private static void bindParametersToSqlCe(MyCeParameter[] parameters, SqlCeCommand cmd) MyCeParameter nesnesi olarak verdiğimiz parametreyi SqlCeParameter nesnesi olarak SqlCeCommand nesnesine bind etmek için kullanacağız.
private static SqlDbType getSqlCeDbType(Type type) C# Type olarak verdiğimiz tipin SqlDbType karşılığını elde etmek için kullanacağız.

Aşağıdaki kod satırlarında görüleceği üzere, dispose edilebilir nesneler için kullanılan using bloğu içinde SqlCeCommand oluşturacağız. Ve eğer var ise parametrelerimizi de SqlCeCommand nesnemize ekledikten sonra sorgumuzu çalıştırıyoruz ve dönüş değeri olarak kaç satırın etkilendiğini görebileceğiz.

public static int ExecuteNonQuery(string sql, params MyCeParameter[] parameters) {

      var rowsAffected = 0;

      try {

        using (var cmd = new SqlCeCommand()) {

          cmd.Connection = conn;

          cmd.CommandText = sql;

 

          if (parameters != null) {

            bindParametersToSqlCe(parameters, cmd);

          }

          rowsAffected = cmd.ExecuteNonQuery();

        }

      }

      catch (SqlCeException ex) {

        ShowErrors(ex);

      }

      return rowsAffected;

    }

Yukarıdaki method içinde parametrelerimizi SqlCeCommand nesnesine bind etmek için bindParametersToSqlCe private methodunu kullandık.

private static void bindParametersToSqlCe(MyCeParameter[] parameters, SqlCeCommand cmd) {

      try {

        for (var i = 0; i < parameters.Length; i++) {

          MyCeParameter myParam = parameters[i];

          SqlCeParameter p = new SqlCeParameter(myParam.Name, getSqlCeDbType(myParam.Type), myParam.Size);

          p.Value = myParam.Value;

          p.Direction = myParam.Direction;

          cmd.Parameters.Add(p);

        }

      }

      catch (Exception) {

        throw;

      }

 }

 

private static SqlDbType getSqlCeDbType(Type type) {

      return DictTypeToSql[type];

 }

Benzer şekilde veri erişim sınıfımızın diğer methodlarını da aşağıdaki gibi oluşturabiliriz. Bu sınıfımızın son halini aşağıdaki gibi yazalım.

C#

  public static class SqlCompactDatabase {

    private static string dbPath;

    private static SqlCeConnection conn;

    private static readonly Dictionary<Type, SqlDbType> DictTypeToSql;

    static SqlCompactDatabase() {

      string currentDirectory = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase);

      dbPath = string.Concat(currentDirectory, "\\urundb");

      if (conn == null) {

        conn = new SqlCeConnection("Data Source =" + dbPath + ";password='1234567'");

      }

      DictTypeToSql = new Dictionary<Type, SqlDbType>();

      DictTypeToSql.Add(typeof(Byte[]), SqlDbType.Image);

      DictTypeToSql.Add(typeof(String), SqlDbType.NVarChar);

      DictTypeToSql.Add(typeof(Char), SqlDbType.Char);

      DictTypeToSql.Add(typeof(DateTime), SqlDbType.DateTime);

      DictTypeToSql.Add(typeof(Double), SqlDbType.Float);

      DictTypeToSql.Add(typeof(Decimal), SqlDbType.Decimal);

      DictTypeToSql.Add(typeof(Int16), SqlDbType.SmallInt);

      DictTypeToSql.Add(typeof(Int32), SqlDbType.Int);

      DictTypeToSql.Add(typeof(Int64), SqlDbType.BigInt);

    }

 

    public static void OpenDb() {

      try {

        if (conn == null) {

          conn = new SqlCeConnection("Data Source =" + dbPath + ";password='1234567'");

        }

        if (conn.State == ConnectionState.Closed) {

        tryConnection:

          try {

            conn.Open();

          }

          catch (SqlCeException ex) {

            if (ex.NativeError == 25138) {

              var sce = new SqlCeEngine(conn.ConnectionString);

              sce.Upgrade();

              goto tryConnection;

            }

          }

        }

      }

      catch (SqlCeException ex) {

        ShowErrors(ex);

      }

      catch (Exception) {

        throw;

      }

    }

 

    public static void CloseDb() {

      try {

        if (conn != null && conn.State == ConnectionState.Open) {

          conn.Close();

          conn.Dispose();

          conn = null;

        }

      }

      catch (SqlCeException ex) {

        ShowErrors(ex);

      }

      catch (Exception) {

        throw;

      }

    }

 

    public static void ShowErrors(SqlCeException e) {

      SqlCeErrorCollection errorCollection = e.Errors;

      StringBuilder bld = new StringBuilder();

      Exception inner = e.InnerException;

      if (null != inner) {

        MessageBox.Show("Inner Exception: " + inner.ToString());

      }

      foreach (SqlCeError err in errorCollection) {

        bld.Append("\n Error Code: " + err.HResult.ToString("X"));

        bld.Append("\n Message   : " + err.Message);

        bld.Append("\n Minor Err.: " + err.NativeError);

        bld.Append("\n Source    : " + err.Source);

        foreach (int numPar in err.NumericErrorParameters) {

          if (0 != numPar)

            bld.Append("\n Num. Par. : " + numPar);

        }

        foreach (string errPar in err.ErrorParameters) {

          if (String.Empty != errPar)

            bld.Append("\n Err. Par. : " + errPar);

        }

        MessageBox.Show(bld.ToString());

        bld.Remove(0, bld.Length);

      }

    }

 

    public static int ExecuteNonQuery(string sql, params MyCeParameter[] parameters) {

      var rowsAffected = 0;

      try {

        using (var cmd = new SqlCeCommand()) {

          cmd.Connection = conn;

          cmd.CommandText = sql;

 

          if (parameters != null && parameters.Length != 0) {

            bindParametersToSqlCe(parameters, cmd);

          }

          rowsAffected = cmd.ExecuteNonQuery();

        }

      }

      catch (SqlCeException ex) {

        ShowErrors(ex);

      }

      return rowsAffected;

    }

 

    private static void bindParametersToSqlCe(MyCeParameter[] parameters, SqlCeCommand cmd) {

      try {

        for (var i = 0; i < parameters.Length; i++) {

          MyCeParameter myParam = parameters[i];

          SqlCeParameter p = new SqlCeParameter(myParam.Name, getSqlCeDbType(myParam.Type), myParam.Size);

          p.Value = myParam.Value;

          p.Direction = myParam.Direction;

          cmd.Parameters.Add(p);

        }

      }

      catch (Exception) {

        throw;

      }

    }

 

    private static SqlDbType getSqlCeDbType(Type type) {

      return DictTypeToSql[type];

    }

 

    public static object ExecuteScalerText(string sql, MyCeParameter[] parameters) {

      object result = null;

      try {

        using (SqlCeCommand cmd = new SqlCeCommand()) {

          cmd.Connection = conn;

          cmd.CommandText = sql;

          if (parameters != null) {

            bindParametersToSqlCe(parameters, cmd);

          }

          result = cmd.ExecuteScalar();

        }

      }

      catch (SqlCeException ex) {

        ShowErrors(ex);

      }

     

     catch (Exception) {

        throw;

      }

      return result;

    }

 

    public static DataTable GetDataTable(string sql, params MyCeParameter[] parameters) {

      DataTable dt = new DataTable();

      try {

        using (SqlCeCommand cmd = new SqlCeCommand(sql)) {

          cmd.Connection = conn;

          if (parameters != null) {

            bindParametersToSqlCe(parameters, cmd);

          }

          using (SqlCeDataAdapter da = new SqlCeDataAdapter(cmd)) {

            dt = new DataTable();

            da.Fill(dt);

          }

        }

        if (dt.Rows.Count == 0) {

          return null;

        }

        return dt;

      }

      catch (SqlCeException ex) {

        ShowErrors(ex);

        return null;

      }

      finally {

        if (dt != null) {

          dt.Dispose();

        }

      }

    }

 

   public static void CreateDb() {

 

      Cursor.Current = Cursors.WaitCursor;

      SqlCeConnection conn_Db = null;

      SqlCeCommand cmd = null;

      try {

        var engine = new SqlCeEngine("Data Source =" + dbPath + ";password='1234567'");

        engine.CreateDatabase();

        engine.Dispose();

 

        conn_Db = new SqlCeConnection("Data Source =" + dbPath + ";password='1234567'");

        conn_Db.Open();

        cmd = conn_Db.CreateCommand();

 

        cmd.CommandText = "CREATE TABLE URUN\n" +

                          "(\n" +

                          "KODU int not null primary key,\n" +

                          "ADI nvarchar(25)\n" +

                          ")";

        cmd.ExecuteNonQuery();

 

        cmd.Dispose();

        MessageBox.Show("Veritabanı Oluşturuldu.", "Bilgi", MessageBoxButtons.OK, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1);

      }

      catch (SqlCeException ex) {

        if (cmd != null) {

          cmd.Dispose();

        }

        ShowErrors(ex);

      }

      catch (Exception) {

        throw;

      }

      finally {

        Cursor.Current = Cursors.Default;

      }

    }

}

Sınıfımızı tamamladıktan sonra veri tabanımız için oluşturduğumuz (CreateDb methodu ile) Urun isimli tablomuza bir ürün satırı ekleme örneği yaparak yazımızı tamamlayalım.

C#

 private void urunKaydet() {

      try {

        const string sql = "insert into URUN(KODU,ADI) values(@p1,@p2) ";

        MyCeParameter kodu = new MyCeParameter("@p1", 1, ParameterDirection.Input);

        MyCeParameter adi = new MyCeParameter("@p2", "Vida", ParameterDirection.Input);

        SqlCompactDatabase.ExecuteNonQuery(sql, kodu,adi);

      }

      catch (Exception) {

        throw;

      }

 }

Yukarıdaki örnekte görüldüğü gibi parametrelerimi oluştururken MyCeParameter nesnemi kullanarak ayrıca parametre hakkındaki detayları belirtmeme gerek kalmıyor, SqlCeCommand, SqlCeDataAdapter nesnelerim ise yalnızca SqlCompactDatabase static sınıfım içinde oluşturulup dispose ediliyor veya aşağıdaki örnekte olduğu gibi datagrid'imin datasourse'una rahatça Urun'lerimi bind edebiliyorum.

C#

 private void urunleriListele() {

      try {

        dataGridUrunler.DataSource = SqlCompactDatabase.GetDataTable("select * from URUN", null);

      }

      catch (Exception) {

        throw;

      }

 }

Dilerim faydalı bir yazı olmuştur. Bir sonraki yazımızda oluşturduğumuz SqlCompactDatabase sınıfımıza SqlCeTransaction nesnesi de ekleyerek sınıfımızı daha detaylı hale getirmeye çalışacağız.

Kolay gelsin.

Gökhan Manduz - gokhanmanduz@hotmail.com , gmanduz@gmail.com