Any with Oracle causes InvalidCastException

Posts   
 
    
TomDog
User
Posts: 623
Joined: 25-Oct-2005
# Posted on: 16-Feb-2012 01:44:22   

I have a Northwind DB in Oracle and when I call Any() in a query, e.g. Category.Any(), I get:

InvalidCastException, Specified cast is not valid.
StackTrace  
at lambda_method(Closure , Object[] , Int32[] )
   at SD.LLBLGen.Pro.ORMSupportClasses.DataValueProjector.ProjectValue(Object[] sourceValues) in c:\Myprojects\VS.NET Projects\LLBLGen Pro v3.1\Frameworks\LLBLGen Pro\RuntimeLibraries\ORMSupportClasses\Projection\DataValueProjector.cs:line 153

The Northwind.llblgenpro project file is here: http://rapiddevbookcode.codeplex.com/SourceControl/changeset/view/96110#1678547 I have type converters on the Int32 fields to convert them from the System.Int64 coming from ODP.NET

LinqSupportClasses v3.1.11.1115 ORMSupportClasses v3.1.11.1129

Jeremy Thomas
daelmo avatar
daelmo
Support Team
Posts: 8245
Joined: 28-Nov-2005
# Posted on: 16-Feb-2012 05:44:26   

Hi Jeremy,

Please provide the code that generate the exception and the full exception stack trace.

David Elizondo | LLBLGen Support Team
TomDog
User
Posts: 623
Joined: 25-Oct-2005
# Posted on: 16-Feb-2012 08:13:05   

daelmo wrote:

Please provide the code that generate the exception and the full exception stack trace.

I ran Category.Any() in LinqPad

   at lambda_method(Closure , Object[] , Int32[] )
   at SD.LLBLGen.Pro.ORMSupportClasses.DataValueProjector.ProjectValue(Object[] sourceValues) in c:\Myprojects\VS.NET Projects\LLBLGen Pro v3.1\Frameworks\LLBLGen Pro\RuntimeLibraries\ORMSupportClasses\Projection\DataValueProjector.cs:line 153
   at SD.LLBLGen.Pro.ORMSupportClasses.ProjectionUtils.FetchProjectionFromReader(List`1 valueProjectors, IGeneralDataProjector projector, IDataReader datasource, Int32 maxNumberOfItemsToReturn, Int32 pageNumber, Int32 pageSize, Boolean clientSideLimitation, Boolean clientSideDistinctFiltering, Boolean clientSidePaging, UniqueList`1 stringCache, Dictionary`2 typeConvertersToRun) in c:\Myprojects\VS.NET Projects\LLBLGen Pro v3.1\Frameworks\LLBLGen Pro\RuntimeLibraries\ORMSupportClasses\Projection\ProjectionUtils.cs:line 125
   at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.FetchProjection(List`1 valueProjectors, IGeneralDataProjector projector, IRetrievalQuery queryToExecute, Dictionary`2 typeConvertersToRun) in c:\Myprojects\VS.NET Projects\LLBLGen Pro v3.1\Frameworks\LLBLGen Pro\RuntimeLibraries\ORMSupportClasses\AdapterSpecific\DataAccessAdapterBase.cs:line 1715
   at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.FetchProjection(List`1 valueProjectors, IGeneralDataProjector projector, IEntityFields2 fields, IRelationPredicateBucket filter, Int32 maxNumberOfItemsToReturn, ISortExpression sortClauses, IGroupByCollection groupByClause, Boolean allowDuplicates, Int32 pageNumber, Int32 pageSize) in c:\Myprojects\VS.NET Projects\LLBLGen Pro v3.1\Frameworks\LLBLGen Pro\RuntimeLibraries\ORMSupportClasses\AdapterSpecific\DataAccessAdapterBase.cs:line 1671
   at SD.LLBLGen.Pro.LinqSupportClasses.LLBLGenProProvider2.ExecuteValueListProjection(QueryExpression toExecute)
   at SD.LLBLGen.Pro.LinqSupportClasses.LLBLGenProProviderBase.ExecuteExpression(Expression handledExpression)
   at SD.LLBLGen.Pro.LinqSupportClasses.LLBLGenProProviderBase.System.Linq.IQueryProvider.Execute[TResult](Expression expression)
   at System.Linq.Queryable.Any[TSource](IQueryable`1 source)
Jeremy Thomas
Walaa avatar
Walaa
Support Team
Posts: 14995
Joined: 21-Aug-2005
# Posted on: 16-Feb-2012 10:52:48   

Reproduced. with the following code:

using (var adapter = new DataAccessAdapter())
{
    var metaData = new LinqMetaData(adapter);
    var z = metaData.Department.Any();
}
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39910
Joined: 17-Aug-2003
# Posted on: 16-Feb-2012 13:55:06   

The query returns an integer, 1. The projection has the lambda: (Convert(Convert(Convert.ChangeType(value, System.Boolean)))

where the converts are casts (from object to bool to object. I know, this is silly, but it's to prevent cast problems from boxed value types). When I execute this: (object)(bool)System.Convert.ChangeType(value, typeof(bool))

it simply works (1 is convertible to bool, results in true). Though the lambda crashes internally. As it's a compiled lambda, it's almost impossible to debug disappointed

Really strange why this fails though... the casts in place should work and not fail. It's unclear which cast fails as well, likely the converts but which one... unclear.

I'll modify the expression a bit to see whether that solves anything.

Frans Bouma | Lead developer LLBLGen Pro
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39910
Joined: 17-Aug-2003
# Posted on: 16-Feb-2012 14:26:47   

Ok, it turns out the '1' returned is a 'decimal', not an 'int'. So when I compile the lambda and invoke it as:

((ProjectionValueProducerFunc)((ValueListProjectionDefinition)toExecute.Projection).ProjectionInstantiatorLambda.Compile())(new object[] { 1}, new int[] {0})

results in true.

same as:

((ProjectionValueProducerFunc)((ValueListProjectionDefinition)toExecute.Projection).ProjectionInstantiatorLambda.Compile())(
new object[] {(decimal)1}, new int[] {0})

But, the compiled variant of the same lambda (compiled exactly as above), fails when I call it as:

_valueProducerFunc(new object[] {(decimal)1}, new int[] {0})

It fails with a cast exception.

it succeeds when I do:

_valueProducerFunc(new object[] {1}, new int[] {0})

Lambda really (object)(bool)Convert.ChangeType(values[indices[0]], typeof(bool))....

(edit) flushed , there are 2 steps of course in the projection system: from raw value array from reader to an object array which is then passed to the result projector. The first step fails, the lambda I was looking at is the second lambda. Looking into what the first lambda is ...

(edit) found it. the raw value -> value for projection array lambda is: (System.Object)((System.Int32)$values[$indices[0]] > 0)

and this of course fails if the value is a decimal. Problem is: this code is ran a lot of times during a fetch. Adding a Convert.ChangeType() method call will likely cost in performance, so I've to check where this int32 cast comes from.

Frans Bouma | Lead developer LLBLGen Pro
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39910
Joined: 17-Aug-2003
# Posted on: 16-Feb-2012 15:38:24   

Fixed. Changed the Convert(value, int) expression to a call to Convert.ToInt32(object). It's slightly slower, but only a tiny fraction and is likely to get inlined...

See attached dll.

Attachments
Filename File size Added on Approval
SD.LLBLGen.Pro.LinqSupportClasses.NET35.zip 89,562 16-Feb-2012 15:38.30 Approved
Frans Bouma | Lead developer LLBLGen Pro
TomDog
User
Posts: 623
Joined: 25-Oct-2005
# Posted on: 17-Feb-2012 13:36:18   

Otis wrote:

Fixed. Changed the Convert(value, int) expression to a call to Convert.ToInt32(object). It's slightly slower, but only a tiny fraction and is likely to get inlined...

Yep sorted smile

Jeremy Thomas
spi
User
Posts: 11
Joined: 28-Jul-2009
# Posted on: 31-Oct-2012 16:09:54   

Hi,

we are on version 2.6, facing the same issue. Is it possible to backport the fix to 2.6?

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39910
Joined: 17-Aug-2003
# Posted on: 31-Oct-2012 16:16:43   

We have to check, code in this area was refactored a bit in v3. We'll get back to you on this tomorrow (thursday)

Frans Bouma | Lead developer LLBLGen Pro
spi
User
Posts: 11
Joined: 28-Jul-2009
# Posted on: 31-Oct-2012 16:31:13   

Thanks.

On a sidenote:

We got around the issue on Oracle 10+ by forcing ODP.Net to return an int32 by changing the default BooleanInProjectionWrapper to return the 1 as number(5,0).


FunctionMappingStore defaultstore = new SD.LLBLGen.Pro.DQE.Oracle.DynamicQueryEngine().FunctionMappings;
defaultstore.Add(new FunctionMapping(typeof(object), "BooleanInProjectionWrapper", 1, "CAST(CASE WHEN {0} THEN 1 ELSE 0 END AS NUMBER(5,0))"));

But this doesn't seem to work on Oracle 9.2. Always returns a decimal in the DataReader, regardless of any casting.


SELECT CAST(1 AS NUMBER(5,0)) FROM DUAL

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39910
Joined: 17-Aug-2003
# Posted on: 01-Nov-2012 15:32:24   

The fix was easy to port (2 lines of code). We create a lambda in-memory and it was easy to port back the specific fix for this boolean convert lambda. I've attached a new release build of v2.6's linq provider which contains the fix to this issue. Please let us know whether this doesn't solve your problem (you can remove the change you made to the functions, as it's no longer needed). simple_smile

Attachments
Filename File size Added on Approval
SD.LLBLGen.Pro.LinqSupportClasses.NET35.zip 88,390 01-Nov-2012 15:32.30 Approved
Frans Bouma | Lead developer LLBLGen Pro
spi
User
Posts: 11
Joined: 28-Jul-2009
# Posted on: 01-Nov-2012 15:51:48   

Problem solved, thanks again.

spi
User
Posts: 11
Joined: 28-Jul-2009
# Posted on: 07-Nov-2012 09:11:49   

Will you release this as a new runtime libraries package build including sourcecode? I like my source code control to be consistent with my binaries.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39910
Joined: 17-Aug-2003
# Posted on: 07-Nov-2012 09:56:24   

spi wrote:

Will you release this as a new runtime libraries package build including sourcecode? I like my source code control to be consistent with my binaries.

We'll do that later today. simple_smile

Frans Bouma | Lead developer LLBLGen Pro
spi
User
Posts: 11
Joined: 28-Jul-2009
# Posted on: 07-Nov-2012 09:59:01   

Great!

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39910
Joined: 17-Aug-2003
# Posted on: 07-Nov-2012 17:21:56   

It's online now simple_smile (customer area, v2.6 section)

Frans Bouma | Lead developer LLBLGen Pro