Почему мой объект MysqlDataReader становится нулевым?

У меня есть следующий класс:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using MySql.Data.MySqlClient;

namespace DataBaseModule.General
{
    public class ManagedDataReader : IDisposable
    {

        private bool _disposed = false;
        private MySqlCommand _command;

        private MySqlDataReader _dataReader;
        // The class constructor.
        public ManagedDataReader(string StrSQL)
            : this(new MySqlCommand(StrSQL))
        {
        }

        public ManagedDataReader(MySqlCommand SQL_Cmd)
        {
            try
            {
                _command = SQL_Cmd;
                _command.Connection = new MySqlConnection(DbAccessProvider._connectionString);
                DbAccessProvider.SqlCommandsPerformed++;
                _command.Connection.Open();
                _dataReader = _command.ExecuteReader();
            }
            catch (Exception ex)
            {
                DataBaseModule.Log.CommonLogger.Log_Database_Error(new Exception("Sql command Was: " + _command.CommandText, ex));
                throw ex;
            }
        }

        public int VisibleFieldCount()
        {
            return _dataReader.VisibleFieldCount;
        }

        public bool Read()
        {
            return _dataReader.Read();
        }

        public object this[int i]
        {
            get
            {
                if (_dataReader[i].Equals(DBNull.Value))
                {
                    return null;
                }
                else
                {
                    return _dataReader[i];
                }
            }
        }

        public object this[string FieldName]
        {
            get
            {
                if (_dataReader[FieldName].Equals(DBNull.Value))
                {
                    return null;
                }
                else
                {
                    return _dataReader[FieldName];
                }
            }
        }

        // Implement IDisposable.
        // Do not make this method virtual.
        // A derived class should not be able to override this method.
        public void Dispose()
        {
            Dispose(true);
            // This object will be cleaned up by the Dispose method.
            // Therefore, you should call GC.SupressFinalize to
            // take this object off the finalization queue 
            // and prevent finalization code for this object
            // from executing a second time.
            GC.SuppressFinalize(this);
        }

        // Dispose(bool disposing) executes in two distinct scenarios.
        // If disposing equals true, the method has been called directly
        // or indirectly by a user's code. Managed and unmanaged resources
        // can be disposed.
        // If disposing equals false, the method has been called by the 
        // runtime from inside the finalizer and you should not reference 
        // other objects. Only unmanaged resources can be disposed.
        protected void Dispose(bool disposing)
        {
            // Check to see if Dispose has already been called.
            if (!this._disposed)
            {
                // If disposing equals true, dispose all managed 
                // and unmanaged resources.
                if (disposing)
                {
                    if (_dataReader != null)
                    {
                        _dataReader.Close();
                    }
                    if (_command != null)
                    {
                        if (_command.Connection != null)
                        {
                            _command.Connection.Dispose();
                            _command.Connection = null;
                            //Free the reference.
                        }
                        _command.Dispose();
                    }
                }
                // Call the appropriate methods to clean up 
                // unmanaged resources here.
                // If disposing is false, 
                // only the following code is executed.
                // Note disposing has been done.
                _disposed = true;
            }
        }

        // This finalizer will run only if the Dispose method 
        // does not get called.
        // It gives your base class the opportunity to finalize.
        // Do not provide finalize methods in types derived from this class.
        ~ManagedDataReader()
        {
            // Do not re-create Dispose clean-up code here.
            // Calling Dispose(false) is optimal in terms of
            // readability and maintainability.
            Dispose(false);
        }
    }
}

Моя проблема в том, что по какой-то причине иногда я получаю исключение при вызове Read(): исключение заключается в том, что мой член _dataReader имеет значение null.

Это странно, потому что у меня есть блок try-catch при его инициализации, и никакое исключение не перехватывается (я использую свой механизм ведения журнала, чтобы проверить это).

Такое поведение редко, но происходит прибл. раз в неделю (я запускаю миллионы запросов в день)

Большое спасибо всем, кто пытается разгадать эту загадку!!


person Eitan H.S.    schedule 31.01.2012    source источник
comment
Не используйте throw ex;, из-за исключения теряется полезная информация. Вместо этого используйте только throw;.   -  person svick    schedule 31.01.2012
comment
Вы уверены, что _dataReader это null, а не какой-то другой объект?   -  person svick    schedule 31.01.2012


Ответы (2)


Я столкнулся с той же проблемой. И оказалось, что ExecuteReader() действительно может возвращать null в некоторых случаях.

Я посмотрел код разъема и вот что нашел:

catch (MySqlException ex)
{

...

// if we caught an exception because of a cancel, then just return null
if (ex.IsQueryAborted)
    return null;

Если копнуть глубже, выясняется, что IsQueryAborted имеет значение true, когда сервер возвращает MySqlErrorCode.QueryInterrupted или MySqlErrorCode.FileSortAborted. И пока выясняется, что это какая-то серверная проблема, так как, по крайней мере, в моем случае проблема непостоянна и выглядит как проблема многопоточности в коде сервера.

person flipm0de    schedule 11.01.2013

Я просмотрел код и обнаружил, что единственный способ получить NULL reader — это создать исключение в коде инициализации.

Поскольку SqlDataReader.ExecuteReader во всех случаях возвращает объект и не может возвращать значение null. Татьяна Рачева подтвердила это путем отражения класса SqlDataReader в этом ответе.

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

person Mohamed Ramadan    schedule 31.01.2012
comment
Сборщик мусора никогда не избавляется от объектов. Он завершает объекты, если они недоступны, но это не установит для поля значение null. И если объект недоступен, это по определению означает, что вы не можете его достичь. - person svick; 31.01.2012
comment
Это MySqlDataReader, а не SqlDataReader... Я могу получить доступ к ManagedDataReader, я не могу получить доступ к _dataReader, потому что он каким-то образом имеет значение null. Спасибо, в любом случае... - person Eitan H.S.; 31.01.2012