I've written a class to make async SQL calls and it appears to work like a charm! But I'm a bit concerned about what it means to send a lot of queries to the server and then aborting them by throwing an abort exception on the calling thread. Is this a problem?
The code also launches a lot of threads. Is there any downside to this? Should I use ThreadPool instead, and if so, how?
I'm using .NET 4.
//Class for asynchronous SQL calls
public class SqlAsync
{
private Thread LastAction { get; set; }
private string ConnectionString { get; set; }
public int Timeout { get; set; }
public SqlAsync(string connstring)
{
this.ConnectionString = connstring;
this.Timeout = 30;
}
public void AsyncSqlCall<T>(string sp, Action<T> Callback, Func<SqlDataReader, T> HandleResult, Dictionary<string, object> Params = null)
{
TryKillThread(this.LastAction);
this.LastAction = new Thread(() =>
{
T returnobj;
using (SqlConnection conn = new SqlConnection(this.ConnectionString))
using (SqlCommand cmd = new SqlCommand(sp, conn))
{
cmd.CommandTimeout = this.Timeout;
cmd.CommandType = CommandType.StoredProcedure;
if (Params != null)
{
foreach (KeyValuePair<string, object> kvp in Params)
{
cmd.Parameters.AddWithValue(kvp.Key, kvp.Value ?? DBNull.Value);
}
}
conn.Open();
using (SqlDataReader rdr = cmd.ExecuteReader())
{
returnobj = HandleResult(rdr);
rdr.Close();
}
conn.Close();
}
new Thread(() => Callback(returnobj)).Start();
});
this.LastAction.Start();
}
private void TryKillThread(Thread thread)
{
if (thread != null && thread.IsAlive)
{
try
{
thread.Abort();
}
catch (ThreadStateException)
{ /*If thread ends between IsAlive check and Abort call, then just ignore*/ }
}
}
}
This is an example of how I use the class
public class Example : Form
{
DbExample DbEx = new DbExample("");
public void tbExample_TextChanged(object sender, EventArgs e)
{
DbEx.BeginGetSomething(tbExample.Text, BindDgvSomething);
}
private void BindDgvSomething(DataTable dt)
{
dgvSomething.Invoke(new Action(() => dgvSomething.DataSource = dt));
}
}
public class DbExample
{
private SqlAsync SqlAsync;
private string Connstr;
public DbExample(string connstr)
{
this.Connstr = connstr;
this.SqlAsync = new SqlAsync(this.Connstr);
}
private DataTable LoadReader(SqlDataReader rdr)
{
DataTable dt = new DataTable();
dt.Load(rdr);
return dt;
}
public void BeginGetSomething(string name, Action<DataTable> Callback)
{
Dictionary<string, object> Params = new Dictionary<string, object>()
{
{ "@Name", name }
};
SqlAsync.AsyncSqlCall("spGetSomething", Callback, LoadReader, Params);
}
}