Al ejecutar un procedimiento almacenado de Oracle desde una aplicación que utiliza ADO.NET, se genera el siguiente mensaje de error:

Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
Description:
An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
Source Error:
An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.
Stack Trace:
[AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.]
Oracle.DataAccess.Client.OpsSql.ExecuteReader(IntPtr opsConCtx, IntPtr& opsErrCtx, IntPtr& opsSqlCtx, IntPtr& opsDacCtx, IntPtr& opsReaderErrCtx, IntPtr opsSubscrCtx, Int32& isSubscrRegistered, Int32 bchgNTFNExcludeRowidInfo, Int32 bQueryBasedNTFNRegistration, Int64& query_id, OpoSqlValCtx*& pOpoSqlValCtx, String pCommandText, OpoDacValCtx*& pOpoDacValCtx, IntPtr[] pOpoPrmValCtx, String[] ppOpoPrmRefCtx, OpoMetValCtx*& pOpoMetValCtx, Int32 NoOfParams) +0
Oracle.DataAccess.Client.OracleCommand.ExecuteReader(Boolean requery, Boolean fillRequest, CommandBehavior behavior) +2789
Oracle.DataAccess.Client.OracleCommand.ExecuteDbDataReader(CommandBehavior behavior) +42
System.Data.Common.DbCommand.ExecuteReader(CommandBehavior behavior) +10

En internet se encuentran muchas razones validas, conflicto entre las versiones del cliente Oracle y el servidor, algunos problemas de configuración del ODP.NET y el Oracle Cliente (home, tsnames, register, etc..), que no permite seleccionar la versión correcta de las librerías cliente al momento de comunicarse con el servidor. Básicamente cualquier cosa que afecte el búfer de ejecución o la transferencia de datos entre cliente y servidor de Oracle puede generar esta excepción. Sin embargo, todavía existen muchos reportes en los foros donde se corrigen todo los puntos referentes al ambiente y la configuración de la plataforma y aun sigue presentándose el error.
Debo decir que antes de llegar a estas conclusiones y solucionar el problema, pase mucho tiempo probando diferentes soluciones y siguiendo recomendaciones hechas en muchos foros. Cuando nada de esto funciono, empezamos a comparar que diferenciaba este procedimiento en especial a los demás y de allí llegamos al acercamiento que el problema estaba en la declaración de los parámetros.
En mi caso se presentaban las siguientes características:
- El procedimiento almacenado tiene un parámetro marcado como «IN OUT» de tipo «DATE».
- Dentro del cuerpo del procedimiento almacenado se asigna un nuevo valor al parámetro.
- En el comando de ADO.NET, en la colección de «Parameters», este parámetro se adiciono como «DbType.DateTime«.
Colocando cometarios para inhabilitar parte del código del procedimiento almacenado, llegamos a la conclusión que el error se generaba cuando se cambiaba el valor o se asignaba un nuevo valor al parámetro, como esta misma acción se realiza con parámetros de tipo «VARCHAR» y «NUMERIC» sin ningún error, nos concentramos en la declaración del tipo de dato del parámetro.
Es importante aclarar que cuando los parámetros en Oracle son definidos utilizando los tipos genéricos ADO.NET («DbType») el ODP.NET realiza el respectivo mapeo a los tipos «OracleDbType». En el caso del «DbType.DateTime» es mapeado a «OracleDbType.TimeStamp» con el fin de conservar las milésimas de segundo, pues el tipo de dato en Oracle «DATE» solo almacena hasta segundos.
La primera aproximación fue editar la definición del procedimiento en Oracle y cambiar la declaración del parámetro al tipo de dato «TIMESTAMP», sin embargo el error se volvió a generar, lo cual nos lleva a la primera conclusión:
«Los parámetros de un procedimiento almacenado de tipo «TIMESTAMP» declarados como «IN OUT» para poder asignarles un nuevo valor, generan error «System.AccessViolationException» al ejecutarse desde ADO.NET.»
Después cambiamos en la declaración del comando de ADO.NET y utilizando «OracleParameter» se asignó el tipo de dato «OracleDbType.Date». En este caso la aplicación corrió sin errores y permitió cambiar el valor del parámetro dentro del procedimiento. Lo cual nos lleva a otras dos conclusiones:
«El mapeo realizado por ODP.NET entre «DbType» y «OracleDbType» no es acertado en todos los escenarios y en varios casos es necesario crear un función propia que realice la equivalencia«, esta es nuestra función.
Finalmente, «Es muy importante seleccionar el tipo de dato correcto en los parámetros, sobre todo si estos se van a utilizar como espacios de memoria donde se van a intercambiar valores entre Oracle y ADO.NET (IN OUT), pues NO existe un validación real en tiempo de desarrollo y esto puede generar problemas en tiempo de ejecución de asignación de memoria que derivan en errores de tipo «System.AccessViolationException»»
Ambiente
| Sistema Operativo | Windows Vista Ultimate + Sp1 |
| Microsoft .NET Framework | 3.5 |
| Microsoft Visual Studio | 2008 Versión 9.0.21022.8 RTM |
| ODP.NET | 11.1.0.6.20 |
| Oracle | 10g |