2009-07-23 8 views
4

Die folgende Methode in einer statischen Klasse gibt mir eine Timeout-Ausnahme, da der Verbindungspool maximiert ist.Verbindungsverlust in C# DataBase.ExecuteScalar

Während ich im Debug-Modus sah ich in sql Management Studio und sah, gab es 150 Schlaf-Prozesse.

Ich erwartete, dass die Verbindungen automatisch geschlossen werden ... Ich habe auch versucht, als ein statisches Mitglied setzen und immer noch den gleichen Fehler.

Irgendwelche Ideen? here der Code.

public static Decimal ExecuteScalarDec(string procName, params object[] parameters) 
{ 
    try 
    { 
     return (Decimal)DatabaseFactory.CreateDatabase().ExecuteScalar(procName, parameters); 
    } 
    catch (Exception ex) 
    { 
     throw new Exception(procName.ToString() + " " + parameters.ToString(), ex); 
    } 
} 

„Mit dem Design, handhaben die meisten der Datenbank Klassenmethoden das Öffnen und Schließen von Verbindungen bei jedem Aufruf an die Datenbank daher der Anwendungscode muss nicht enthalten Code zum Verwalten von Verbindungen. ". ExecuteReader ist eine Ausnahme (weil es eine Ressource zurückgibt). ExecuteScalar befindet sich in der Schwebe: Es gibt einen "Skalar" zurück. Ich denke jedoch, dass der Skalar ziemlich schwer sein kann, z. Ein Stream, der aus einer großen Datentyp-Rückgabe erstellt wurde und die die Verbindung offen lassen müsste. - Remus Rusanu

ich nicht auf deine Antwort kommentieren konnte, weil er sagt, „kommentiert erfordert 50 Ruf“ Nachdem ich meine Benutzer registriert ...

Ich bin eine Spalte Id in ExecuteScalar Rückkehr() und der Wert wird zurückgegeben - ich weiß dies, weil der nächste Aufruf zum Ausführen von Skalar nur aufgerufen wird, nachdem ich einen Wert erhalten ... Es ergibt keinen Sinn, dass der Stream für immer offen bleiben wird Und ich sah in SQL Management das Alle Prozesse schlafen.

+0

verfolgen Was macht die ExecuteScalar() -Methode mit dem zugrunde liegenden DbConnection-Objekt - ruft sie Dispose() oder Close() auf, z eine "using" Aussage? –

Antwort

5
public static Decimal ExecuteScalarDec(string procName, params object[] parameters) 
{ 
    try 
    { 
     using (Database database = DatabaseFactory.CreateDatabase()) 
     { 
      return (Decimal)database.ExecuteScalar(procName, parameters); 
     } 
    } 
    catch (Exception ex) 
    { 
     throw new Exception(procName.ToString() + " " + parameters.ToString(), ex); 
    } 
} 

aktualisieren

OK, da dieser EnterpriseLibrary Code. Die Database Klasse implementiert ExecuetScalar wie folgt aus (andere Signaturen wird diese kollabieren schließlich):

public virtual object ExecuteScalar(DbCommand command) 
     { 
      if (command == null) throw new ArgumentNullException("command"); 

      using (ConnectionWrapper wrapper = GetOpenConnection()) 
      { 
       PrepareCommand(command, wrapper.Connection); 
       return DoExecuteScalar(command); 
      } 
     } 

und die ConnectionWrapper verfügt die Verbindung (Ende der Quelldatei in Verbindung), so die Theorie geht, Ihren Anruf sollte sein OK und entsorgen Sie die Verbindung.

Die GetOpenConnection() Methode gibt einen Wrapper, der die Verbindung funktioniert entsorgen ... es sei denn, eine solche existiert in der aktuellen TransactionScopeConnections:

protected ConnectionWrapper GetOpenConnection(bool disposeInnerConnection) 
    { 
     DbConnection connection = TransactionScopeConnections.GetConnection(this); 
     if (connection != null) 
     { 
      return new ConnectionWrapper(connection, false); 
     } 

     return new ConnectionWrapper(GetNewOpenConnection(), disposeInnerConnection); 
    } 

Und hier ist, wie TransactionScopeConnections die Verbindung zurückgibt:

public static DbConnection GetConnection(Database db) 
    { 
     Transaction currentTransaction = Transaction.Current; 

     if (currentTransaction == null) 
      return null; 

     Dictionary<string, DbConnection> connectionList; 
     DbConnection connection; 

     lock (transactionConnections) 
     { 
      if (!transactionConnections.TryGetValue(currentTransaction, out connectionList)) 
      { 
       // We don't have a list for this transaction, so create a new one 
       connectionList = new Dictionary<string, DbConnection>(); 
       transactionConnections.Add(currentTransaction, connectionList); 

       // We need to know when this previously unknown transaction is completed too 
       currentTransaction.TransactionCompleted += OnTransactionCompleted; 
      } 
     } 

     lock (connectionList) 
     { 
      // Next we'll see if there is already a connection. If not, we'll create a new connection and add it 
      // to the transaction's list of connections. 
      // This collection should only be modified by the thread where the transaction scope was created 
      // while the transaction scope is active. 
      // However there's no documentation to confirm this, so we err on the safe side and lock. 
      if (!connectionList.TryGetValue(db.ConnectionString, out connection)) 
      { 
       // we're betting the cost of acquiring a new finer-grained lock is less than 
       // that of opening a new connection, and besides this allows threads to work in parallel 
       connection = db.GetNewOpenConnection(); 
       connectionList.Add(db.ConnectionString, connection); 
      } 
     } 

     return connection; 
    } 

Jetzt, wenn ich mich nicht irre, wird die TransactionsScopeConnections immer eine neue Verbindung für ein brandneues Datenbank-Objekt erstellen (wie in Ihrem Fall) und sie im internen Wörterbuch behalten. Das Database-Objekt implementiert "Disposable" nicht, sodass ich nicht weiß, wer genau die Verbindungen von dieser internen TransactionScopeConnecitons-Liste bereinigen soll.

Matt, ist es möglich, die Schritte in this article about CLR leaks zu verfolgen und zu sehen, ob es eine große Anzahl von Datenbankobjekten in Ihrem Prozess gibt?Laden Sie SOS und tun Sie eine !dumpheap -type Microsoft.Practices.EnterpriseLibrary.Data.Database. Wenn Sie viele Objekte finden, können Sie den Pin-Stack auf einigen von ihnen mit !gcroot <AddressOfObject>

+2

Dies funktioniert nicht, weil die Datenbank nicht Einweg – Matt

+1

ist dann machen Sie es besser Wegwerf oder Sie werden durchgesickerte Verbindungen von jetzt bis zum Tag nachverfolgen. –

+0

Dann ist das die Schuld Ihrer Datenbank-Klasse: Wenn sie nicht verfügbar ist, sollte sie sicherstellen, dass * it * die Verbindung in ihrer ExecuteScalar-Methode entsorgt. –