SqlDataAdapter.Fill has slow performance
This post falls into the category of "Solution for a technical issue for which I found no help on the Internet".
Overview
If you're using adhoc SqlParameters with a SqlDataAdapter and notice that calls to the SqlDataAdapter's Fill method take longer than expected, try using a typed SqlParameter instead of an adhoc SqlParameter.
Example of an adhoc SqlParameter:
adapter.Parameters.Add("@WMSReceiptID", wmsReceiptID);Example of a typed SqlParameter:
SqlParameter parameter = adapter.SelectCommand.Parameters.Add("@WMSReceiptID", SqlDbType.VarChar, 64);
parameter.Value = wmsReceiptID;
adapter.Parameters.Add(parameter);When using an adhoc SqlParameter, the ADO.NET implementation grabs schema information. In this case, it led to SqlAdapter.Fill taking 20 seconds to execute. I switched in the typed SqlParameter and execution speed returned to normal.
The gory details
Today I tested a bug fix on a search screen. The business layer builds a dynamic query based upon the search criteria entered by the user. With shock, I noticed that it was taking 20 seconds to perform the search. The request was carried out across a VPN connection to the customer's network, but it had never taken 20 seconds to execute this query.
At first I thought it was a dormant problem with the SQL that finally decided to wake up. But when executed via SQL Query Analyzer, the query completed in 1 or 2 seconds.
To isolate the problem, I threw the code into a console application. The code uses a SqlDataAdapter to fill a DataTable. The particular instance of this query had one parameter that was filled via an ad hoc SqlParameter.
Following is a stripped down version of the test code...
SqlConnection connection = new SqlConnection(Database.ConnectionString);
try
{
connection.Open();
SqlDataAdapter adapter = new SqlDataAdapter();
adapter.SelectCommand =
new SqlCommand("SELECT ...", connection);
adapter.SelectCommand.Parameters.Add("@WMSReceiptID", "[SomeRecordID]");
DataTable table = new DataTable();
try
{
adapter.Fill(table);
}
catch (Exception E)
{
Console.WriteLine("Error: " + E.Message);
}
}
finally
{
connection.Close();
}The test program also took 20 seconds or so to execute the source code line "adapter.Fill(table);". For kicks, I removed the SqlParameter and hardcoded the receipt ID. Bam! The call to adapter.Fill returned in 1 second.
Hmmm. Why would using a SqlParameter cause slow performance? I've used them hundreds, if not thousands of them, with no slowdown.
I downloaded a trial version of Automated QA's AQTime and profiled the test application. AQTime reported a large amount of time in UnsafeNativeMethods/Dbnetlib::ConnectionRead. I used the Call Graph to backtrack. Once I reached method SqlDataReader.get_MetaData. I reasoned that the adhoc SqlParameter was causing ADO.NET to fetch the schema information and doing so was taking a lot of time.
I switched to a typed SqlParameter and performance went back to its expected level.