Почему SqlConnection не утилизируется / не закрывается?

Учитывая метод:

internal static DataSet SelectDataSet(String commandText, DataBaseEnum dataBase)
{
    var dataset = new DataSet();

    SqlConnection sqlc = dataBase == DataBaseEnum.ZipCodeDb
                             ? new SqlConnection(ConfigurationManager.AppSettings["ZipcodeDB"])
                             : new SqlConnection(ConfigurationManager.AppSettings["WeatherDB"]);
    SqlCommand sqlcmd = sqlc.CreateCommand();
    sqlcmd.CommandText = commandText;
    var adapter = new SqlDataAdapter(sqlcmd.CommandText, sqlc);
    adapter.Fill(dataset);


    return dataset;
}

Почему sqlc (SqlConnection) не удаляется / не закрывается после того, как вызывающий метод выходит из области видимости, или у sqlc больше нет ссылок?

РЕДАКТИРОВАТЬ 1: Даже оборачивая его в использование, я все еще могу видеть соединение используя (у меня отключен пул соединений):

SELECT DB_NAME(dbid) as 'Database Name',
COUNT(dbid) as 'Total Connections'
FROM sys.sysprocesses WITH (nolock)
WHERE dbid > 0
GROUP BY dbid

РЕДАКТИРОВАТЬ 2: Получите еще отладку с помощью, которую я получил отсюда - ответом было то, что кто-то жестко закодировал строку соединения с пулами Спасибо за помощь - если бы я мог, я бы отметил все ответы как ответы.

5 ответов

Решение

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

using (SqlConnection connection = new SqlConnection(...))
{
    // ...  
}

Это создаст try/finally блок, который обеспечит удаление объекта подключения независимо от того, что происходит в методе. Вы действительно должны обернуть любые экземпляры типов, которые реализуют IDisposable в таком блоке использования, как это, он будет обеспечивать ответственное управление ресурсами (неуправляемых ресурсов, таких как соединения с базой данных), а также даст вам детерминированный контроль, который вы ищете.

Потому что C# - это язык сборки мусора, а сборка мусора не является детерминированной. Факт в том, что ваше sqlconnection расположено. Вы просто не можете выбирать, когда.

Sql-соединения являются ограниченным ресурсом, и вполне возможно, что вы можете создать их достаточно, чтобы они закончились. Напишите это так:

internal static DataSet SelectDataSet(String commandText, DataBaseEnum dataBase)
{
    var dataset = new DataSet();

    using (SqlConnection sqlc = dataBase == DataBaseEnum.ZipCodeDb
                             ? new SqlConnection(ConfigurationManager.AppSettings["ZipcodeDB"])
                             : new SqlConnection(ConfigurationManager.AppSettings["WeatherDB"]))
    using (SqlCommand sqlcmd = sqlc.CreateCommand())
    {
        sqlcmd.CommandText = commandText;
        var adapter = new SqlDataAdapter(sqlcmd.CommandText, sqlc);
        adapter.Fill(dataset);

    }
    return dataset;
}

Хотя в этом случае вы можете сойти с рук, потому что .Fill() метод странный зверь

Если IDbConnection закрывается до вызова Fill, он открывается для извлечения данных, а затем закрывается.

Это означает, что адаптер данных должен позаботиться об этом за вас, если вы начинаете с закрытого соединения. Я гораздо больше обеспокоен тем, что вы передаете команду sql в виде простой строки. Время от времени в ваших запросах должны быть пользовательские параметры, и это означает, что вы объединяете эти данные непосредственно в командной строке. Не делай этого!! Вместо этого используйте коллекцию Paramters SqlCommand.

Я согласен со всеми ответами здесь, плюс связь может быть шире, чем просто метод. Когда вам нужно использовать существующее соединение в разных местах, сценарий немного меняется. Обязательно звоните Dispose для всех объектов, которые реализуют IDisposable после того, как вы закончите, используя их. Это хорошая практика, поэтому вы не получите неиспользуемые объекты, поскольку сборщик мусора не сможет решить, что с ними делать.

Я считаю, что это как-то связано с пулом SqlConnection. То, что вы можете сделать, и мы часто делаем на работе, это заключить весь вызов в оператор using, что заставляет его вызывать метод dispose(), закрывая соединение и удаляя объект.

Тогда вы можете сделать что-то вроде этого:


internal static DataSet SelectDataSet(String commandText, DataBaseEnum dataBase)
{
    var dataset = new DataSet();

    using(SqlConnection sqlc = dataBase == DataBaseEnum.ZipCodeDb
                             ? new SqlConnection(ConfigurationManager.AppSettings["ZipcodeDB"])
                             : new SqlConnection(ConfigurationManager.AppSettings["WeatherDB"])) {
        SqlCommand sqlcmd = sqlc.CreateCommand();
        sqlcmd.CommandText = commandText;
        var adapter = new SqlDataAdapter(sqlcmd.CommandText, sqlc);
        adapter.Fill(dataset);


        return dataset;
    }
}

Это будет после того, как сборка мусора сделает свое дело. То же самое касается открытия файлового потока для записи без его закрытия. Он может быть заблокирован, даже если коды вышли из области видимости.

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