SqlDataReader抛出NullReferenceException!什么可能导致这种情况,我该如何调试?

时间:2008-12-15 00:50:09

标签: .net multithreading debugging exception

我在错误日志中找到了这个,并试图弄清楚它是如何可能的。并不是每天NullReferenceException都会在.net基类中深入了解!

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方法来复制问题?

顺便说一句,我发现了其他一些此问题的案例,一个在this主题中,另一个在this个人页面的Google缓存中。似乎没有任何帮助。

正在执行的我的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(sic)是一个实例变量,声明为一个用SqlDataAdapter填充的IDbDataAdapter。 dataSet是DataSet类型的实例变量。

我的理论认为,线程A贯穿并进入SqlDataAdapter.Fill()方法的中途。与此同时,线程B也在执行,并做了一些混乱线程A的事情,就像这一行:

dataAdaptor.SelectCommand.Connection.Close();

我可以看到我的这段代码不是线程安全的,但我怎么能确定这是导致上述异常的问题?

非常感谢任何建议!

编辑:修复了糟糕拼写的情况。没有在代码中更新它,就像它是如此。

更新:我同意这个代码有几个问题应该修复,但我的主要兴趣是是否有任何方法可以验证是否是导致此错误的线程问题。鉴于我的应用程序,它有点牵强,但我唯一能想到的。在我修改代码以使其更好之前,我想确定我找到了异常的原因,以便我可以确定我已修复它。

有没有办法进入.net代码?我正在使用VS 2005 / .net 2.0,但我认为在VS 2008中你可以查看.NET框架源代码吗?如果是这种情况,我可以创建一个双线程场景并逐步重新创建此问题吗?或者有没有办法不需要我安装VS 2008?

4 个答案:

答案 0 :(得分:2)

您的数据集不应是类级变量。它可能在这两个调用之间或填充操作期间由另一个线程处理或访问。

prepareDataAdaptor(SPString,CommandType.Text);

dataAdaptor.Fill(数据集);

答案 1 :(得分:1)

我会在dataAdaptor.Fill(dataSet)周围添加一些日志记录代码,以显示ThreadID和其他信息。您可以使用Console.Writeline,但我强烈推荐使用log4net。此外,使您的代码线程安全。每个线程都应该拥有自己的DataSet和DataAdapter,或者使用互斥锁。

答案 2 :(得分:1)

  

多线程方案是否会导致此异常

ADO.NET类的实例方法(例如SqlCommand)通常不是线程安全的。 因此,如果您使用来自多个线程的此类实例,则可能会出现问题,例如您描述的问题。

答案 3 :(得分:0)

我猜测处理数据集会使其包含的表无效!

BTW你不需要数据集,你可以直接填充数据表 - 或者调用另一个为你创建数据表的数据适配器函数(Get,我认为)

它拼写为“适配器”而不是“适配器”; - )