Почему SqlConnection не утилизируется / не закрывается? - PullRequest
5 голосов
/ 12 октября 2009

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

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 ]

19 голосов
/ 12 октября 2009

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

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

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

2 голосов
/ 12 октября 2009

Поскольку 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.

1 голос
/ 12 октября 2009

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

1 голос
/ 12 октября 2009

Я считаю, что это как-то связано с пулом 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;
    }
}

1 голос
/ 12 октября 2009

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

...