2008-11-06 15 views
16

Proszę pomóc!. Połączenie SqlConnection nie jest zamykane, nawet jeśli używasz {}

informacji Tło

Mam aplikacji WPF, który uzyskuje dostęp do bazy danych SQL Server 2005. Baza danych działa lokalnie na komputerze, na którym działa aplikacja.

Wszędzie Używam DataContext Linq Używam instrukcji {} i używam funkcji, która zwraca obiekt SqlConnection, który został otwarty i wykonał SqlCommand używając go przed powrotem do konstruktora DataContext. To znaczy

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

gdzie getConnection wygląda następująco (mam odpędza się z „puch” z funkcji, aby uczynić go bardziej czytelnym, ale nie ma żadnych dodatkowych funkcji, której brakuje).

// 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; 
    } 

Nie sądzę, że wniosek jest jeden WPF ma żadnego wpływu na ten temat mam.

Problem mam

pomimo SqlConnection jest umieszczony wraz z DataContext w studiu SQL Server Management Wciąż widzę mnóstwo otwartych połączeń z:

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 

ostatecznie przyłącza basen zostaje zużyty, a aplikacja nie może kontynuować.

Tak więc mogę tylko stwierdzić, że w jakiś sposób uruchamianie polecenia SQLC do ustawienia Context_Info oznacza, że ​​połączenie nie zostanie usunięte, gdy DataContext zostanie usunięty.

Czy ktoś zauważy coś oczywistego, co powstrzymałoby połączenie przed zamknięciem i usunięciem, gdy DataContext, z którego są używane, zostanie usunięte?

Odpowiedz

19

Od MSDN (DataContext Constructor (IDbConnection)):

Jeśli stworzenie otwartego połączenia, DataContext nie będzie go zamknąć. W związku z tym nie należy tworzyć instancji DataContext z otwartym połączeniem , chyba że masz dobry powód, aby wykonać to .

Wygląda na to, że połączenia oczekują na GC, aby je sfinalizować, zanim zostaną zwolnione. Jeśli masz dużo kodu, który to robi, jednym z podejść może być przepełnienie Dispose() w częściowej klasie kontekstu danych i zamknięcie połączenia - wystarczy tylko udokumentować, że kontekst danych przejmuje własność połączenia!

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); 
    } 

Osobiście szczęśliwie dać (regularne danych, kontekst, w/o włamania powyżej) powstaje otwarte połączenie, tak długo jak „używa” połączenie (co pozwala mi wykonać wiele operacji) - tj

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 
} 
0

Dispose powinien zamknąć połączenia, jak MSDN zaznacza:

Jeśli SqlConnection wychodzi zakresie, to nie zostaną zamknięte. Dlatego należy jawnie zamknąć połączenie , wywołując Close lub Dispose. Zamknij i usuń są funkcjonalnie równoważne. Jeśli wartość łączenia puli połączeń wynosi ustawiona na wartość true lub yes, podstawowe połączenie jest zwracane z powrotem do puli połączeń . Z drugiej strony, jeśli Łączenie jest ustawione na wartość fałsz lub nie, to podstawowe połączenie z serwerem jest zamknięte.

Zgaduję, że twój problem ma coś wspólnego z GetContext().

7

Użyty przez LINQ SqlProviderDataContext zamyka połączenie SQL (przez SqlConnectionManager.DisposeConnection), jeśli to on je otworzył. Jeśli podasz już otwarty obiekt SqlConnection do konstruktora DataContext, nie zostanie on zamknięty dla ciebie. Tak więc powinieneś napisać:

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

Myślę, że połączenie, które nie jest już wymienione, czeka na to, aby GC w pełni go zbyło.

Rozwiązanie:

Stwórz własną klasę DataContext pochodzący od jednego auto generowane. (zmień nazwę bazową, aby nie trzeba było zmieniać żadnego innego kodu).

W swoim pochodnym DataContext - dodaj funkcję Dispose(). W tym - pozbądź się połączenia wewnętrznego.

+0

Możesz po prostu dodać częściową klasę, aby rozszerzyć automatycznie wygenerowany kontekst danych; nie ma potrzeby tworzenia podklasy. –

1

Well Dzięki za pomoc kumpli, został rozwiązany teraz ..

Zasadniczo Wziąłem elementy większość odpowiedzi powyżej i realizowany konstruktora DataContext jak powyżej (miałem już przeciążone konstruktory więc to nie było” t duża zmiana).

// 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); 
} 

Powodem robi to raczej niż niektóre z powyższych sugestii jest to, że dostęp do this.Connection w sposobie rozporządzania rzuca ObjectDisposedException.

I powyższe działa tak dobrze, jak miałem nadzieję!

+0

Hej, tak właśnie powiedziałem. Muszę być mądry. – GeekyMonkey

3

Doświadczyłem tego samego problemu przy użyciu Entity Framework. Mój ObjectContext został zawinięty wokół bloku using.

Połączenie zostało ustanowione, gdy zadzwoniłem pod numer SaveChanges(), ale po tym, jak oświadczenie using było poza zakresem, zauważyłem, że SQL Management Studio nadal miało "AWAITING COMMAND" dla klienta .NET SQL. Wygląda na to, że ma to związek z zachowaniem dostawcy ADO.NET, który domyślnie ma włączone buforowanie połączeń.

Od "Korzystanie z zestawianiem połączeń z SQL Server" na kopalni MSDN (nacisk):

Connection pooling zmniejsza liczbę razy, że nowe połączenia muszą być otwarte. Pularz zachowuje własność fizycznego połączenia. Zarządza połączeniami utrzymując przy życiu zestaw aktywnych połączeń dla każdej danej konfiguracji połączenia. Za każdym razem, gdy użytkownik dzwoni pod numer Open, osoba przeglądająca sprawdza, czy w puli jest dostępne połączenie. Jeśli połączenie zbiorcze jest dostępne, zwraca je do osoby dzwoniącej zamiast otwierać nowe połączenie. Gdy aplikacja wywołuje Close dla połączenia, to pooler zwraca ją do połączonego zestawu aktywnych połączeń, zamiast faktycznie go zamykać. Po przywróceniu połączenia z pulą, jest ono gotowe do ponownego użycia podczas następnego połączenia Open.

także ClearAllPools i ClearPool wydaje użyteczny jawnie zamknąć wszystkie pule połączeń w razie potrzeby.

Powiązane problemy