.net SqlConnection не закрывается, даже если в пределах использования {}

Пожалуйста, помогите!

Фоновая информация

У меня есть приложение WPF, которое обращается к базе данных SQL Server 2005. База данных выполняется локально на компьютере, на котором запущено приложение.

Всюду, где я использую Linq DataContext, я использую оператор {} и передаю результат функции, которая возвращает объект SqlConnection, который был открыт, и выполнил его с помощью SqlCommand перед возвратом к конструктору DataContext. Т.е.

// In the application code
using (DataContext db = new DataContext(GetConnection()))
{
    ... Code 
}

где getConnection выглядит так (я удалил "пушину" из функции, чтобы сделать ее более читаемой, но нет дополнительных функций, которые отсутствуют).

// Function which gets an opened connection which is given back to the DataContext constructor
public static System.Data.SqlClient.SqlConnection GetConnection()
{
   System.Data.SqlClient.SqlConnection Conn = new System.Data.SqlClient.SqlConnection(/* The connection string */);

    if ( Conn != null )
    {
        try
        {
            Conn.Open();
        }
        catch (System.Data.SqlClient.SqlException SDSCSEx)
        {
             /* Error Handling */
        }

        using (System.Data.SqlClient.SqlCommand SetCmd = new System.Data.SqlClient.SqlCommand())
        {
            SetCmd.Connection = Conn;
            SetCmd.CommandType = System.Data.CommandType.Text;

            string CurrentUserID = System.String.Empty;
            SetCmd.CommandText = "DECLARE @B VARBINARY(36); SET @B = CAST('" + CurrentUserID + "' AS VARBINARY(36)); SET CONTEXT_INFO @B";

            try
            {
                SetCmd.ExecuteNonQuery();
            }
            catch (System.Exception)
            {
                /* Error Handling */
            }
        }

        return Conn;
    }

Я не думаю, что приложение, являющееся WPF, имеет какое-либо отношение к проблеме, которую я имею.

Проблема, с которой я сталкиваюсь

Несмотря на то, что SqlConnection размещается вместе с DataContext в студии Sql Server Management, я все еще вижу множество открытых подключений с помощью:

status : 'Sleeping' 
command : 'AWAITING COMMAND' 
last SQL Transact Command Batch : DECLARE @B VARBINARY(36); SET @B = CAST('GUID' AS VARBINARY(36)); SET CONTEXT_INFO @B

В конечном итоге пул соединений будет исчерпан, и приложение не может продолжить.

Поэтому я могу только заключить, что каким-то образом запуск SQLCommand для установки Context_Info означает, что соединение не удаляется, когда DataContext удаляется.

Может ли кто-нибудь заметить что-либо очевидное, что будет прекращать закрытие соединений и удалять их, когда используются DataContext, которые они используют?

Ответы

Ответ 1

От MSDN (DataContext Constructor (IDbConnection)):

Если вы предоставляете открытое соединение, DataContext не закрывает его. Поэтому не создавайте экземпляр DataContext с открытым соединением если у вас нет веских оснований это.

В принципе, похоже, что ваши соединения ждут, когда GC завершит их до того, как они будут выпущены. Если у вас много кода, который делает это, один подход может заключаться в том, чтобы переопределить Dispose() в частичном классе данных-контекста и закрыть соединение - просто убедитесь, что документ-контекст предполагает принадлежность к соединению!

    protected override void Dispose(bool disposing)
    {
        if(disposing && this.Connection != null && this.Connection.State == ConnectionState.Open)
        {
            this.Connection.Close();
            this.Connection.Dispose();
        }
        base.Dispose(disposing);
    }

Лично я бы с радостью дал ему (обычный контекст данных, без взлома выше) открытое соединение, пока я "использовал" соединение (позволяя выполнять несколько операций) - i.e.

using(var conn = GetConnection())
{
   // snip: some stuff involving conn

   using(var ctx = new FooContext(conn))
   {
       // snip: some stuff involving ctx
   }

   // snip: some more stuff involving conn
}

Ответ 2

SqlProvider SqlProvider закрывает соединение SQL (через SqlConnectionManager.DisposeConnection) только в том случае, если он был открыт. Если вы предоставите уже открытый объект SqlConnection конструктору DataContext, он не закроет его для вас. Таким образом, вы должны написать:

using (SqlConnection conn = GetConnection())
using (DataContext db = new DataContext(conn))
{
    ... Code 
}

Ответ 3

Я столкнулся с той же проблемой, используя Entity Framework. Мой ObjectContext был обернут вокруг блока using.

Соединение было установлено при вызове SaveChanges(), но после того, как оператор using вышел из области видимости, я заметил, что SQL Management Studio по-прежнему имеет "AWAITING COMMAND" для .NET SQL Client. Похоже, что это связано с поведением поставщика ADO.NET, который по умолчанию включил пул соединений.

В разделе "Использование пула соединений с SQL Server" на MSDN (выделено мной):

Пул соединений уменьшает количество открываемых новых подключений. Пулёт поддерживает право собственности на физическое соединение. Он управляет соединениями, поддерживая живой набор активных соединений для каждой конкретной конфигурации соединения. Всякий раз, когда пользователь вызывает Open в соединении, пул смотрит, есть ли доступное соединение в пуле. Если объединенное соединение доступно, оно возвращает его вызывающему абоненту вместо открытия нового соединения. Когда приложение вызывает Close в соединении, пул возвращает его в объединенный набор активных соединений, а не фактически закрывает его. После того, как соединение будет возвращено в пул, он будет готов к повторному использованию при следующем вызове Open.

Также ClearAllPools и ClearPool представляется полезным явно закрыть все объединенные соединения, если это необходимо.

Ответ 4

Я думаю, что соединение, в то время как больше не ссылается, ждет, пока GC полностью избавится от него.

Решение:

Создайте свой собственный класс DataContext, который происходит из автоматически сгенерированного. (переименуйте базовый, чтобы вам не пришлось менять какой-либо другой код).

В вашем производном DataContext - добавьте функцию Dispose(). В этом - распоряжаться внутренним соединением.

Ответ 5

Хорошо спасибо за помощь, он был решен сейчас.

По сути, я взял элементы большинства ответов выше и реализовал конструктор DataContext, как указано выше (я уже перегрузил конструкторы, поэтому это не было большим изменением).

// Variable for storing the connection passed to the constructor
private System.Data.SqlClient.SqlConnection _Connection;

public DataContext(System.Data.SqlClient.SqlConnection Connection) : base(Connection)
{
    // Only set the reference if the connection is Valid and Open during construction
    if (Connection != null)
    {
        if (Connection.State == System.Data.ConnectionState.Open)
        {
            _Connection = Connection;                    
        }
    }           
}

protected override void Dispose(bool disposing)
{        
    // Only try closing the connection if it was opened during construction    
    if (_Connection!= null)
    {
        _Connection.Close();
        _Connection.Dispose();
    }

    base.Dispose(disposing);
}

Причина этого, а не некоторые из приведенных выше предложений заключается в том, что доступ к this.Connection в методе dispose вызывает ObjectDisposedException.

И это работает так же, как я надеялся!

Ответ 6

Dispose должен закрыть соединения, поскольку MSDN указывает:

Если SqlConnection выходит из scope, он не будет закрыт. Следовательно, вы должны явно закрыть соединение путем вызова Close или Dispose. Закрыть и удалить функционально эквивалентны. Если значение объединения пулов установлено значение true или yes, основной соединение возвращается к пул соединений. С другой стороны, если Для пула установлено значение "ложь" или "нет", базовое соединение с сервером замкнутый.

Я предполагаю, что ваша проблема имеет отношение к GetContext().