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





Я предполагаю, что удаление набора данных аннулирует содержащуюся в нем таблицу!
Кстати, вам не нужен набор данных, вы можете напрямую заполнить datatable - или вызвать другую функцию адаптера данных, которая создает для вас datatable (Get, я думаю)
и пишется «адаптер», а не «адаптер» ;-)
Проклятые устаревшие кодоры, которые не умели писать! сбил меня с толку ... Итак, какие-либо идеи о том, как быть уверенным, что этот многопоточный доступ с последующим удалением - это действительно то, что произошло, например, для репликации сценария?
Я бы добавил код регистрации прямо вокруг 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) обычно не являются потокобезопасными. Поэтому, если вы используете такие экземпляры из нескольких потоков, вы можете ожидать проблем, подобных описанной вами.
"слегка надуманный" - это многопоточность этого кода? Сделайте локальную переменную набора данных и посмотрите, повторится ли это снова.