Problem with exception handler in ActionQuery

Posts   
 
    
RDH
User
Posts: 5
Joined: 12-Jun-2015
# Posted on: 12-Jun-2015 16:42:03   

LLBLGen Version: 4.2 Final (February 5th, 2015) SD.LLBLGen.Pro.ORMSupportClasses.dll version: 4.2.15.127 Stack Trace: System.InvalidCastException: Specified cast is not valid. at SD.LLBLGen.Pro.ORMSupportClasses.Query.ToString(Boolean verbose) at SD.LLBLGen.Pro.ORMSupportClasses.ActionQuery.<ExecuteAsync>d__0.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterCore.<ExecuteActionQueryAsync>d__7a.MoveNext() Database: Oracle Description: It appears that the exception handler(s) in ActionQuery cannot handle parameters which are collections.

The expected behavior is that if ActionQuery gets an exception from the database that it will wrap it with an outer exception which has a message like, “_An exception was caught during the execution of an action query: .... Check InnerException, QueryExecuted and Parameters of this exception to examine the cause of this exception._”.  The inner exception will have the details of the exception from the database.

The behavior we observe is that when some of the database parameters are not scalar (i.e., collections), then a “_Specified cast is not valid_” exception is thrown with no inner exception.  My guess is that exception handler is looking at the database parameters and it gets a cast exception because the parameter type is not what it expects.
Walaa avatar
Walaa
Support Team
Posts: 14993
Joined: 21-Aug-2005
# Posted on: 12-Jun-2015 19:10:45   

Could you please give a code sample, of an ActionQuery taking a Collection as a parameter?

RDH
User
Posts: 5
Joined: 12-Jun-2015
# Posted on: 12-Jun-2015 22:06:45   

Unfortunately, I don't have a simple example available. But, this might help. In the example below I'm trying to do a bulk insert where the database parameter is a byte array.

...
DbCommand command = adapter.GetDbSpecificCreatorInstance().CreateCommand();
OracleParameter parameter = new OracleParameter(":c",OracleDbType.Raw);
parameter.Value = dataTable.AsEnumberable().Select(dr => dr.Field<byte[]>(column).ToArray();
command.Parameters.Add(parameter);
command.CommandText = "insert into a.b (c) values (:c)";
command.ArrayBindCount = dataTable.Rows.Count;
count = await adapter.ExecuteActionQueryAsync(new ActionQuery(command), cancellationToken);
...

I **think **that because the database parameter, ":c", is a byte array rather than a scalar value, that the ActionQuery exception handler is having problems.

Remember, that if there is no issue until the SQL statement returns an error; and thus, causes the ActionQuery exception handler to run.

daelmo avatar
daelmo
Support Team
Posts: 8245
Joined: 28-Nov-2005
# Posted on: 13-Jun-2015 09:58:53   

Hi RDH,

I will try to reproduce your scenario this weekend to give you some feedback on this.

How does look your SP signature?

David Elizondo | LLBLGen Support Team
daelmo avatar
daelmo
Support Team
Posts: 8245
Joined: 28-Nov-2005
# Posted on: 15-Jun-2015 07:30:08   

The data type should be know by ODP.Net (if that is the one you are using.

  • In your SP, are you using a custom type?
  • Are you using ODP.Net or MSOracle? And What version?
  • If possible, please show us real code so we can reproduce it over here.
David Elizondo | LLBLGen Support Team
RDH
User
Posts: 5
Joined: 12-Jun-2015
# Posted on: 15-Jun-2015 16:47:08   

We are using ODP.Net, 12.1.0.2.4. The datatype is OracleDbType.Raw, which gets stored into a BLOB column. The data from the datatable is a byte array.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39863
Joined: 17-Aug-2003
# Posted on: 16-Jun-2015 09:49:38   

Please post the full stack trace when you disable tracing (if you have it enabled). The 'ToString' exception isn't the real exception, I need the inner exception stacktrace, hence my question for the full stacktrace.

Frans Bouma | Lead developer LLBLGen Pro
RDH
User
Posts: 5
Joined: 12-Jun-2015
# Posted on: 16-Jun-2015 17:13:51   

This is the entire stack trace that I have available to me. It doesn't provide much more information.

System.InvalidCastException: Specified cast is not valid.
   at SD.LLBLGen.Pro.ORMSupportClasses.Query.ToString(Boolean verbose)
   at SD.LLBLGen.Pro.ORMSupportClasses.ActionQuery.<ExecuteAsync>d__0.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterCore.<ExecuteActionQueryAsync>d__7a.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Slb.Studio.DataAccess.Techlog.Providers.Llbl.DbSpecificAdapter.OracleDbSpecificAdapter.<BatchSaveEntityCollectionAsync>d__c.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at Slb.Studio.DataAccess.Techlog.Providers.Llbl.DataAccess.<CheckoutEntitiesAsync>d__81.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Slb.Studio.Server.Techlog.Logic.EntityLogic.<CheckoutEntities>d__71.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
  at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Slb.Studio.Server.Techlog.Services.Transfer.EntityController.<Post>d__27.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Slb.Studio.Server.HttpHandlers.StudioServerContractHttpHandler.<ProcessInternal>d__8.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at Slb.Studio.Server.HttpHandlers.StudioServerContractHttpHandler.<Process>d__0.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Slb.Studio.Server.HttpHandlers.BaseHttpHandler.<ProcessRequestAsync>d__10.MoveNext()

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39863
Joined: 17-Aug-2003
# Posted on: 16-Jun-2015 18:52:09   

I think the exception is caused in the exception handler, as another exception occurs, but the handler wants to wrap it inside an ORMQueryExecutionException and to do that it will embed the query as text with the parameter values. This is done in the .ToString() method overload and it there crashes.

As there are no line numbers given (please copy the .pdb file from the runtime libraries folder into your bin folder to get the line number!) I have to guess.

The parameter you add, what DbType does that parameter have?

Also, as you're using ODP.NET, you don't need to use async, as ODP.NET doesn't have async support for .NET in their ADO.NET provider (i.o.w.: the code will execute synchronously internally). The managed provider doesn't change that.

Frans Bouma | Lead developer LLBLGen Pro
RDH
User
Posts: 5
Joined: 12-Jun-2015
# Posted on: 16-Jun-2015 18:56:07   

The datatype is OracleDbType.Raw, which gets stored into a BLOB column. The data from the datatable is a byte array.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39863
Joined: 17-Aug-2003
# Posted on: 16-Jun-2015 22:08:33   

RDH wrote:

The datatype is OracleDbType.Raw, which gets stored into a BLOB column. The data from the datatable is a byte array.

Please reread my post: I asked you to use the .pdb file in the runtimes folder (it's stored with the runtime library) so we can see what the line number is.

Secondly, right before you add the parameter to the command, you can read the DbType property, please check what the type is returned by that property, as the tostring method uses that value to produce different values and I think the cast is failing there somewhere. All this to find where things go wrong, as I don't know where things crash.

Frans Bouma | Lead developer LLBLGen Pro
Ronald avatar
Ronald
User
Posts: 10
Joined: 17-Jun-2015
# Posted on: 18-Jun-2015 11:00:39   

Hi, I’m the developer who originally investigated the issue and came up with this bug. What we were trying to accomplish is a bulk insert of a number of rows directly in the table.

We created an OracleDbCommand that is simply an insert command into a table

OracleCommand oracleCommand = new OracleCommand();
oracleCommand.CommandText = string.Format("insert into {1} ({2}) values ({3})", tableName, columns, parameters)

But the parameters are now an array of values, each index correspond to the values of a single row.

OracleParameter parameter = new OracleParameter(parameterName, GetDbType(column.DataType));
parameter.Value = dataTable.AsEnumerable().Select(dr => dr.Field<DateTime?>(column)).ToArray();
oracleCommand.Parameters.Add(parameter);

Basically, we are passing around a DataTable and for each parameter, we take all values of the corresponding DataTable column and add it as a parameter value. This is how we end up with a DbCommand where the parameter values are basically arrays of values, corresponding to the DbType.

Otis is on the right track. So what happens is that the execution of the ActionQuery fails (in my local test I got an ORA exception because of not enough disk space?).

That one is catched in which you are trying to create an ORMQueryExecutionException.

catch (Exception ex)
{
   throw new ORMQueryExecutionException(string.Format("An exception was caught during the execution of an action query: {0}. Check InnerException, QueryExecuted and Parameters of this exception to examine the cause of this exception.", (object)ex.Message), base.ToString(), this.Parameters, ex, this.GetExceptionInfo(ex as DbException));
}

In here, you call base.ToString(), which will stringify the DbCommand (I guess for debug/info purposes).

The problem is linked to the DbType.DateTime case, yes only DateTimes are an issue here.

        case DbType.DateTime:
                  str = string.Format("{0}", (object) ((DateTime) dbDataParameter.Value).ToString("O"));

Because our dbDataParameter.Value contains an array of DateTime objects, the cast to a single DateTime is failing cry

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39863
Joined: 17-Aug-2003
# Posted on: 19-Jun-2015 10:02:33   

Thanks Ronald, I can work with that simple_smile I had the suspicion it was that line (as there are no casts in the method otherwise). I'll look into adding more check code so it won't fail with a cast error and the real exception is properly thrown.

About the real exception ... that's something related to oracle I guess. Not enough diskspace is really odd though. But it's ODP.NET, so 'odd' sometimes gets new meanings wink

I'll see if I can get it added today so you can try out a new build for me.

Frans Bouma | Lead developer LLBLGen Pro
Ronald avatar
Ronald
User
Posts: 10
Joined: 17-Jun-2015
# Posted on: 19-Jun-2015 10:54:37   

The underlaying exception can be really anything DB related. The problem we had was that the InvalidCast exception hid the real exception so we were not able to understand what was going on. Right know we worked around it by not using the ActionQuery but directly calling the ODP.NET classes.

Anyway, let me know when and where I can get a new build. I'll be happy to try it out for you on our scenario. But it is not something I can do in a couple of minutes, so give me at least the week-end wink

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39863
Joined: 17-Aug-2003
# Posted on: 19-Jun-2015 11:30:19   

The string is indeed used for verbose error reporting. I've added a guard clause around the cast, so it should be fine now.

See the attached zip, it contains both .net 4.5 and normal builds.

Btw, using async with odp.net isn't giving any async behavior as internally inside the ado.net classes they're falling back onto the synchronous code (as they haven't implemented any async code inside the ado.net classes themselves), so it will give extra overhead for no gain. Just a FYI simple_smile

Take your time, report back when you had time to test it simple_smile Cheers.

Attachments
Filename File size Added on Approval
ORMSupportClasses_42_06192015.zip 946,882 19-Jun-2015 11:30.26 Approved
Frans Bouma | Lead developer LLBLGen Pro
Ronald avatar
Ronald
User
Posts: 10
Joined: 17-Jun-2015
# Posted on: 19-Jun-2015 11:51:25   

Great, thanks. I'll try this out during the weekend since I have to set-up the environment first.

I'll get back to you with the response.

As for the async, you're right. But we're providing a higher level data interface with only async methods and that connects to either Oracle or SQL Server based on the configuration.

Right now, improvements are on the way by another team simple_smile

Ronald avatar
Ronald
User
Posts: 10
Joined: 17-Jun-2015
# Posted on: 19-Jun-2015 17:15:21   

By the way, we're investigating another issue that seems to be linked to the same execution of a similar ActionQuery.

This time, our ActionQuery will insert about 30 000 rows in the table (= dbParameter.Value is array[30000]).

We experience an AccessViolationException when executing the

ExecuteActionQueryAsync

of the

DataAccessAdapterBase

class. The inner exception looks like Attempted to read or write protected memory. This is often an indication that other memory is corrupt.

I'm not sure how deep this is thrown and I have not yet run your dlls with associated pdb files either. My guess, we should batch the number of rows/values we're trying to insert to a more reasonable size. But since this thread is still ongoing I thought I'd ask your advice on this.

Note, this is still with the old commercial dlls. Not with your provided build.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39863
Joined: 17-Aug-2003
# Posted on: 19-Jun-2015 18:25:40   

Ronald wrote:

By the way, we're investigating another issue that seems to be linked to the same execution of a similar ActionQuery.

This time, our ActionQuery will insert about 30 000 rows in the table (= dbParameter.Value is array[30000]).

We experience an AccessViolationException when executing the

ExecuteActionQueryAsync

of the

DataAccessAdapterBase

class. The inner exception looks like Attempted to read or write protected memory. This is often an indication that other memory is corrupt.

I'm not sure how deep this is thrown and I have not yet run your dlls with associated pdb files either. My guess, we should batch the number of rows/values we're trying to insert to a more reasonable size. But since this thread is still ongoing I thought I'd ask your advice on this. Note, this is still with the old commercial dlls. Not with your provided build.

It's likely caused by some mess inside the CLI wrapping of the ODP.NET provider, if you use the normal one (the normal ODP.NET provider is written in .NET but wraps the java CLI for oracle, and I've seen this error before with respect to that combination). Do you use the managed ODP.NET provider? It comes with the 12c version. See: http://www.llblgen.com/documentation/4.2/LLBLGen%20Pro%20RTF/hh_goto.htm#Using%20the%20generated%20code/gencode_dbspecificfeatures.htm#oraclemanagedprovider

The new build shouldn't make a difference in this case, as we don't allocate any memory outside the CLR so all memory we allocate is done through normal .NET object allocations.

Frans Bouma | Lead developer LLBLGen Pro
Ronald avatar
Ronald
User
Posts: 10
Joined: 17-Jun-2015
# Posted on: 19-Jun-2015 18:37:37   

No, I see we are using the unmanaged one: Oracle.DataAccess.dll rage I'll check that out, thanks for the info.

daelmo avatar
daelmo
Support Team
Posts: 8245
Joined: 28-Nov-2005
# Posted on: 20-Jun-2015 08:53:31   

Ronald wrote:

No, I see we are using the unmanaged one: Oracle.DataAccess.dll rage I'll check that out, thanks for the info.

Looking forward for your feedback on those two tests.

David Elizondo | LLBLGen Support Team
Ronald avatar
Ronald
User
Posts: 10
Joined: 17-Jun-2015
# Posted on: 20-Jun-2015 13:26:22   

Some updates. As for the initial InvalidCastException, I retested using the provided dll and that one seems to be correct. smile

Where before I received:

InvalidCastException: Specified cast is not valid.

I now receive, with the same scenario,:

An exception was caught during the execution of an action query: ORA-01654: unable to extend index DATABASE.INDEXNAME by 128 in tablespace TABLESPACENAME. Check InnerException, QueryExecuted and Parameters of this exception to examine the cause of this exception.

This is correct since it displays now the underlying issue (sending large data to a database with not enough disk space).

Thanks for this. Any information regarding releases that include this fix?

As for the second (memory) issue, an initial test yesterday with the managed provider still returned the same issue. Although, I'm not sure if it actually used the managed one or not. I cannot reproduce the scenario from home so I'll continue on this on Monday.

daelmo avatar
daelmo
Support Team
Posts: 8245
Joined: 28-Nov-2005
# Posted on: 22-Jun-2015 05:59:06   

Ronald wrote:

Some updates. As for the initial InvalidCastException, I retested using the provided dll and that one seems to be correct. smile

Where before I received:

InvalidCastException: Specified cast is not valid.

I now receive, with the same scenario,:

An exception was caught during the execution of an action query: ORA-01654: unable to extend index DATABASE.INDEXNAME by 128 in tablespace TABLESPACENAME. Check InnerException, QueryExecuted and Parameters of this exception to examine the cause of this exception.

This is correct since it displays now the underlying issue (sending large data to a database with not enough disk space).

Good to know it worked smile

Ronald wrote:

Thanks for this. Any information regarding releases that include this fix?

The next minor release update will contain recent fixes, like this one, usually in couple of days.

Ronald wrote:

As for the second (memory) issue, an initial test yesterday with the managed provider still returned the same issue. Although, I'm not sure if it actually used the managed one or not. I cannot reproduce the scenario from home so I'll continue on this on Monday.

Ok. We will look forward for your feedback. wink

David Elizondo | LLBLGen Support Team
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39863
Joined: 17-Aug-2003
# Posted on: 22-Jun-2015 09:07:24   

About the build: we use an automated build system, and I attached a release build of the runtimes, so you can keep using the dll as-is, the next build released on the website contains this dll.

Frans Bouma | Lead developer LLBLGen Pro
Ronald avatar
Ronald
User
Posts: 10
Joined: 17-Jun-2015
# Posted on: 25-Jun-2015 15:35:40   

Hey just a quick update for you guys on the memory issue.

We tested the problematic workflow on code that's using the Managed ODP.NET provider and it did not occur anymore. Now we can start the process to upgrade our libraries simple_smile

Thank you all for your help, insight and quick response. I learned a lot from this experience wink