I have the following code:
using (SqlConnection sqlConnection = new SqlConnection(\"blahblah;Asynchronous Processing=true;\")
{
using (SqlCommand command
In this case the using
statements won't be necessary because you should manually close it yourself rather than allowing the syntactic sugar dispose it for you (i.e. at the }
).
It should be as simple as this to ensure you don't have leaks.
using (SqlConnection sqlConnection = new SqlConnection("blahblah;Asynchronous Processing=true;") { using (SqlCommand command = new SqlCommand("someProcedureName", sqlConnection)) { sqlConnection.Open(); command.CommandType = CommandType.StoredProcedure; command.Parameters.AddWithValue("@param1", param1); command.BeginExecuteNonQuery((ar) => { var cmd = (SqlCommand)ar.AsyncState; cmd.EndExecuteNonQuery(ar); cmd.Connection.Close(); }, command); } }As you can see the lambda expression that is fired once the command is finished (no matter how long it takes) will do all the closing for you.
You can't close the connection after you submit the BeginExceuteNotQuery. It will abort the execution. Remove the using block.
In order to close the connection, you must know when the call has completed. For that you must call EndExecuteNonQuery, usually from a callback:
.
command.BeginExecuteNonQuery(delegate (IAsyncResult ar) {
try { command.EndExecuteNonQuery(ar); }
catch(Exception e) { /* log exception e */ }
finally { sqlConnection.Dispose(); }
}, null);
If you want to submit a query and don't care about the results, see Asynchronous T-SQL execution for a reliable pattern that ensures execution even if client diconnects or crashes.
I know this is an old post; just adding my 2c based on our recent (very conclusive) implementation and testing :D
To answer the OP's questions:
On to our solution:
Refs: BeginExecuteNonQuery -> BENQ, EndExecuteNonQuery -> EENQ
Use Case:
We have a windows service (C#) that makes use of the .Net TPL library. We needed to load data with a stored procedure from one database to another at run time, based on a add hoc request that the service picks up. Our stored procedure had an internal transaction and exception handling with try catch blocks.
First Try:
For our first try we implemented a solution found here MS Solution in this example you will see that MS opts to call BENQ then implements a while loop to block execution and then calls EENQ. This solution was mainly implemented if you don't need a callback method. The problem with this solution is that only BENQ is ignorant to sql connection timeouts. EENQ will timeout. So for a long running query (which is hopefully the reason why you are using BENQ) you will get stuck in the while and once the operation has completed and you call EENQ, you will get an sql timeout connection.
Second Try:
For our second try we thought ok so lets call BENQ, then add a while so that we don't close our sql connection and never call EENQ. This worked, until an exception was thrown in our stored procedure. Because we never called EENQ, the operation was never completed and the exception never bubbled up to our code. Hence we were stuck in a loop/thread/memory leak forever.
Third Try: (The Solution)
For our third try we thought to call BENQ, then directly after call EENQ. What happened was that EENQ effectively blocked execution in the thread until the operation completed. When an exception occurred in the stored procedure it was caught. When the query ran long EENQ did not throw a timeout exception and in all cases our sql connection object was disposed as well as our thread.
Here are some extracts of our code:
Here we open up a new thread for the method that calls the stored procedure.
//Call the load data stored procedure. As this stored procedure can run longer we start it in its own thread.
Task.Factory.StartNew(() => ClassName.MethodName(Parameters));
This is the code inside the method we use to call the stored procedure.
//Because this is a long running stored procedure, we start is up in a new thread.
using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings[ConfigurationManager.AppSettings["ConnectionStringName"]].ConnectionString))
{
try
{
//Create a new instance SqlCommand.
SqlCommand command = new SqlCommand(ConfigurationManager.AppSettings["StoredProcedureName"], conn);
//Set the command type as stored procedure.
command.CommandType = CommandType.StoredProcedure;
//Create input parameters.
command.Parameters.Add(CreateInputParam("@Param1", SqlDbType.BigInt, Param1));
command.Parameters.Add(CreateInputParam("@Param2", SqlDbType.BigInt, Param3));
command.Parameters.Add(CreateInputParam("@Param3", SqlDbType.BigInt, Param3));
//Open up the sql connection.
conn.Open();
//Create a new instance of type IAsyncResult and call the sp asynchronously.
IAsyncResult result = command.BeginExecuteNonQuery();
//When the process has completed, we end the execution of the sp.
command.EndExecuteNonQuery(result);
}
catch (Exception err)
{
//Write to the log.
}
}
I hope this answer save's someone some headache :D We have tested this thoroughly and have not experienced any issues.
Happy coding!
This won't work because you're closing the connection while the query is still running. The best way to do this would be to use the threadpool, like this:
ThreadPool.QueueUserWorkItem(delegate {
using (SqlConnection sqlConnection = new SqlConnection("blahblah;Asynchronous Processing=true;") {
using (SqlCommand command = new SqlCommand("someProcedureName", sqlConnection)) {
sqlConnection.Open();
command.CommandType = CommandType.StoredProcedure;
command.Parameters.AddWithValue("@param1", param1);
command.ExecuteNonQuery();
}
}
});
In general, when you call Begin_Whatever_, you usually must call End_Whatever_ or you'll leak memory. The big exception to this rule is Control.BeginInvoke.
You should always call the EndExecuteNonQuery() method to prevent leaks. It may work now but who knows what will happen in future versions of .NET. The general rule is always follow a BeginExecute... with an EndExecute...