Problem checking for DBNull.Value in return value from Oracle stored proc. using ODP.NET 10.2.0.2.20
Joined: 13-Oct-2005
Hi
We are currently using LLBLGen v1.0.2005.1 Final (July 6th, 2006) and have upgraded to v2.0.0.0 Final DEMO (Jan 23rd, 2007). The SD.LLBLGen.Pro.ORMSupportClasses.NET20.dll is v2.0.7.209 and we are using Adapter scenario.
In the process of upgrading we have upgraded from ODP.NET v9.2.0.7 to ODP.NET v2.102.2.20 (or is it called 10.2.0.2.20?). We are using Oracle database version 10.2.0.2. We have also regenerated the code and have added references to new Oracle and LLBLGen dll's in our project files (using C# and .Net v2.0.50727). The order of the "Template bindings" when generating code in the LLBLGen designer is not changed.
We have a problem with the return value for the generated code when calling a stored procedure. The generated code is as follows:
/// Calls stored procedure
public static int GetNextMessage(ref System.String returnValue, DataAccessAdapter adapter)
{
OracleParameter[] parameters = new OracleParameter[1];
parameters[0] = new OracleParameter("RETURN_VALUE", OracleDbType.Varchar2, 4000, ParameterDirection.ReturnValue, true, 0, 0, "", DataRowVersion.Current, returnValue);
int toReturn = adapter.CallActionStoredProcedure("DIPSCOREDB.CIMELDING.DWF_HENTNESTEMELDING", parameters);
if(parameters[0].Value!=System.DBNull.Value) //PROBLEM! Statement is true even when procedure returns NULL
{
//This code throws the exception
returnValue = (System.String)ValueConverter.Convert(parameters[0]);
}
for(int i=0;i<1;i++)
{
if(parameters[i] != null)
{
parameters[i].Dispose();
}
}
return toReturn;
}
The stored procedure returns NULL. When running with the "old" version of LLBLGen code and Oracle driver, the DBNull check (if(parameters[0].Value!=System.DBNull.Value)) works fine, but with the ODP.Net v2.102.2.20 this check fails the null test. The error message we get is:
Oracle.DataAccess.Types.OracleNullValueException: Invalid operation on null data at Oracle.DataAccess.Types.OracleString.get_Value() at DIPS.Core.Generated.DatabaseSpecific.ValueConverter.Convert(OracleParameter parameter) in C:\DIPS-scm\Dev\Med_v1\Modules\Core\Server\Database\DIPS.Core.Server.DB\Generated\DatabaseSpecific\ActionProcedures.cs:line 874 at DIPS.Core.Generated.DatabaseSpecific.ActionProcedures.MeldingDwfHentNesteMelding(String& returnValue, DataAccessAdapter adapter) in C:\DIPS-scm\Dev\Med_v1\Modules\Core\Server\Database\DIPS.Core.Server.DB\Generated\DatabaseSpecific\ActionProcedures.cs:line 341 at DIPS.Core.MessagingDataAccess.RetrieveMessage(DataAccessAdapter adapter) in C:\DIPS-scm\Dev\Med_v1\Modules\Core\Server\Database\DIPS.Core.Server.DB\MessagingDataAccess.cs:line 31 at DIPS.Core.Messaging.MessagingService.RetrieveMessage(DataAccessAdapter adapter) in C:\DIPS-scm\Dev\Med_v1\Modules\Core\Server\DIPS.Core.Server\Messaging\MessagingService.cs:line 31 at DIPS.Core.Messaging.RetrieveMessageLoopingWorker.RetrieveAndHandleMessage() in C:\DIPS-scm\Dev\Med_v1\Modules\Core\Server\DIPS.Core.WindowsService.Server\Messaging\RetrieveMessageLoopingWorker.cs:line 291 at DIPS.Core.Messaging.RetrieveMessageLoopingWorker.DoLoopWork() in C:\DIPS-scm\Dev\Med_v1\Modules\Core\Server\DIPS.Core.WindowsService.Server\Messaging\RetrieveMessageLoopingWorker.cs:line 128
It seems that Oracle changed the OracleParameter definition from v9 to v10. "Parameters[0].Value" should be "System.DBNull.Value", instead of an OracleString that has a property "IsNull=true". The watch for the parameter variable is:
- parameters[0].Value {null} object {Oracle.DataAccess.Types.OracleString} IsCaseIgnored false bool IsNull true bool
- Length '((Oracle.DataAccess.Types.OracleString)(parameters[0].Value)).Length' threw an exception of type 'Oracle.DataAccess.Types.OracleNullValueException' int {Oracle.DataAccess.Types.OracleNullValueException} m_bCaseIgnored false bool m_bNotNull false bool m_value null string -Value '((Oracle.DataAccess.Types.OracleString)(parameters[0].Value)).Value' threw an exception of type 'Oracle.DataAccess.Types.OracleNullValueException' string {Oracle.DataAccess.Types.OracleNullValueException} +base {"Invalid operation on null data"} Oracle.DataAccess.Types.OracleTypeException {Oracle.DataAccess.Types.OracleNullValueException}
What am I missing? It seems that other database operations on tables (select, etc) works fine. I have also checked that the correct Oracle driver is used.
Another thing regarding the new LLBLGen designer: the "Migrating your code" section of the documentation states "PredicateFactory class are no longer generated by default. You have to enable that task explicitly in the preset you're using." How can you "enable the task explicitly", that is, is it possible to generate this class with LLBLGen Pro 2.0?
Thanks, Kjell-Arne
Hmm. So oracle decided in 10.2 for oracle to convert the value to an oracle type class anyway and set a property on that, instead of keeping it simple and return DBNull.Value..
I could move up the valueconvert so the result of that is used in the test.
You're moving from 9i to 10g ODP.NET. Did you convert the .lgp file to the 10g driver first? If not, the templates used are still the ones of 9i.
After conversion with the project converter (see 2.0 extras section in customer area) you're using the 10g driver and thus also the 10g templates.
Could you test for me if this works: (first create copy of the template) in the templates\Oracle10gSpecific\Net2.x\C# folder, open actionProcecduresAdapter.template in a texteditor, for example notepad.
Starting at line 64, you'll find this snippet:
<[Foreach OutputParameter CrLf]> if(parameters[<[ParameterIndex]>].Value!=System.DBNull.Value)
{
<[CaseCamel CurrentParameterName]> = (<[If IsNullable]><[If IsValueType]>Nullable<<[TypeOfParameter]>><[Else]><[ TypeOfParameter]><[EndIf]><[Else]><[TypeOfParameter]><[EndIf]>)ValueConverter.Convert(parameters[<[ParameterIndex]>]);
}<[NextForeach]>
which generates the faulty piece of code.
If you change that into:
<[Foreach OutputParameter CrLf]> object parameterValue = ValueConverter.Convert(parameters[<[ParameterIndex]>]);
if((parameterValue!=null) && (parameterValue!=System.DBNull.Value))
{
<[CaseCamel CurrentParameterName]> = (<[If IsNullable]><[If IsValueType]>Nullable<<[TypeOfParameter]>><[Else]><[TypeOfParameter]><[EndIf]><[Else]><[TypeOfParameter]><[EndIf]>)parameterValue;
}<[NextForeach]>
and starting at line 160, you'll have this:
case "Oracle.DataAccess.Types.OracleString":
toReturn = ((OracleString)parameter.Value).Value;
break;
If you change that into:
case "Oracle.DataAccess.Types.OracleString":
if(((OracleString)parameter.Value).IsNull)
{
toReturn = null;
}
else
{
toReturn = ((OracleString)parameter.Value).Value;
}
break;
and generate code again, it should work.
What's sad is that Oracle did implement their types with an interface INullable, so nulltesting COULD be done, however... their smart programmers made that interface... internal..
ABout the tasks, please go to tab 3 in the code generation configuration screen. You'll see there the tasks being disabled for predicatefactory and sortclause factory. Select the task you want to enable, and check the isEnabled checkbox, then save the preset under a different name, as described in the documentation.
Joined: 13-Oct-2005
Thanks for the fast reply!
I converted the LLBLGen project file and altered the template as you described. It all seems to work fine now. As long as there are no other breaking changes when migrating to ODP.NET 10.2.0.2.20 we should be ok.
One thing though: when changing the template code to
<[Foreach OutputParameter CrLf]> object parameterValue = ValueConverter.Convert(parameters[<[ParameterIndex]>]);
etc...
you will get the "object parameterValue" declaration repeated for each parameter in the generated code when using several parameters. I guess you could just move the declaration of the "parameterValue" outside the "Foreach" loop.
Is there going to be a fix released anywhere in the nearby future?
KAF wrote:
Thanks for the fast reply!
I converted the LLBLGen project file and altered the template as you described. It all seems to work fine now. As long as there are no other breaking changes when migrating to ODP.NET 10.2.0.2.20 we should be ok.
One thing though: when changing the template code to
<[Foreach OutputParameter CrLf]> object parameterValue = ValueConverter.Convert(parameters[<[ParameterIndex]>]); etc...
you will get the "object parameterValue" declaration repeated for each parameter in the generated code when using several parameters. I guess you could just move the declaration of the "parameterValue" outside the "Foreach" loop.
yes, that's indeed a mistake from my part, thanks for that so the code becomes:
object parameterValue = null;
<[Foreach OutputParameter CrLf]> parameterValue = ValueConverter.Convert(parameters[<[ParameterIndex]>]);
if((parameterValue!=null) && (parameterValue!=System.DBNull.Value))
{
<[CaseCamel CurrentParameterName]> = (<[If IsNullable]><[If IsValueType]>Nullable<<[TypeOfParameter]>><[Else]><[TypeOfParameter]><[EndIf]><[ Else]><[TypeOfParameter]><[EndIf]>)parameterValue;
}<[NextForeach]>
Is there going to be a fix released anywhere in the nearby future?
Yes. Our main office is currently being redecorated/restuctured so we're currently located in a temporary office and I can't reach the oracle testbox from here via Wifi, it has low or no reception from my devbox (which isn't a laptop unfortunately). So I expect to test it in-house in about a week or so.
I hope you can fix your own project with the template fix. Please let me know if this template fix indeed doesn't or does work. Sorry for the inconvenience.
Joined: 12-Nov-2006
We ran into the same null value problem as described in this post, but the suggested fix seemed to only provide half of the solution, even after upgrading to the 16-Apr-2007 release. In addition to making the suggested ForEach fix, we followed the example provided for handling null strings and modified the template further, changing the "OracleDecimal" case in ValueConverter.Convert to the following:
case "Oracle.DataAccess.Types.OracleDecimal":
if(((OracleDecimal)parameter.Value).IsNull)
{
toReturn = null;
}
else
{
decimal value = ((OracleDecimal)parameter.Value).Value;
int actualPrecision = parameter.Precision;
if(parameter.Precision==0)
{
actualPrecision = 38;
}
toReturn = value;
// Following code is based on ODP.NET's conversion routine, as it is pretty obscure what ODP.NET's doing with values read. The if statements are from ODP.NET.
if(parameter.Scale==0)
{
if(actualPrecision < 5)
{
toReturn = System.Convert.ToInt16(value);
}
else
{
if(actualPrecision < 10)
{
toReturn = System.Convert.ToInt32(value);
}
else
{
if(actualPrecision < 19)
{
toReturn = System.Convert.ToInt64(value);
}
}
}
}
else
{
if(actualPrecision < 8)
{
toReturn = System.Convert.ToSingle(value);
}
else
{
if(actualPrecision < 16)
{
toReturn = System.Convert.ToDouble(value);
}
}
}
}
break;
After we made this change everything worked as expected. If there is something we're missing and there's some important reason we shouldn't have implemented this change, please let me know!
Thanks for the excellent product!!
Zack
Joined: 13-Oct-2005
That is correct, we modified the template as you described only a couple of days ago when we ran into the same problem with the OracleDecimal type. It works fine after the fix. Was about to post a note here about it .
Hopefully it will be included in a future release.
Kjell-Arne
Joined: 27-Jun-2007
KAF wrote:
Hi
We are currently using LLBLGen v1.0.2005.1 Final (July 6th, 2006) and have upgraded to v2.0.0.0 Final DEMO (Jan 23rd, 2007). The SD.LLBLGen.Pro.ORMSupportClasses.NET20.dll is v2.0.7.209 and we are using Adapter scenario.
In the process of upgrading we have upgraded from ODP.NET v9.2.0.7 to ODP.NET v2.102.2.20 (or is it called 10.2.0.2.20?). We are using Oracle database version 10.2.0.2. We have also regenerated the code and have added references to new Oracle and LLBLGen dll's in our project files (using C# and .Net v2.0.50727). The order of the "Template bindings" when generating code in the LLBLGen designer is not changed.
We have a problem with the return value for the generated code when calling a stored procedure. The generated code is as follows:
/// Calls stored procedure public static int GetNextMessage(ref System.String returnValue, DataAccessAdapter adapter) { OracleParameter[] parameters = new OracleParameter[1]; parameters[0] = new OracleParameter("RETURN_VALUE", OracleDbType.Varchar2, 4000, ParameterDirection.ReturnValue, true, 0, 0, "", DataRowVersion.Current, returnValue); int toReturn = adapter.CallActionStoredProcedure("DIPSCOREDB.CIMELDING.DWF_HENTNESTEMELDING", parameters); if(parameters[0].Value!=System.DBNull.Value) //PROBLEM! Statement is true even when procedure returns NULL { //This code throws the exception returnValue = (System.String)ValueConverter.Convert(parameters[0]); } for(int i=0;i<1;i++) { if(parameters[i] != null) { parameters[i].Dispose(); } } return toReturn; }
The stored procedure returns NULL. When running with the "old" version of LLBLGen code and Oracle driver, the DBNull check (if(parameters[0].Value!=System.DBNull.Value)) works fine, but with the ODP.Net v2.102.2.20 this check fails the null test. The error message we get is:
Oracle.DataAccess.Types.OracleNullValueException: Invalid operation on null data at Oracle.DataAccess.Types.OracleString.get_Value() at DIPS.Core.Generated.DatabaseSpecific.ValueConverter.Convert(OracleParameter parameter) in C:\DIPS-scm\Dev\Med_v1\Modules\Core\Server\Database\DIPS.Core.Server.DB\Generated\DatabaseSpecific\ActionProcedures.cs:line 874 at DIPS.Core.Generated.DatabaseSpecific.ActionProcedures.MeldingDwfHentNesteMelding(String& returnValue, DataAccessAdapter adapter) in C:\DIPS-scm\Dev\Med_v1\Modules\Core\Server\Database\DIPS.Core.Server.DB\Generated\DatabaseSpecific\ActionProcedures.cs:line 341 at DIPS.Core.MessagingDataAccess.RetrieveMessage(DataAccessAdapter adapter) in C:\DIPS-scm\Dev\Med_v1\Modules\Core\Server\Database\DIPS.Core.Server.DB\MessagingDataAccess.cs:line 31 at DIPS.Core.Messaging.MessagingService.RetrieveMessage(DataAccessAdapter adapter) in C:\DIPS-scm\Dev\Med_v1\Modules\Core\Server\DIPS.Core.Server\Messaging\MessagingService.cs:line 31 at DIPS.Core.Messaging.RetrieveMessageLoopingWorker.RetrieveAndHandleMessage() in C:\DIPS-scm\Dev\Med_v1\Modules\Core\Server\DIPS.Core.WindowsService.Server\Messaging\RetrieveMessageLoopingWorker.cs:line 291 at DIPS.Core.Messaging.RetrieveMessageLoopingWorker.DoLoopWork() in C:\DIPS-scm\Dev\Med_v1\Modules\Core\Server\DIPS.Core.WindowsService.Server\Messaging\RetrieveMessageLoopingWorker.cs:line 128
It seems that Oracle changed the OracleParameter definition from v9 to v10. "Parameters[0].Value" should be "System.DBNull.Value", instead of an OracleString that has a property "IsNull=true". The watch for the parameter variable is:
- parameters[0].Value {null} object {Oracle.DataAccess.Types.OracleString} IsCaseIgnored false bool IsNull true bool
- Length '((Oracle.DataAccess.Types.OracleString)(parameters[0].Value)).Length' threw an exception of type 'Oracle.DataAccess.Types.OracleNullValueException' int {Oracle.DataAccess.Types.OracleNullValueException} m_bCaseIgnored false bool m_bNotNull false bool m_value null string -Value '((Oracle.DataAccess.Types.OracleString)(parameters[0].Value)).Value' threw an exception of type 'Oracle.DataAccess.Types.OracleNullValueException' string {Oracle.DataAccess.Types.OracleNullValueException} +base {"Invalid operation on null data"} Oracle.DataAccess.Types.OracleTypeException {Oracle.DataAccess.Types.OracleNullValueException}
What am I missing? It seems that other database operations on tables (select, etc) works fine. I have also checked that the correct Oracle driver is used.
Another thing regarding the new LLBLGen designer: the "Migrating your code" section of the documentation states "PredicateFactory class are no longer generated by default. You have to enable that task explicitly in the preset you're using." How can you "enable the task explicitly", that is, is it possible to generate this class with LLBLGen Pro 2.0?
Thanks, Kjell-Arne
hi thre
im a data base programmer few days back i came across a prolm regarding oracle installation and configuration, on friends recommendation i consult www.oracleplace.com which realy help me ,may be it could help u solving ur problem 2.
best of luk
karen jones