Making async SQL calls 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&lt;T&gt;(string sp, Action&lt;T&gt; Callback, Func&lt;SqlDataReader, T&gt; HandleResult, Dictionary&lt;string, object&gt; Params = null)
    {
        TryKillThread(this.LastAction);

        this.LastAction = new Thread(() =&gt;
        {
            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&lt;string, object&gt; 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(() =&gt; Callback(returnobj)).Start();
        });
        this.LastAction.Start();
    }

    private void TryKillThread(Thread thread)
    {
        if (thread != null &amp;&amp; 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(() =&gt; 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&lt;DataTable&gt; Callback)
    {
        Dictionary&lt;string, object&gt; Params = new Dictionary&lt;string, object&gt;() 
        {
            { "@Name", name }
        };

        SqlAsync.AsyncSqlCall("spGetSomething", Callback, LoadReader, Params);
    }
}
Your code does not follow naming conventions (private fields should be camelCased, and usually have an underscore prefix; parameters should be camelCased)
there is no need in private properties, use fields instead (ConnectionString)
It is not an asynchronous version of SQL access, it is multithreaded access. The difference is that you don't necessarily need to span new threads (and you should not, in fact) to make your code asynchronous.
Stopping a thread using Thread.Abort is discouraged, and in general is very dangerous as it may lead to leaked resources and a lot of other side effects. It was explained in a lot of places, e.g. How To Stop a Thread in .NET (and Why Thread.Abort is Evil)
Killing a previous SQL action upon running the new one is the best way to get random issues all over your application
You don't provide any means to control the execution of asynchronous request, so calling code has no idea whether SQL finished execution
And finally, AsyncSqlCall&lt;T&gt; method should not have Action&lt;T&gt; Callback parameter as it is a continuation action, you should return Task&lt;T&gt; instead (so that caller can add a continuation)


I don't like custom ORMs and would suggest you to use Entity Framework or NHibernate as a data layer, but as an exercise here are the fixes I would apply (.NET 4.5):

public class SqlAsync
{
    private readonly string _connectionString;
    public int Timeout { get; set; }

    public SqlAsync(string connstring)
    {
        _connectionString = connstring;
        Timeout = 30;
    }

    public Task&lt;T&gt; AsyncSqlCall&lt;T&gt;(string sp, Func&lt;SqlDataReader, Task&lt;T&gt;&gt; handleResult, Dictionary&lt;string, object&gt; parameters = null)
    {
        return AsyncSqlCall&lt;T&gt;(sp, (reader, token) =&gt; handleResult(reader), CancellationToken.None, parameters);
    }

    public async Task&lt;T&gt; AsyncSqlCall&lt;T&gt;(string sp, Func&lt;SqlDataReader, CancellationToken, Task&lt;T&gt;&gt; handleResult, CancellationToken cancellationToken, Dictionary&lt;string, object&gt; parameters = null)
    {
        using (SqlConnection conn = new SqlConnection(_connectionString))
        using (SqlCommand cmd = new SqlCommand(sp, conn))
        {
            cmd.CommandTimeout = Timeout;
            cmd.CommandType = CommandType.StoredProcedure;

            if (parameters != null)
            {
                foreach (KeyValuePair&lt;string, object&gt; kvp in parameters)
                    cmd.Parameters.AddWithValue(kvp.Key, kvp.Value ?? DBNull.Value);
            }

            await conn.OpenAsync(cancellationToken);

            using (SqlDataReader rdr = await cmd.ExecuteReaderAsync(cancellationToken))
                return await handleResult(rdr, cancellationToken);
        }
    }
}