SqlDataReader генерирует исключение NullReferenceException! что может вызвать это и как я могу отладить? - PullRequest
3 голосов
/ 15 декабря 2008

Я нашел это в журнале ошибок и пытаюсь понять, как это возможно. Не каждый день исключение 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>

Единственное, что я могу придумать, - это (тонкий) шанс, что два потока выполняли один и тот же метод одновременно, и один очистил или изменил DataSet, который передается в Fill (). Так что мой вопрос действительно:

  • как это исключение может быть выброшено
  • Может ли многопоточный сценарий вызвать это исключение
  • Как я могу быть уверен, например, есть ли способ пройти через методы System.Data, чтобы повторить проблему?

Между прочим, я обнаружил несколько других случаев этой проблемы, один в этом потоке, а другой в этом кеше 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) - это переменная экземпляра, объявленная как IDbDataAdapter, заполненная SqlDataAdapter. dataSet - это переменная экземпляра типа DataSet.

Моя теория гласит, что поток A проходит и частично входит в метод SqlDataAdapter.Fill (). Тем временем поток B также выполняет и делает что-то, что портит поток A, как эта строка:

dataAdaptor.SelectCommand.Connection.Close();

Я вижу, что мой код не является поточно-ориентированным, но как я могу быть уверен, что именно эта проблема вызвала указанное выше исключение?

большое спасибо за любые предложения!

Рори

РЕДАКТИРОВАТЬ: исправлен случай паршивой орфографии. Не обновлять его в коде, так как это так.

ОБНОВЛЕНИЕ: Я согласен, что в этом коде есть несколько ошибок, которые должны быть исправлены, но меня интересует, есть ли способ проверить, что именно эта проблема с потоками могла вызвать эту ошибку. Учитывая мое заявление, оно немного надуманно, но единственное, о чем я могу думать. Прежде чем я изменю код, чтобы в целом сделать его лучше, я хотел бы убедиться, что нашел причину исключения, чтобы быть уверенным, что исправил его.

Можно ли, например, войти в код .net? Я использую VS 2005 / .net 2.0, но я думаю, что в VS 2008 вы можете просмотреть источник .NET Framework? Если это так, могу ли я создать двухпоточный сценарий и повторить эту проблему? Или есть способ, который не требует от меня установки VS 2008?

Ответы [ 4 ]

2 голосов
/ 15 декабря 2008

Ваш набор данных не должен быть переменной уровня класса. Вероятно, он удаляется или доступен другому потоку между этими двумя вызовами или во время операции заполнения.

prepareDataAdaptor (SPString, CommandType.Text);

dataAdaptor.Fill (DataSet); * +1005 *

1 голос
/ 15 декабря 2008

Может ли многопоточный сценарий вызвать это исключение

Методы экземпляров классов ADO.NET (например, SqlCommand) обычно не являются поточно-ориентированными. Поэтому, если вы используете такие экземпляры из нескольких потоков, вы можете ожидать таких проблем, как описанная вами.

1 голос
/ 15 декабря 2008

Я бы добавил код регистрации прямо рядом с dataAdaptor.Fill (dataSet), который показывает ThreadID и другую информацию. Вы можете использовать Console.Writeline, но я настоятельно рекомендую log4net. Кроме того, сделайте свой код потокобезопасным. Каждый поток должен получить свой собственный DataSet и DataAdapter или использовать мьютекс.

0 голосов
/ 15 декабря 2008

Я предполагаю, что удаление набора данных делает таблицу недействительной!

Кстати, вам не нужен набор данных, вы можете заполнить таблицу данных напрямую или вызвать другую функцию адаптера данных, которая создает таблицу данных для вас (Get, я думаю)

и пишется "адаптер", а не "адаптер"; -)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...