How to properly close ODP.net connection : dispose() or close()?

前端 未结 4 493
梦谈多话
梦谈多话 2021-01-14 16:42

this is my powershell code :

[void][System.Reflection.Assembly]::LoadFile(\"C:\\DLL\\Oracle.ManagedDataAccess.dll\")
$OracleConnexion = New-Object Oracle.Ma         


        
4条回答
  •  星月不相逢
    2021-01-14 17:26

    It looks like everybody else, I noticed late that you're in powershell. In that case, it doesn't really matter. Everything is going to get cleaned up when the shell ends regardless. I suppose you could add a [catch] and maybe close/dispose the connection there if it's still open, but I think that would only be necessary if you planned on letting your script continue.

    I'll leave my longwinded c# answer below. Even though it doesn't really apply to your script, it explains the difference (or lack thereof).

    The short answer (for c#):

    using (var conn = new OracleConnection(connectionString))
    {
    }
    

    "using" ensures that .Dispose is called at the end of the block even if an exception is thrown. That way you never risk a connection being orphaned until garbage collection finally gets around to cleaning it up and that might be well after you run out of database connections.

    The long answer:

    Using a reflector, you will see that Dispose calls Close:

    protected override void Dispose(bool disposing)
    {
      if (ProviderConfig.m_bTraceLevelPublic)
        Trace.Write(OracleTraceLevel.Public, OracleTraceTag.Entry);
      this.m_disposed = true;
      this.m_dataSource = string.Empty;
      this.m_serverVersion = string.Empty;
      try
      {
        bool flag = this.m_connectionState == ConnectionState.Closed && this.m_oracleConnectionImpl == null;
        try
        {
          if (!disposing)
          {
            if (!flag)
            {
              if (OraclePool.m_bPerfNumberOfReclaimedConnections)
                OraclePool.PerformanceCounterIncrement(OraclePerfParams.CounterIndex.NumberOfReclaimedConnections, this.m_oracleConnectionImpl, this.m_oracleConnectionImpl.m_cp);
            }
          }
        }
        catch (Exception ex)
        {
          if (ProviderConfig.m_bTraceLevelPublic)
            Trace.Write(OracleTraceLevel.Public, OracleTraceTag.Error, ex.ToString());
        }
        if (!flag)
        {
          try
          {
            this.Close();
          }
          catch (Exception ex)
          {
            if (ProviderConfig.m_bTraceLevelPublic)
              Trace.Write(OracleTraceLevel.Public, OracleTraceTag.Error, ex.ToString());
          }
        }
        try
        {
          base.Dispose(disposing);
        }
        catch (Exception ex)
        {
          if (ProviderConfig.m_bTraceLevelPublic)
            Trace.Write(OracleTraceLevel.Public, OracleTraceTag.Error, ex.ToString());
        }
        try
        {
          GC.SuppressFinalize((object) this);
        }
        catch (Exception ex)
        {
          if (!ProviderConfig.m_bTraceLevelPublic)
            return;
          Trace.Write(OracleTraceLevel.Public, OracleTraceTag.Error, ex.ToString());
        }
      }
      catch (Exception ex)
      {
        if (!ProviderConfig.m_bTraceLevelPublic)
          return;
        Trace.Write(OracleTraceLevel.Public, OracleTraceTag.Error, ex.ToString());
      }
      finally
      {
        if (ProviderConfig.m_bTraceLevelPublic)
          Trace.Write(OracleTraceLevel.Public, OracleTraceTag.Exit);
      }
    }
    

    Is there any real difference? No - the unmanaged resource IS the connection which is taken care of with .Close. You'd see no functional difference (other than delayed tracing) if you checked the connection status in a finally block and called .Close there if it was still open.

      OracleConnection conn = null;
      try
      {
        conn = new OracleConnection(connectionString);
      }
      finally
      {
        if(conn.State != ConnectionState.Closed)
          conn.Close();
      }
    

    That said the recommended pattern for idisposible objects is to use a "using" block. Yes I suppose it is true that you have the option to reopen the connection with close, but I don't see that being a useful thing to do.

    If you didn't use a using or a finally and an exception is thrown and close/dispose is never called, then freeing the connection to the db would be nondeterministic - Dispose(false) would happen whenever the garbage collector got around to it - and that might be long after you run out of connections to your db.

        OracleConnection conn = null;
        conn = new OracleConnection(connectionString);
        conn.Open();
    
        //exception occurs - Close is never called - resource leak!!
    
        conn.Close();
    

提交回复
热议问题