SqlDataReader выдает исключение NullReferenceException! что могло вызвать это и как я могу отладить?

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

"слегка надуманный" - это многопоточность этого кода? Сделайте локальную переменную набора данных и посмотрите, повторится ли это снова.

dotjoe 15.12.2008 05:45
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
5
1
3 616
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Я предполагаю, что удаление набора данных аннулирует содержащуюся в нем таблицу!

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

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

Проклятые устаревшие кодоры, которые не умели писать! сбил меня с толку ... Итак, какие-либо идеи о том, как быть уверенным, что этот многопоточный доступ с последующим удалением - это действительно то, что произошло, например, для репликации сценария?

Rory 15.12.2008 04:06

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

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

prepareDataAdaptor (SPString, CommandType.Text);

dataAdaptor.Fill (набор данных);

Ответ принят как подходящий

Could a multi-thread scenario cause this exception

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

Другие вопросы по теме