We recently began running into issues with performance on our IIS server. As described in a prior post worker thread processes would crash or become unresponsive. Our research found that it was our code that was the cause. In fact it was due to how we were handling disposing of Oracle connection and especially command objects within the Oracle Data Provider for .NET (ODP.NET). As an aside, how many times does one assume that it is outside of your code but time and time again it is your stuff that is causing the problem and not something else - certainly a topic for a future post.
It wasn't as if our original code totally ignored disposing of ODP.NET objects. In fact we had been pretty consistent with how we disposed of the Oracle connection and command objects. The example below taken from one of our data services (modified slightly to show all calls/etc.) illustrates how we were previously dealing with "cleaning up" after using the ODP.NET.
Public Shared Function GetDevicesByType(ByVal AConnectString as String, ByVal ADeviceType As Integer) As List (Of Device) Dim lDeviceList As List (Of Device) = New List (of Device) Dim lReturnOraParam As OracleParameter Dim lDbConn As New OracleConnection(AConnectString) Dim lOraCmd As New OracleCommand Dim lOraDataAdapt As New OracleDataAdapter Dim lOraDataTab As New DataTable Try lDbConn.Open() lDbConn.Connection = lDbConn lDbConn.CommandType = CommandType.StoredProcedure lDbConn.CommandText = "get_device_plsql.get_device" lReturnOraParam = lDbConn.Parameters.Add("rtn_code", OracleDbType.Int32, 10, ParameterDirection.ReturnValue) lDbConn.Parameters.Add("device_type", OracleDbType.Int32, 50, ADeviceType, ParameterDirection.Input) lDbConn.Parameters.Add("device_rc", OracleDbType.RefCursor, ParameterDirection.Output) ' call plsql lDbConn.ExecuteNonQuery() lOraDataAdapt.SelectCommand = lDbConn lOraDataAdapt.Fill(lOraDataTab) For Each oRow As DataRow In lOraDataTab.Rows AddDevice(lDeviceList, oRow) Next Catch ex As Exception Throw New Exception("Get Device Failure: ", ex) Finally lDbConn = Nothing If Not lOraDataTab Is Nothing Then lOraDataTab.Dispose() End If lOraDataTab = Nothing If Not lOraDataAdapt Is Nothing Then lOraDataAdapt.Dispose() End If lOraDataAdapt = Nothing If Not lDbConn Is Nothing AndAlso lDbConn.State = ConnectionState.Open Then lDbConn.Close() lDbConn.Dispose() End If lDbConn = Nothing End Try Return lDeviceList End Function
There however was one important set of objects that we did not properly dispose of the parameters and more importantly their values. We made a decision at the outset of our last rewrite of our front-end to build our data layer retrieving data using a REF CURSOR.
For Each p As OracleParameter In cmd.Parameters If TypeOf p Is IDisposable Then If TypeOf p.Value Is IDisposable Then DirectCast(p.Value, IDisposable).Dispose() End If DirectCast(p, IDisposable).Dispose() End If Next
The importance to making sure all of the parameters and their values are properly disposed is because the ODP uses both managed and unmanaged code. It is the unamanaged code that requires an explicit call to Dispose so that it is not left up to the mercy of the garbage collector.
No comments:
Post a Comment