我在错误日志中发现了这个问题,正在努力弄清楚它是如何可能的。不是每天在 .net 基础类中都会出现 NullReferenceException 异常!
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>
我能想到的唯一可能是有两个线程同时执行同一个方法,其中一个清除或修改了传递给Fill()的DataSet。所以我的问题实际上是:
- 这个异常是如何引发的
- 多线程场景会导致这个异常吗
- 我怎么确定,例如是否有办法逐步执行System.Data方法来复制问题?
我的GetTable()方法看起来像这样:
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是一个实例变量,声明为IDbDataAdapter类型,并用SqlDataAdapter进行了填充。
dataSet是一个DataSet类型的实例变量。
我的理论是,线程A运行并进入SqlDataAdapter.Fill()方法的一部分。同时,线程B也在执行并做了一些会干扰线程A的事情,比如这一行:
dataAdaptor.SelectCommand.Connection.Close();
我看到我的代码不是线程安全的,但我怎么确定这是导致上面异常的问题呢?
非常感谢任何建议!
Rory
编辑:修正了拼写错误。没有在代码中更新,因为它就是这样。
更新:我同意这个代码有几个问题需要解决,但我的主要兴趣是是否有任何方法可以验证这是导致此错误的线程问题。考虑到我的应用程序,这有点牵强,但这是我能想到的唯一的事情。在修改代码以普遍改善之前,我想确定我已经找到了异常的原因,以便我可以确定我已经解决了它。
有没有办法进入.NET代码?例如,我正在使用VS 2005 / .net 2.0,但我认为在VS 2008中,您可以查看.NET框架源代码?如果是这种情况,我可以创建一个2线程场景并逐步重现此问题吗?或者是否有一种不需要我安装VS 2008的方式?