I found this in an error log and am trying to work out how it's possible. It's not every day that a NullReferenceException turns up deep within the .net base classes!
1) Exception Information
*********************************************
Exception Type: System.NullReferenceException
Message: Object reference not set to an instance of an object.
Data: System.Collections.ListDictionaryInternal
TargetSite: Void Bind(System.Data.SqlClient.TdsParserStateObject)
HelpLink: NULL
Source: System.Data
StackTrace Information
*********************************************
at System.Data.SqlClient.SqlDataReader.Bind(TdsParserStateObject stateObj)
at System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString)
at System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async)
at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, DbAsyncResult esult)
at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method)
at System.Data.SqlClient.SqlCommand.ExecuteReader(CommandBehavior behavior, String method)
at System.Data.SqlClient.SqlCommand.ExecuteDbDataReader(CommandBehavior behavior)
at System.Data.Common.DbCommand.System.Data.IDbCommand.ExecuteReader(CommandBehavior behavior)
at System.Data.Common.DbDataAdapter.FillInternal(DataSet dataset, DataTable[] datatables, Int32 startRecord, Int32 maxRecords, String srcTable, IDbCommand command, CommandBehavior behavior)
at System.Data.Common.DbDataAdapter.Fill(DataSet dataSet, Int32 startRecord, Int32 maxRecords, String srcTable, IDbCommand command, CommandBehavior behavior)
at System.Data.Common.DbDataAdapter.Fill(DataSet dataSet)
at MyCode.Shared.Data.DataSocket.GetTable(String SPString)
at <rest of stack trace>
The only thing I could come up with is there's a (slim) chance there were two threads executing the same method at the same time, and one cleared or modified the DataSet that is being passed to Fill(). So my question is really:
- how could this exception get thrown
- Could a multi-thread scenario cause this exception
- How can I be sure, eg is there a way i can step through the System.Data methods to replicate the problem?
Incidentally, I've found a couple of other cases of this problem, one in this thread and another in this google cache of someone's page. Neither seems to be of any help though.
The GetTable() method of mine that is being executed looks like this:
public DataTable GetTable(string SPString)
{
//Setup the data objects by calling helper
prepareDataAdaptor(SPString,CommandType.Text);
dataAdaptor.Fill(dataSet);
dataAdaptor.SelectCommand.Connection.Close();
DataTable dt;
//ensure we dispose okay
using(dataSet)
{
if(dataSet.Tables.Count==0)
{
dataSet.Tables.Add(new DataTable("EmptyTable"));
}
dt=dataSet.Tables[0];
//because we are disposing we need to remove the table from the dataset
dataSet.Tables.Clear();
}
return dt;
}
private void prepareDataAdaptor(string SPString,CommandType Type)
{
checkForConnection();
dataSet=new DataSet();
dbCommand.CommandText=SPString;
dbCommand.CommandTimeout = MySettings.CommandTimeout;
dataAdaptor.SelectCommand=dbCommand;
dataAdaptor.SelectCommand.CommandType=Type;
dataAdaptor.SelectCommand.Connection=dbConnection;
dataAdaptor.SelectCommand.Parameters.Clear();
}
dataAdaptor (sic) is an instance variable declared as an IDbDataAdapter populated with a SqlDataAdapter. dataSet is an instance variable of type DataSet.
My theory goes that thread A runs through and gets partway into the SqlDataAdapter.Fill() method. Meanwhile thread B is also executing and does something that messes up thread A, like this line:
dataAdaptor.SelectCommand.Connection.Close();
I can see that this code of mine isn't thread-safe but how can I be sure this is the problem that caused the above exception?
many thanks for any suggestions!
Rory
EDIT: fixed case of lousy spelling. Didn't update it in the code as that's how it is.
UPDATE: I agree there's several things wrong with this code that should be fixed, but my main interest is whether there's any way to verify that it is this threading issue that would have caused this error. Given my application it's slightly far-fetched but the only thing I can think of. Before I modify the code to generally make it better I'd like to be sure that I have found the cause of the exception so that I can be sure I've fixed it.
Is there any way to step into the .net code for instance? I'm using VS 2005 / .net 2.0 but I think in VS 2008 you can view the .NET framework source? If that's the case could I create a 2-thread scenario and step through to recreate this problem? Or is there a way that doesn't require me installing VS 2008?