I am developing a stand alone application, using sql server compact 3.5 sp2 which runs in process. No Database writes involved. Its purely a reporting application. Read many articles about reusing open db connections in case of sql compact(connection pooling) due to its different behavior from sql server.
Quoting the comments from a quiz opened by Erik Ejlskov Jensen Link, where its discussed an open early close late strategy for sql server compact databases. Based on this, with my limited experience I have implemented a not so complex Connection handling+Data access layer. Basically I am unsure if i am writing it in a recommended way. Please could any one point me in the right direction with rooms for improvement in this connection handling approach i have written?
The DbConnection class
public class FkDbConnection
{
private static SqlCeConnection conn;
private static DataTable table;
private static SqlCeCommand cmd;
~FkDbConnection() { conn = null; }
//This will be called when the main winform loads and connection will be open as long as the main form is open
public static string ConnectToDatabase()
{
try {
conn = new SqlCeConnection(ConfigurationManager.ConnectionStrings["Connstr"].ConnectionString);
if (conn.State == ConnectionState.Closed || conn.State == ConnectionState.Broken)
{
conn.Open();
}
return "Connected";
}
catch(SqlCeException e) { return e.Message; }
}
public static void Disconnect()
{
if (conn.State == ConnectionState.Open || conn.State == ConnectionState.Connecting || conn.State == ConnectionState.Fetching)
{
conn.Close();
conn.Dispose();
//conn = null; //does conn have to be set to null?
}
//else the connection might be already closed due to failure in opening it
else if (conn.State == ConnectionState.Closed) {
conn.Dispose();
//conn = null; //does conn have to be set to null?
}
}
/// <summary>
/// Generic Select DataAccess
/// </summary>
/// <param name="sql"> the sql query which needs to be executed by command object </param>
public static DataTable ExecuteSelectCommand(SqlCeCommand comm)
{
if (conn != null && conn.State == ConnectionState.Open)
{
#region block using datareader
using (table = new DataTable())
{
//using statement needed for reader? Its closed below
using (SqlCeDataReader reader = comm.ExecuteReader())
{
table.Load(reader);
reader.Close(); //is it needed?
}
}
#endregion
# region block using dataadpater
//I read DataReader is faster?
//using (SqlCeDataAdapter sda = new SqlCeDataAdapter(cmd))
//{
// using (table = new DataTable())
// {
// sda.Fill(table);
// }
//}
#endregion
//}
}
return table;
}
/// <summary>
/// Get Data
/// </summary>
/// <param name="selectedMPs"> string csv, generated from a list of selected posts(checkboxes) from the UI, which forms the field names used in SELECT </param>
public static DataTable GetDataPostsCars(string selectedMPs)
{
DataTable dt;
//i know this it not secure sql, but will be a separate question to pass column names to select as parameters
string sql = string.Format(
"SELECT " + selectedMPs + " "+
"FROM GdRateFixedPosts");
using (cmd = new SqlCeCommand(sql,conn))
{
cmd.CommandType = CommandType.Text;
//cmd.Parameters.Add("@fromDateTime",DbType.DateTime);
//cmd.Parameters.Add("@toDateTime",DbType.DateTime);
dt = ExecuteSelectCommand(cmd);
}
return dt;
}
}
The Main UI (Form) in which connection opened, for connection to be open through out. 2 other reporting forms are opened from here. Closing main form closes all, at which point connection is closed and disposed.
private void FrmMain_Load(object sender, EventArgs e)
{
string str = FkDbConnection.ConnectToDatabase();
statStDbConnection.Items[0].Text = str;
}
private void FrmMain_FormClosing(object sender, FormClosingEventArgs e)
{
FkDbConnection.Disconnect();
}
Comments, improvements on this connection class much appreciated. See my questions also inline code Thank you.
Updated classes as per Erik's suggestion. with a correction on ExecuteSelectCommand() and an additional class which will instantiate command objs in "using" and pass data to the UI. I intent to add separate GetDataForFormX() methods since the dynamic sql for each form may differ. Hope this is ok?
Correction to Erik's code:
public static DataTable ExecuteSelectCommand(SqlCeCommand comm)
{
var table = new DataTable();
if (conn != null && conn.State == ConnectionState.Open)
{
comm.Connection = conn;
using (SqlCeDataReader reader = comm.ExecuteReader())
{
table.Load(reader);
}
}
return table;
}
New FkDataAccess class for passing Data to UI
public class FkDataAccess
{
public static DataTable GetDataPostsCars(string selectedMPs)
{
var table = new DataTable();
string sql = string.Format(
"SELECT " + selectedMPs + " " +
"FROM GdRateFixedPosts");
if (FkDbConnection.conn != null && FkDbConnection.conn.State == ConnectionState.Open)
{
using (SqlCeCommand cmd = new SqlCeCommand(sql, FkDbConnection.conn))
{
cmd.CommandType = CommandType.Text;
//cmd.Parameters.Add("@fromDateTime",DbType.DateTime);
table = FkDbConnection.ExecuteSelectCommand(cmd);
}
}
return table;
}
//public static DataTable GetDataXY(string selectedvals)
// and so on
}