База данных .Net, как правильно закрыть соединения с базой данных?

1

Поэтому я много читал о том, что SqlDataReaders не утилизируются должным образом в.Net - и я сражался с "Истекло время ожидания". Период ожидания истекал до получения соединения из пула. Возможно, это произошло из-за того, что все объединенные соединения были использованы и максимальный размер пула был достигнут "ошибка на пару дней. Очевидно, я мог бы увеличить максимальный размер пула до 30 000, но это не касается реальной проблемы.

Когда я просматриваю код, я выполняю следующий SQL-запрос:

select * from sys.dm_os_performance_counters
where counter_name ='User Connections'

После

cmd.Connection.Open(); 

line, пользовательские подключения увеличиваются на 1. Однако он НИКОГДА не возвращается, если я не переработаю пул приложений на веб-сервере (после чего все активные подключения к базе данных с сайта будут убиты).

Вот мой код:

public static DataTable SPExecuteDataTable(string[] ConnectionData, params object[] args)
{
    SqlConnection conn = null;
    SqlCommand cmd = null;
    SqlDataReader dr = null;
    try
    {
        conn = new SqlConnection(ConnectionData[1]);
        cmd = new SqlCommand(ConnectionData[0], new SqlConnection(ConnectionData[1]));
        cmd.CommandType = CommandType.StoredProcedure;

        for (int i = 0; i < args.Length; i++)
        {
            SqlParameter Param = new SqlParameter(ConnectionData[i + 2], DBNullIfNull(args[i]));
            cmd.Parameters.Add(Param);
        }

        cmd.Connection.Open();
        DataTable dt = new DataTable();

        using (dr = cmd.ExecuteReader())
        {
            if (dr != null)
                dt.Load(dr);
            else
                dt = null;
        }

        return dt;
    }
    catch (Exception e)
    {
        Exception x = new Exception(String.Format("DataAccess.SPExecuteDataTable() {0}", e.Message));
        throw x;
    }
    finally
    {
        conn.Close();
        cmd.Connection.Close();
        dr.Close();
        conn.Dispose();
        cmd.Dispose();
        dr.Dispose();
    }

До сих пор я пытался явно закрыть соединения (например, в моем блоке finally), но это не работает. Я также пробовал использовать такие утверждения:

using (SqlDataReader dr = blah blah blah)
{
    //code here
}

Но это тоже не работает. Что не так с моим кодом, здесь?

Теги:
database-connection

2 ответа

0
Лучший ответ

Решение:

Используйте DataTables! Чтобы не допустить, чтобы уровни доступа, не связанные с доступом к данным вашего приложения, были связаны с базой данных, просто сделайте это на своем уровне доступа к данным:

using (SqlDataReader dr = cmd.ExecuteReader())
                        {
                            if (dr != null)
                                dt.Load(dr);
                            else
                                dt = null;
                        }
                        return dt;

Затем вы можете манипулировать dt, как бы вы ни хотели, в остальной части вашего решения, и соединения УЖЕ БЫЛИ правильно утилизированы. Работает как шарм и, к счастью, код для datatable и datareader очень схожи, поэтому изменение приложения таким образом было относительно безболезненным.

3

Предпочтительной практикой является обматывание соединений, команд и считывателей при using блоков:

using(SqlConnection conn = new SqlConnection(ConnectionData[1])
{
    using(SqlCommand cmd = new SqlCommand(ConnectionData[0], conn)
    {                                                     // ^-- re-use connection - see comment below
        cmd.CommandType = CommandType.StoredProcedure;

        for (int i = 0; i < args.Length; i++)
        {
            SqlParameter Param = new SqlParameter(ConnectionData[i + 2], DBNullIfNull(args[i]));
            cmd.Parameters.Add(Param);
        }

        cmd.Connection.Open();
        DataTable dt = new DataTable();

        using (dr = cmd.ExecuteReader())
        {
            if (dr != null)
                dt.Load(dr);
            else
                dt = null;
        }

        return dt;
    }    
}

таким образом они все закрываются и утилизируются должным образом.

Хотя я думаю, что суть вашей проблемы в том, что вы каждый раз создаете два соединения:

conn = new SqlConnection(ConnectionData[1]);
cmd = new SqlCommand(ConnectionData[0], new SqlConnection(ConnectionData[1]));
                                        ^----  creating a second connection

Наконец, вы теряете много потенциально ценных сведений (трассировка стека и т.д.), Создавая новое исключение и бросая его, а не повторно бросая исходное исключение:

catch (Exception e)
{
    Exception x = new Exception(String.Format("DataAccess.SPExecuteDataTable() {0}", e.Message));
    throw x;
}

Я бы либо допустил исходное исключение, либо включил исходное исключение как InnerException:

catch (Exception e)
{
    string message = String.Format("DataAccess.SPExecuteDataTable() {0}", e.Message);
    Exception x = new Exception(message, e);
    throw x;
}
  • 0
    Спасибо, попробую. Для пояснения, избыточные соединения (конец вашего поста) не были оригинальной настройкой. Я изменил это при устранении неполадок. Я просто изменил строку кода cmd = и заменил «новый sqlcon ...» на «conn» - и проблема остается.
  • 0
    Большое спасибо - все еще тестируем это ... Кроме того, можете ли вы объяснить, как бы вы возвращали SqlDataReader из функции при использовании вложенных операторов using? Будут ли они по-прежнему самообладаться?
Показать ещё 6 комментариев

Ещё вопросы

Сообщество Overcoder
Наверх
Меню