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

徘徊边缘 提交于 2019-12-01 11:19:40

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

Close closes the connection and allows you to reopen it again.

Dispose closes the connection if it hasn't been closed and also disposes of it so you can't reopen it again.

Use dispose - dispose frees up memory of a resource, if that resource is open, then a well behaved .dispose method will close the resource.

Dispose() vs. Close() with ConnectionPooling: https://community.oracle.com/thread/165664?start=0&tstart=0

More standard implementation for resource which implement IDisposable is by wrapping it by using:

using (OracleConnection connection = new OracleConnection(connectionString)){
    using (OracleCommand command = new OracleCommand(sql, connection))
        using (OracleDataReader reader = cmd.ExecuteReader())
        {
        }
    connection.Close(); //optional
}

It is equivalent to implementing .Dispose after the execution block. Internally, Dispose will also handle the closing. Nevertheless, you could also call the .Close() after the command block.

A sample in the oracle documentation which uses Oracle.DataAccess encourages the use of Dispose too.

I would wrap your connection by a using statement. When you're done with your connection then close it before adding the bracket. To be 100% safe I would do something like this:

using(OracleConnexion Con = new OracleConnection (...))
{
    Con.Open()
    ...
    Con.Close()
}

Edit:

I added the Con.Close(), because in the past the dispose was not implemented correctly in the ODP.NET. The connection was kept open. We had to force the connection to close manually and that's why in the example, I specify the Close.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!